How can I add relative paths to libraries in perl? - perl

I want to have a library (my_library_1) which makes use of another library in a folder relative to itself. If I write it like this:
use lib "/./libraries/";
use my_library_2;
It will use the path from where I execute the script.
If I use the following as proposed in other similar quiestions:
use FindBin;
use lib "$FindBin::Bin/./libraries/";
use my_library_2;
It will be relative to the main script being executed, therefore if I'm calling this library from another script, and then this library calles the other one (my_library_1), the library declaration will not be as expected if the first library (my_library_1) and the main script are in the same folder.
How can I solve this issue without relying on absolute paths?
Edit: To add some more information
This is the current structure:
folder
\_folder_1
\__main_script
\_folder_2
\__my_library_1
\__folder_1
\___my_library_2
I want to reference library_3 from library_2 with a relative path. The two proposed options do not work when I use them on "my_library_2".

lib::relative is a straightforward way to use __FILE__ to add absolutized lib paths relative to either a script or module. It also documents the equivalent core module commands so it doesn't need to be installed.
In the script:
use lib::relative '../folder2';
or:
use Cwd ();
use File::Basename ();
use File::Spec ();
use lib File::Spec->catdir(File::Basename::dirname(Cwd::abs_path __FILE__), '../folder2');
Similarly in the module:
use lib::relative 'folder1';
I recommend the much simpler __FILE__ approach over FindBin in all cases - FindBin is action at a distance, requires workarounds, and has serious bugs on old Perls that cannot be fixed because it isn't available on CPAN.

Here is one approach using __FILE__ to get the directory name of my_library_1.pm:
folder1/main.pl:
use strict;
use warnings;
use FindBin;
use lib "$FindBin::RealBin/../folder2/";
use my_library_1;
folder2/my_library_1.pm:
package my_library_1;
use strict;
use warnings;
use File::Basename qw(dirname);
my $dir;
BEGIN {
$dir = dirname(__FILE__);
}
use lib "$dir/folder1";
use my_library_2;

Related

'use lib' can't find Perl module

I'm on Windows 7, I downloaded a CPAN module called XML::XPath, and I want to use it in a script I wrote.
I cannot modify #INC on my machine, I cannot modify environmental variables on my machine, I cannot run make on my machine, and I cannot use a package manager on my machine.
So in order to use the module, I went to the CPAN website, downloaded the module's .tar.gz file, and unzipped it into a lib folder in my project. I did that because this guide suggested that I can use use lib in the context of my script to reference the downloaded module:
Adding a use lib statement to the script will add the directory to #INC for that specific script. Regardless who and in what environment runs it.
My script is called test.pl. I am trying to use the XML::XPath module to parse an XML file called test.xml. Here is an example of my directory structure:
C:/
→ sandbox/
test.pl
test.xml
→ lib/
→ XML-XPath-1.13/
XPath.pm
→ XPath/
XMLParser.pm
(etc.)
This is my test.pl script:
#!/usr/bin/perl
use strict;
use warnings;
use lib qw("C:/sandbox/lib/XML-XPath-1.13");
my $xml_file = "C:/sandbox/test.xml";
my $result;
if (-f $xml_file) {
$result = XML::XPath->new(filename => "$xml_file");
}
When I run this script with perl test.pl, the script fails with the following error:
Can't locate object method "new" via package "XML::XPath" (perhaps you forgot to load "XML::XPath"?) at test.pl line 10.
Can I resolve this error with use lib?
EDIT:
When I add use XML::XPath; to my test.pl script, as such:
#!/usr/bin/perl
use strict;
use warnings;
use lib qw("C:/sandbox/lib/XML-XPath-1.13");
I get an error like:
Can't locate XML/XPath.pm in #INC
But as stated above, I cannot physically modify #INC or recompile Perl on my machine.
This is not going to work. XML::XPath depends on the XML::Parser module which needs to be compiled. Since you do not have a complete toolchain with a C compiler available, there is no way you are getting this to run. You will have to search for a different way to handle your XML.
Just extracting a tarball only works when you have a pure-Perl module.
The easiest way to get a full Perl toolchain on Windows is to install Linux install the Strawberry Perl distribution. There's even a ZIP edition and a portable edition that can be installed on a thumb drive, no admin privileges required.
Your more immediate problem is that XML::XPath in version 1.13 seems to be a shoddy module with an unusual directory layout. The package you are trying to load is at XML-XPath-1.13/XPath.pm but should be at XML-XPath-1.13/lib/XML/XPath.pm. Why?
When you say use XML::XPath; this does two things. First, Perl searches for a file XML/XPath.pm in all #INC directories. Assuming you have something like
use FindBin;
use lib "$FindBin::Bin/lib/XML-XPath-1.13";
then perl would expect a file PROJECT/lib/XML-XPath-1.13/XML/XPath.pm which doesn't exist (where I'm using PROJECT as placeholder for the directory of your script).
Since the file PROJECT/lib/XML-XPath-1.13/XPath.pm does exist you could say use XPath;.
But then the second thing happens: The package is imported so that it can load subroutines or constants into your namespace. The use'd package name is now used as a class: XPath->import. However, the XPath.pm file does not contain the XPath package but the XML::XPath package, so there is no XPath class with an import method.
The import method call can be suppressed by providing an empty list in the use statement: use XPath ();.
Better than such hacks, you could use a more recent version of XML::XPath (version 1.13 is from 2003, but the newer version 1.42 is from 2007). Once you extract it you could use lib "$FindBin::Bin/lib/XML-XPath-1.42/lib" and then use XML::XPath;.
But this still won't work, because XML::XPath does use XML::Parser and you don't have that module installed. Now we are back to your main problem: XML::Parser needs to be compiled first because it contains a C extension, and also relies on an external library. Without a full toolchain you can't compile it.
First of all, you should properly install the module into lib rather than unzipping the distribution into lib.
Then, you'll need
use FindBin qw( $RealBin );
use lib "$RealBin/lib";
use XML::XPath qw( );

