drupsspen
drupsspen

Reputation: 21

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.

Upvotes: 2

Views: 2013

Answers (4)

amphetamachine
amphetamachine

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/.

TMTOWTDI

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

David W.
David W.

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

hexcoder
hexcoder

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

salparadise
salparadise

Reputation: 5805

Couldn't you use findbin and lib ?:

use FindBin qw($Bin);
use lib "$Bin/lib";

Upvotes: 3

Related Questions