Include perl module from a different location - perl

my problem is as follows
I have the lib in location ./abc/def/lib
I am using it in path ./abc/xyz/lib/tuv/abc.pm
I am running the code from a different location altogether
which is something like ./abc/xyz/blah1/blah2/../../ . I'm running the code here.
I have created env params
setenv def ./abc/def
setenv xyz ./abc/xyz
I tried the below solutions
Begin { push(#INC, $ENV{def}."/lib" ) }
Along with the providing the path for other libraries i used
use lib $ENV{xyz}."/lib"
This solution didn't work. I get the error saying the library in ./abc/def/lib
is not included.
I tried the FindBin
# for the libs present in ./abc/def/lib
use FindBin;
use lib "$FindBin::Bin/../../";
use mylib::abc;
use mylib::def;
(I tried giving the path also of root from where to start looking..in FindBin)
I tried using different combinations but not sure why it doesn't work.
Can someone point out what i am doing wrong or is my understanding of FindBin wrong.

You don't say what problems you're having. Just saying you're "not sure why it doesn't work" isn't really helpful. I assume you're getting a "Can't locate [some module] in #INC" error.
There are three (recommended) ways to change the value of #INC. Each have their uses.
Inside a program, you can use use lib
From the command line (or in a shebang), you can use -I
In your shell environment, you can set PERL5LIB
In all these cases, the new directories will be added to the start of #INC, so these directories will be searched before the standard library directories.
You can also manually manipulate #INC. Note that in order for manual manipulation of #INC to have any effect on use statements in your code, they will need to be executed in a BEGIN block (as use statements are executed at compile time). It looks like you have tried that too, but that you used Begin rather than BEGIN - which wouldn't work.
If you are running your program from a location that is completely unrelated to the directories where the libraries are kept, then FindBin is likely to be of limited use. FindBin is useful where the location of the executable file and the libraries are closely related (for example the executable file is in ./bin and the libraries is in ./lib).
It also seems that you are trying to adjust #INC from within a library (./abc/xyz/lib/tuv/abc.pm). Note that FindBin will not set $FindBin::Bin to this directory. $FindBin::Bin is always set to the directory that contains the executable file which is running.
Without knowing more about the actual paths that you are using (you have only given relative paths in your examples) it's hard to know for sure what the best approach is. But I think that in your situation I'd be setting PERL5LIB to the absolute path of your extra libraries.

The FindBin module is the best fit for your needs. The important thing to remember is that $FindBin::Bin refers to the directory that contains the script. So when you refer to it ...
use lib $FindBin::Bin . '/../../etc';
the '/../..' path must describe the path from the script directory to the lib directory.
The other important thing to take into consideration is the error message. It will start with Can't locate Your/Module/Name.pm but will then go on to list all the directories in #INC - the library search path. Check this list of directories to make sure it includes the lib path you intended to add.

Related

perl - package name: Can I use path from root?

I am reading documentation about use. It is the same as
BEGIN{ require Namespace::NameOfModule; }
And the perl interpreter loads the module and translate the double colon :: to system path's separator (UNIX / and WINDOWS \\, if I remember correctly). I just wondered, If I am able to load module from root. Because in that case, for example module DateTime.pm in dir /home/nickname/dir. It would be (according to double colons rule) ::home::nickname::dir, which is wrong (like it should even look like terrible package path). So how - if even - is possible to load a module from root dir? And Does the path starts from current dir by default? (That is from dir where is the perl script located in), or only from #INC dirs?
The module namespace is relative to #INC directories only. These are by default set up according to where Perl was installed, with privlib (core modules installed with Perl), sitelib (modules installed by a CPAN client), and vendorlib (modules installed by a vendor package manager), and architecture-specific versions of each of these. Additional #INC directories may be added by local::lib, and before Perl 5.26 #INC also included . (the current working directory), but this was a bad idea.
When you invoke use or require on a bareword package name, it does the translation you described (convert :: to path separator and append .pm), and then appends it to each directory in #INC until it finds a file. (It also checks for .pmc but this is rarely relevant.) The package statement inside the file is expected to match the module path used to look it up for the import component of use.
You can modify #INC manually, but it is best to do it with one of the following mechanisms, so that architecture-specific and version-specific subdirectories will be respected if present.
lib - use lib '/path/to/lib';
-I - perl -I/path/to/lib ...
PERL5LIB - env PERL5LIB=/path/to/lib perl ...
These options should only be used to insert absolute paths into #INC, since relative paths will have the same vulnerability as . used to, in that they may mean something different when the current directory is changed. To add a path relative to the script wherever it may be, lib::relative can be installed to simplify the process, or its documentation describes equivalent functionality using core modules.
use lib::relative '../lib';
Once you have added your custom location(s) to #INC, that directory will be searched first for any further use calls.
use Foo::Bar; # now checks /path/to/lib/Foo/Bar.pm first
# which should contain package Foo::Bar
Even if you could load the module like that, you'd have numerous other problems.
The module's package directive would have to be package ::home::nickname::dir::SomeModule; for import to be found.
You'd have to use ::home::nickname::dir::SomeModule->some_method to call a static method.
You'd have to use ::home::nickname::dir::SomeModule::some_sub to call a sub.
That's obviously the wrong approach.
For modules installed in a position relative to a script, use the following in the script to tell perl where to look:
use FindBin qw( $RealBin );
use lib "$RealBin/../lib"; # Or whatever.
For modules installed for any script to use, use the following environment variable:
export PERL5LIB="$HOME/dir" # Or whatever.

How to dynamically add a program's library path to #INC in Perl program?

I'm working on an application that is written in Perl. The application is so large now that I want to move each class out into a separate file. This complicated building and installing the program; when it was only file it could easily be copied in place anywhere. Now I have to use a build system like Build::Module or ExtUtils::MakeMaker.
I have structured my source tree so that I have two directories: bin where the program launcher is, and lib where a number of modules are. Bin contains an executable Perl script which the user invokes, and it loads the necessary module from the lib directory.
The problem that I have is that I want the user to be able to specify a prefix where they want the program installed, similar to the --prefix option offered by packages based on GNU Autotools. Since this might not be a standard path where Perl looks for modules (for example /opt/program) the user will see a message saying something like Can't locate Program.pm in #INC.
Is there a way to make the program detect where the modules should be loaded frm and dynamically add that path to #INC? I don't want the user to have to manually work with environment variables like PERL5LIB in order to get the program running.
Couldn't you use findbin and lib ?:
use FindBin qw($Bin);
use lib "$Bin/lib";
If I understand you, you're breaking up a big large program into smaller components. Good for you! That's great programming technique. Making each class a true Perl module is a great idea. It makes your program so much easier to maintain.
I do this all of the time. First, I use the module name Local:: as my prefix. CPAN will never use Local as a module prefix, so I know I will never clash with some CPAN Module. Then, I put my Local module directory in the same directory as my script. In most standard Perl installations, when Perl searches #INC for modules, the last directory it searches for is the current directory (.). Since my module names will never clash with any CPAN modules, I know Perl will find my modules and only my modules under that ./Local directory.
You can now distribute the entire directory structure to other users. All a user has to do is install your entire directory (which includes the scripts and modules) and run the script. No need to go through an entire install process.
The following code works for me. You supply a command line option like (--prefix abc) and that value is appended to the lib path very early in your script. So all following modules will be searched with the dynamically set path.
use strict;
use warnings;
use Getopt::Long;
my $prefix = '';
BEGIN {
GetOptions ('prefix=s' => \$prefix );
$prefix = $prefix || '.';
}
use lib "$prefix";
use mymod; # uses the dynamical search path

How do I best use a library whose directory may change occasionally?

I am writing Perl scripts and when I have too many functions, I usually move them all into a library (also good for code reuse). So I usually create a package (e.g. my_lib.pm) and add use lib 'path/to/lib'; use my_lib; to my script.
I wonder if it's possible to skip the use lib 'path/to/lib';, which sometimes gives me trouble since I reorganize my directory hierarchy, and make Perl look for packages in the same dir where the script is running from.
Thank you.
First, i suggest you - "Never mess up with Core Perl and its libraries - never put your lib in among there".
If you want your script look into current dir, then use like:
require "mylibrary/functions.pm";
where mylibrary is a dir that exists the same path as your caller script.
I would put my .pm file into one directory so you can use if from Perl scrips irrespective of their location.
Then create an envrironment variable PERL5LIB with the name of that directory.
You need
use lib '.';

How can my Perl script find its module in the same directory?

I recently wrote a new Perl script to kill processes based on either process name / user name and extended it using Classes so that I could reuse the process code in other programs. My current layout is -
/home/mutew/src/prod/pskill <-- Perl script
/home/mutew/src/prod/Process.pm <-- Package to handle process descriptions
I added ~/src/prod in my $PATH variable to access the script from anywhere. On running the script from any directory other than its resident directory leads to a "Can't locate Process.pm in #INC" (which is understandable given that other than the shared directories in /usr, #INC includes only the current directory - '.'). One workaround that I have been using is the use lib directive as so -
use lib '/home/mutew/src/prod';
but this is a major portability issue. Any solutions which will also allow me to export the script to other systems without and changes?
EDIT
I chose 'depesz' answer as the correct one because of its simplicity and core module usage.
brian d foy's answer though suggests other methods to accomplish the same (TMTOWTDI), his contribution in perlfaq8 renders this question absolutely redundant.
I am curious why the simplistic solution
use File::Basename;
use lib dirname (__FILE__);
use SomeModuleLocatedInTheSameDirectoryAsThisPerlScriptOrModule;
did not come up. The FindBin module seems to have some issues if the file is not the main executable perl script, but simply a non-executable Perl module. At least that's how I interpret the comment in the documentation. Did not really test it.
To have any other path relative to the location of this Perl file, do something like
use File::Basename;
use lib dirname (__FILE__) . "/MyModules";
The simplest approach I found it to use FindBin module. Like this:
use FindBin;
use lib $FindBin::Bin;
Generally I prefer to have my scripts provided in such a way that programs are in whatever/bin, and libraries are in whatever/lib
In these situations I use a slightly more complicated approach:
use Cwd qw(abs_path);
use FindBin;
use lib abs_path("$FindBin::Bin/../lib");
The abs_path call is to make the #INC contain whatever/lib, and not whatever/bin/../lib - it's just a slight change, but makes reading error messages easier.
From perlfaq8, which answers "How do I add a directory to my include path (#INC) at runtime?". There are several other answers for questions around this issue too.
How do I add a directory to my include path (#INC) at runtime?
Here are the suggested ways of modifying your include path, including environment variables, run-time switches, and in-code statements:
the PERLLIB environment variable
$ export PERLLIB=/path/to/my/dir
$ perl program.pl
the PERL5LIB environment variable
$ export PERL5LIB=/path/to/my/dir
$ perl program.pl
the perl -Idir command line flag
$ perl -I/path/to/my/dir program.pl
the use lib pragma:
use lib "$ENV{HOME}/myown_perllib";
The last is particularly useful because it knows about machine dependent architectures. The lib.pm pragmatic module was first included with the 5.002 release of Perl.
Besides the already stated solutions:
use FindBin / lib
Perl Faq 8 How do I add a directory to my include path (#INC) at runtime?
'The simplest approach' (™) that I use while dev/testing a module prior
to deploying it (in /usr/local/lib/site_perl/ or elsewhere in #INC) is
to modify #INC before loading the module as follows:
#!/usr/bin/perl
use strict;
use warnings;
# Modify #INC prior to module loading.
BEGIN { unshift #INC, '.'; }
use YourModuleInCWD;
(Add current working directory to #INC? - PerlMonks)
You can make perl look in any directory by using the -I flag. Here, -I stands for #INC which is the array of paths in which perl looks for modules. Using -I adds the given path to the #INC array for this execution.
eg:
perl -I lib bin/script.pl
where lib contains the modules I want to use.
I know that this works for perl 5. I am not at sure about other versions.
Take a look at Par::Packer. It creates an executable with all of the script's dependencies included. It makes distribution easy. You also could also provide your users a version of your module that can be installed on their systems. See Module::Starter for an easy way to build all of the files required to make a standard CPAN-style distribution.
The path can be included in perl script in first line, marked with #!
#!/usr/bin/perl -I.
use anymodule;
To convert relative links to absolute and add to #INC, I normally use ex::lib. Of course, this is useful only if you plan to keep the relative locations of the script and the module identical on all machines where you plan to run your script.
Just keep it simple. There is no need to import any libraries; just find out your current working directory:
use lib "$ENV{PWD}/relativ_path_own_perllib";
For similar problems, you can read out the environment variable which gives your information about where you are, your home directory, operating system stuff, and so on, with just one row of programming code in the shell-terminal, like:
perl -e 'map { print; print " : ". $ENV{$_}." \n\r"; } sort keys %ENV '
There is no need to bind some libraries; just use the %ENV-Hash.
FindBin::libs will find all your libs placed at reasonable places relative to the path from where your script is running.

How can I force Perl to run the module from the test directory only?

I have perl module file and test script pl which I use to run some function, and after browsing through the use lib and this previous question here...
I still don't know how to do this.
Basically I have a production directory which contains the module file, and I have a test directory file which contains the same module and the test script file. each time I try to run the script file, it will automatically calls the module in the directory. by printing out the #INC, it seems that the production directory is hosted in there. thus I try to remove it by using
no lib qw(prod_dir);
and while printing out the #INC shows that the directory is no longer there, somehow the script is still calling that other module...
I know this probably sounds really dumb, but hope someone can help me see the light here :)
thanks.
After you have required or used the module, check %INC to see where it came from.
For example:
use Data::Dumper;
print $INC{'Data/Dumper.pm'}."\n";
Note that "::" becomes "/" and you append ".pm". That might give you a clue.
Remember that the current directory (".") is usually an entry in #INC. But the first step is finding out what directory the module was loaded from.
Another thing to remember is that the directories in #INC are searched in order. use lib prepends to that list (making it the first-searched directory), so you may just need to add the appropriate directory.
Can you say more about what you are trying to do and how you are trying to do it? Is this stuff in a standard Perl distribution structure? If you aren't using the standard distribution structure, can you show us a directory listing so we know where things are? Can you also include the code you use to try to load the module? Just update your original question when you pull together the details.
Typically, I run tests through the build runner, which automatically sets up the right #INC.
If I want to run one test in my distribution, I use the blib module to find the build library which has the development versions of my modules:
% perl -Mblib t/test.t
Some people do the same thing with prove.
If you aren't using the basic distribution set-up, consider using it. The tools and best techniques rely on it.
If you just have your module and test file in the same directory, have you tried adding the current directory to #INC with PERL5LIB?