Perl module dependency organization and include order

I am aware of cyclic module dependency in perl and the fact that it is very bad idea e.g.:
package ModuleA;
use ModuleB;
package ModuleB;
use ModuleA;
I want to ask if following model is safe and if it follows some best practicing rules:
package main;
use ModuleA;
use ModuleB;
package ModuleA;
use ModuleB;
use ModuleC;
package ModuleB;
use ModuleC;
Also I would like to ask if the order of use-ing modules have any impact? e.g. if
package main;
use ModuleA;
use ModuleB;
is the same as
package main;
use ModuleB;
use ModuleA;
and if
package ModuleA;
use ModuleB;
use ModuleC;
is the same as
package ModuleA;
use ModuleC;
use ModuleB;
etc.
EDIT:
Note to say that ModuleA loads ModuleC explicitly (and do not rely on ModuleB that it will load ModuleC) because ModuleA uses functions from ModuleC. Is this good design approach?
The best practice is easy: Each file, program or module, should specify all its dependencies. That's it. E.g., if a script needs modules A and B, and module A needs module B, don't count on module B already loaded by the script - what if some other script needs module A without needing B?
Good Exporter based modules should use #EXPORT_OK and you should explicitly list the imported subroutines in the use-clause. It helps to prevent name clashes.
For normal modules that only export subroutines, order shouldn't matter. In other cases, it might, though: consider
use warnings_;
use diagnostics;
versus
use diagnostics;
use warnings_;

Perl `use` - import is not called twice?

Doubt in perl basics use
it is somewhat similar to my other question Perl: Two packages in same file...
consider a perl script:
Script.pl
use INCLUDES;
INCLUDES.pm
package INCLUDES;
use Exporter;
############# MY DOUBT STARTS HERE ###############
use Module1;
use Module2;
##################################################
our #ISA = qw(Exporter);
our #EXPORT = qw();
sub import {
print 'INCLUDES imported to ' . caller . "\n";
}
Module1.pm
package Module1;
use strict;
use Exporter;
use INCLUDES; #####=> INCLUDES.pm 'use'd
our #ISA = qw(Exporter);
our #EXPORT = qw();
1;
Module2.pm
package Module2;
use strict;
use Exporter;
use INCLUDES; #####=> INCLUDES.pm 'use'd
our #ISA = qw(Exporter);
our #EXPORT = ();
1;
OUTPUT
D:\Do_analysis>Script.pl
INCLUDES imported to main
According to perl docs, use INCLUDES; in Module1 & Module2 => BEGIN {require 'INCLUDES.pm'; 'INCLUDES'->import();} . So, the import() should be called in Module1.pm , Module2.pm also.
I would expect the output as something like below,
EXPECTED OUTPUT ??
D:\Do_analysis>Script.pl
INCLUDES imported to main
INCLUDES imported to Module1
INCLUDES imported to Module2
But why is the execution not as expected?
UPDATED
This is what I am trying to achieve, by having the INCLUDES.pm file.
Note that: PACKAGE2 might want to access PACKAGE3, PACKAGE4 etc. Instead of useing all the modules inside PACKAGE2 seperately, I would like to create a Library INCLUDES and use it in all other modules.
Is this approach valid? or recommendable?
I appreciate any idea about how to achieve this.
Thanks!
If you were to move
use Module1;
use Module2;
below sub import { ... }, you'd get the expected behaviour.
The problem is that you execute Module1.pm before the compiler even reaches sub import in INCLUDES.pm.
When Module1.pm does use INCLUDES;, Perl says "oh, it's already loaded" (in response to require) and "oh, it doesn't have an import" (in response to import), since the part of INCLUDES.pm that would have created import hasn't been compiled yet. Same for Module2.pm.
Generally speaking, if you have a modules that include each other (directly or otherwise), you're doing something wrong (in terms of design), and you'll end up having to deal with such issues. You might find Mini-Tutorial: Mutual Use of Exporting Modules useful in dealing with these issues.
You have circular dependencies between your modules. main uses INCLUDES, INCLUDES uses Module1, and Module1 uses INCLUDES (Module2 is basically irrelevant here, except to act the same as Module1). If perl did the naïve thing, this would cause an infinite loop (or at least a stack overflow), as every time perl tried to compile INCLUDES it would lead to a compilation of Module1, and every time perl tried to compile Module1 it would lead to a compilation of INCLUDES.
Since we don't want that to happen, perl has to do something different. What it does instead is to ignore any request to compile a module that's already compiled or that it's already in the process of compiling. So the flow of events is:
Perl begins compiling script.pl and enters the package main.
Perl sees use INCLUDES;, loads INCLUDES.pm, and begins compiling it.
At the line package INCLUDES;, perl enters the package INCLUDES.
Perl sees use Module1;, loads Module1.pm, and begins compiling it.
At the line package Module1, perl enters the package Module1.
Perl sees the use INCLUDES;, realizes that it's already in the process of compiling INCLUDES.pm, and doesn't attempt to compile it again.
As a result of use INCLUDES; perl tries to call INCLUDES->import, but the import method hasn't been defined yet (we suspended compilation of INCLUDES at step 4 to start compiling Module1).
Compilation of Module1 completes and we go back to INCLUDES.
Perl calls Module1->import from INCLUDES.
Compilation of INCLUDES completes, including the use Module2 and the definition of sub import. Perl goes back to main.
INCLUDES->import is called from main and your message is printed.
Compilation of script.pl completes.
script.pl has nothing to do at runtime, and perl exits.

