I've taken over a project written in Perl that has a few dependencies such as Template::Toolkit, Image::ExifTool, and GD to name a few. Currently, these dependencies are built into a directory ("deps" folder) using the --prefix option in make. In the main Perl script, I have something like the following to import the necessary modules:
use File::Spec::Functions qw(catdir);
use lib "lib";
use lib catdir(qw(deps lib perl5 site_perl));
catdir is used to stray away from certain system requirements (such as assuming '/' is the folder separator; if someone has another alternative, I'm open to suggestions!). The "lib" folder contains my modules, and the "deps/lib/perl5/site_perl" folder is where my dependencies were installed on my original machine.
However, after building the dependencies on another machine, it seems the needed libraries have moved around to different folders. I now need to use these statements for my script to run correctly:
use File::Spec::Functions qw(catdir);
use lib "lib";
use lib catdir(qw(deps lib perl 5.14.2));
use lib catdir(qw(deps share perl 5.14.2));
I was wondering if there was anyway specify the prefix directory ("deps") and have the use lib pragma recursively search through that directory for my dependencies. Also, the users of this script do not have access to the Internet, so compiling my project as a CPAN module would be counterproductive.
Thanks for all the help!
Use INSTALL_BASE for the Makefile.PL and --install_base for Build.PL when installing modules. They provide stable install locations. PREFIX and --prefix try to emulate how perl has its system libraries laid out and is more useful for making packages.
Also you'll want to run rel2abs over the path before feeding it to lib to give you an absolute path. This will protect you in case your code chdirs and then tries to require a module. Or you can use lib::abs.
And ikegami is correct, it's unnecessary to make the file paths passed to lib portable. Just pass them in Unix style. Perl will figure it out.
Related
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
I am looking into rewriting a old application in Perl.
Setting up three packages and running subroutines from them makes a lot of sense for what I'm going to be doing, but I'm not very familiar with setting up packages.
I want the packages to be in the same place as my other Perl scripts, i.e. nothing other than scripts in this bin directory will need to call these packages.
My question is how can I point Perl to know where my packages are (and how do I install them in a place other then the default) and is this an okay/smart thing to do?
This is a common task. The PERL5LIB environment variable contains a list of directories to look for a module in. You can also use the the lib pragma to specify directories a specific script should look for module in:
#!/usr/bin/perl
use strict;
use warnings;
use lib "$ENV{HOME}/lib";
You might also want to look into cpanm and local::lib.
If the packages and scripts are in the same location, I'd recommend using FindBin:
use FindBin '$Bin';
use lib $Bin;
I don't understand what use local::lib does that regular use lib doesn't. Could someone explain it?
local::lib
Defaults to ~/perl5 if you don't specify a directory (while use lib; is a no-op).
Resolves relative paths to absolute paths before adding them to #INC. (lib just adds the relative path as-is.)
Expands ~ and ~user in the directory name.
Appends /lib/perl5 to the directory you specify. (So use local::lib '/foo'; is somewhat equivalent to use lib '/foo/lib/perl5';.)
Prepends DIR/bin to your PATH, so you can use scripts installed by local modules.
use lib adds a directory to your module search path (#INC). It has no effect on anything outside of the program or module which contains the use lib directive.
local::lib is intended to be used to enable a private module installation directory and, if you configure your shell environment in the way that it recommends, this private directory will be used for all Perl module installations (whether via CPAN or manual make install) and modules installed there will be made available to all Perl programs/modules run from within your local::lib-aware shell environment.
Regular use lib foo is almost as simple as:
BEGIN { unshift(#INC, foo) }
Whereas use local::lib sets many other Perl environment variables to make sure you can install modules locally, see the source.
I have a Perl script which uses installed packages. One is a Perl package another one is Perl XS package.
Now I want to call this script but use not installed packages, but packages with the same name by path.
I used perl -I /home/.../lib script.pl but it doesn't work
How can I do it?
For various ways of affecting where your modules are loaded, please review this SO posting:
How is Perl's #INC constructed? (aka What are all the ways of affecting where Perl modules are searched for?)
You can use the lib pragma to prepend directories to the path perl uses to find modules.
So, if there is a module named Foo installed in the default directories and a different version installed in /home/cowens/perl5 you can say
use lib "/home/cowens/perl5";
use Foo;
and perl will find the version in /home/cowens/perl5.
Can you show us a recursive listing of the directory where you're storing the modules you want to use? An ls -R could help us figure out if you have the right paths.
When you use the -I switch, you have to ensure you get the right path in there. If you use a module:
use Some::Module;
Perl actually looks for:
$lib/Some/Module.pm
The $lib is one of the directories in #INC. Another way to say that though, is that if the particular directory is not in #INC, Perl isn't going to look in it. This means that Perl won't automatically look in subdirectories for you
If your module is not at that location, Perl is not going to rummage around in that $lib to look for it. Your XS module is probably not stored like that. It might have a Perl version and archtype in the path, so you might find it in:
$lib/5.10.1/darwin-2level/Some/Module.pm
You need to add those paths yourself if you are using -I.
However, you can load modules on the command line. It's much easier to use lib, which adds the extra directories for you:
perl -Mlib=/path/to/lib ...
I wrote some modules, and trying to run them. How I tell where to take the module from?
I am writing in Windows, and tried to put it in c:\perl\lib but it didn't help.
Can I put it in the same directory, as the file that calls the module?
Perl uses the paths in:
#INC
to search for modules. (.pm files)
If you:
perl -e 'print join "\n", #INC;'
you'll see what paths are currently being searched for modules. (This #INC list is compiled into the perl executable)
Then you can:
BEGIN { unshift #INC, 'C:\mylibs' }
or
use lib 'C:\mylibs'
and place MyModule.pm inside C:\mylibs to enable:
use MyModule;
This is exactly what local::lib is designed to handle. By default, use local::lib; will prepend ~/perl5 to your module search path (#INC), but you can easily tell it to add another directory.
If you're doing it with a fixed directory (rather than one relative to your home directory), then you're probably just as well off with use lib 'PATH'.
If this is for code that will only be run from the command line, another option would be to create a PERL5LIB environment variable which points to your personal module directory. This would affect all Perl run by your user account from the command line, so you wouldn't need to modify your Perl code at all with this method, but it's trickier to set up for non-command-line code (e.g., web scripts) and, if the code will be run by multiple users, PERL5LIB will need to be set for all of them.
I wouldn't recommend mucking about with #INC directly in any case. There are plenty of easier ways to do it these days.
For application-specific libraries the common approach is to use the FindBin module to locate the application directory and then use lib to add the application's library to #INC:
use FindBin;
use lib "$FindBin::Bin/lib";
use AppModule;
For general-purpose modules, I recommend developing them the same way you would for a CPAN release (e.g. start with module-starter) and install them with perl (usually under site/lib). See also: Which framework should I use to write modules?
If you can't install with perl (i.e. you don't have the necessary permissions) you can have a personal library instead. See How do I keep my own module/library directory? in perlfaq8.