Reputation: 21
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.
Upvotes: 2
Views: 2013
Reputation: 30595
Inside your main program script (NOT your .pm files) include the following lines near the top.
# This prepends to the module load path.
use FindBin qw//;
use lib "$FindBin::RealBin/lib";
Any modules loaded BELOW those lines will come FIRST from the local 'lib' directory.
# Loads from lib/MyLocalLib/MyClass.pm
require MyLocalLib::MyClass;
How FindBin
works is the first time it's loaded via use
, it sets $FindBin::RealBin
to the directory name of the currently executing script ($0
). FindBin
should ship with Perl.
You'll want to use $FindBin::RealBin
and not $FindBin::Bin
in case your script is executing from a symbolic link. (e.g. you symlinked it to /usr/bin/myprogram.pl
). If you use $FindBin::Bin
, it'll try loading from /usr/bin/lib/
.
If you prefer a less-robust solution, the following method will do the more-or-less the same thing as above:
use File::Basename 'dirname';
use Cwd 'abs_path';
use lib dirname(abs_path($0)) . '/lib';
Upvotes: 0
Reputation: 107040
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.
Upvotes: 2
Reputation: 1220
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
Upvotes: 0
Reputation: 5805
Couldn't you use findbin and lib ?:
use FindBin qw($Bin);
use lib "$Bin/lib";
Upvotes: 3