How to change @INC to find Perl modules in non-standard locations
When using modules that are not installed in the standard directories of Perl, we need to change @INC so perl will be able to find them. There are several ways to do that solving different use-cases.
We are going to see a use-case and a solution, but if you have arrived to this page, I wonder, do you really have a special case, or do you just need to install the Perl module?
Loading your private Perl module
You have a script and have just started to move some parts of it, out to a new module called My::Module. You saved the module to /home/foobar/code/My/Module.pm.
Your perl script now starts like this:
use strict; use warnings; use My::Module;
When you run the script you get a friendly error message like this:
Can't locate My/Module.pm in @INC (@INC contains: /home/foobar/perl5/lib/perl5/x86_64-linux-gnu-thread-multi /home/foobar/perl5/lib/perl5 /etc/perl /usr/local/lib/perl/5.12.4 /usr/local/share/perl/5.12.4 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.12 /usr/share/perl/5.12 /usr/local/lib/site_perl .). BEGIN failed--compilation aborted.
Perl cannot find your module.
Upgrading a Perl module
On a system you are thinking to upgrade a module that came from CPAN. You don't want to install it in the standard location yet. First you'd like to put it in some private directory, try it, and install to the system only once you are sure it works well.
In this case too you "install" the module in a private directory, e.g. in /home/foobar/code and somehow you'd like to convince perl to find that version of the module, and not the one that was installed in the system.
The use statement
When perl encounters use My::Module; it goes over the elements of the built-in @INC array that contains directory names. In each directory it checks if there is a subdirectory called "My" and if in that subdirectory there is a file called "Module.pm".
The first such file it encounters will be loaded into memory.
If it does not find the file you get the above error messages.
@INC is defined when perl is compiled and it is embedded in the binary code. You cannot change that, unless you recompile perl. Not something we would do every day.
Luckily the @INC array can be changed in several ways when we execute a script. We'll see these solutions and discuss when each one of them is appropriate to use.
PERLLIB and PERL5LIB
You can define the PERL5LIB environment variable (though PERLLIB works the same way, I'd recommend using PERL5LIB over PERLLIB as that makes it clear it is related to Perl 5) the same way you can define the PATH environment variable. Every directory listed in this variable will be added to the beginning of @INC.
On Linux/Unix when using Bash, you would write
You can add this to the ~/.bashrc to make it always available when you log-in.
On Windows you can set the same in the cmd command window by typing
For a more long term solution follow these steps:
Right-click My Computer and click Properties.
In the System Properties window, click on the Advanced tab.
In the Advanced section, click the Environment Variables button.
In the Environment Variables window in the "User variables for Foo Bar" section click on New and type in the following:
Variable name: PERL5LIB
Variable value: c:\path\to\dir
Then click OK 3 times. Windows that you open after this will already know about the new variable. Type this in the command window, to see the newly set value:
This will add the private /home/foobar/code directory (or c:\path\to\dir directory) to the beginning of @INC for every script that is executed in the same environment.
In taint mode, that will be explained in a separate post, the PERLLIB and PERL5LIB environment variables are ignored.
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.
You just have to make sure to have the use lib statement before trying to load the module:
use lib '/home/foobar/code'; use My::Module;
One note here. I saw several companies where use lib statements were added to the modules so they will be able to load their dependencies. I don't think this is good. I think the right place to change @INC is the main script or even better, outside the script such as in the two other solutions.
-I on the command line
(That's a capital i)
The last solution is the most temporary solution. Add a -I /home/foobar/code flag to perl when running the script.
perl -I /home/foobar/code script.pl
This will add /home/foobar/code to the beginning of @INC for this specific execution of the script.
So which one to use?
If you would like to just test a newer version of a module, I'd recommend the command line flag: perl -I /path/to/lib.
If you are installing lots of modules in a private directory then I'd probably use PERL5LIB though we'll also see local::lib that does this for you.
use lib is used in two cases:
- When you have a fixed, but not standard company-wide environment in which you put modules in a common standard location.
- When you are developing an application and you'd like to make sure the script always picks up the modules relative to their own location. We'll discuss this in another post.