lattice-tool path issue

I have downloaded and installed a perl tool (lattice-tool).
But it is in my local directory.
While I'm running it says can't locate Directed.pm(a lib file) which is available in lib folder of my local directory.
I hope it will be set right if I set path variable. If so, how do I set it?
For use lib you have to use full path, and you are should not use relativ path like this.
use '../lib';#not working in all times.
Scenario: Your scripts in something/bin/prog.pl, your lib is something/lib/lib.pm.
If you use relativ path, you should call your program like this:
cd something/bin/ && ./prog.pl
If you would like to use relativ path, use FindBin to find your current path:
use FindBin;
use lib "$FindBin::Bin/../lib";#your lib realitv to your script
use lib $FindBin::Bin;#your current script full path
Then you could call your program from anywhere it will always find its lib realtiv to itself.
cd ~
something/bin/prog.pl# ti will use the correct lib
In my scripts, I have the following (which I'm sure can be improved, but it has worked thus far):
my $mydir; BEGIN { ($mydir) = ($0 =~ m#(.*)[/\\]#) or $mydir = '.'; }
use lib "$mydir/lib";
So the script tries to determine its own directory and then tells Perl to look for libraries within the lib subdirectory of that directory.
You need to add 'lib' to the directories perl searches for modules. You can do this with the -I flag:
perl -Ilib lattice-tool.pl
Use lib:
use lib 'lib';
lib also checks for architecture specific sub directories under lib to make sure machine-dependent libraries are loaded.
EDIT: Note that directories passed to lib are relative to your current working directory, so that if you want to execute your script from another location you should use use lib '/home/user1126070/lib'.
From perlvar:
The array #INC contains the list of places that the do EXPR , require, or use
constructs look for their library files. It initially consists of the arguments
to any -I command-line switches, followed by the default Perl library, probably
/usr/local/lib/perl, followed by ".", to represent the current directory. ("."
will not be appended if taint checks are enabled, either by -T or by -t .) If you
need to modify this at runtime, you should use the use lib pragma to get the
machine-dependent library properly loaded [...]

How can I use vendor-packaged modules from a Perl I compiled myself?

My OS-Distribution provides the rpm-package "perl-obexftp", which installs the Modul "OBEXFTP".
These are the files:
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/OBEXFTP.pm
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP/.packlist
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP/OBEXFTP.bs
/usr/lib/perl5/vendor_perl/5.10.0/x86_64-linux-thread-multi/auto/OBEXFTP/OBEXFTP.so
/var/adm/perl-modules/obexftp
I am using now a Perl which I have build from the source.
Is there a simple way to make this OBEXFTP-module accesseble to my Perlinstallation?
Choose one of
Add the following pragma to your code:
use lib '/usr/lib/perl5/vendor_perl/5.10.0';
Add that path to the PERL5LIB environment variable
Invoke your code with perl -I/usr/lib/perl5/vendor_perl/5.10.0 program
Rebuild perl so that path is in its baked-in #INC
Build the module yourself using your custom-built perl
For details, see perlrun.