Reputation: 4416
Background: I have a perl module -- let's call it Foo::Common
which has been installed on one of our file servers (for the sake of simplicity, I will call thie the 'global' version). The module is included by somewhere on the order of 1000 perl scripts which are launched every 15 minutes. The module is several versions old, but Updating it is going to take a lot of testing over the course of several months.
I'm writing a new perl script which will run on the same processing server, but I need functionality which is only included in the most recent version of Foo::Common
. If I simply want to use the newer version of Foo::Common
, I can place Foo/Common.pm
in a ./lib
directory where the script is being run, then use the following code:
my $lib = '';
BEGIN {
$lib = $ENV{FOO_ENV_HOME} || '';
}
use lib "$lib/core/bin/";
use lib './lib';
use Foo::Common;
Because use lib
adds directories to the beginning of @INC
, the newer version of Foo::Common
will be found and used first.
This is all well and good, but I don't want my script dependent on the local version of Foo::Common
. The global version of Foo::Common
has our $VERSION = '1.1.2'
, and the version that I want to install locally is at 1.1.7
. If I deploy the code above, and then we upgrade the global version to the not-yet-written 1.2.0
, my script will be stuck running 1.1.7
.
According to perldoc -f require
:
`require $filename` Has semantics similar to the following subroutine:
sub require {
my ($filename) = @_;
if (exists $INC{$filename}) {
return 1 if $INC{$filename};
die "Compilation failed in require";
}
my ($realfilename,$result);
ITER: {
foreach $prefix (@INC) {
$realfilename = "$prefix/$filename";
if (-f $realfilename) {
$INC{$filename} = $realfilename;
$result = do $realfilename;
last ITER;
}
}
die "Can't find $filename in \@INC";
}
if ($@) {
$INC{$filename} = undef;
die $@;
} elsif (!$result) {
delete $INC{$filename};
die "$filename did not return true value";
} else {
return $result;
}
}
I'm not clear on how this interacts with the use MODULE VERSION
syntax, although discussion in the answer to What does Perl do when two versions of a module are installed? indicates that use Module Version
is not enough.
I presume that I'm going to manipulate @INC, but I'm not sure how to do that based on each module's $VERSION
, especially because use
has an implicit BEGIN
statement.
How do I approach this?
Upvotes: 2
Views: 286
Reputation: 385789
You can't make the check based on the version since $VERSION
doesn't exist until after it has been loaded aka executed.
So you want the "global" version used if present, and the local version used otherwise? Append the local path to @INC instead of prepending it.
BEGIN {
push @INC, "$ENV{FOO_ENV_HOME}/core/bin";
if $ENV{FOO_ENV_HOME};
}
use Foo::Common;
If you can't rearrange @INC
because it would affect something else, try to load Foo::Common before changing @INC
.
BEGIN { eval { require Foo::Common; }; }
use lib ...;
use Foo::Common;
use ...;
The use Foo::Common;
won't load the module if it was already loaded earlier, and it will import from whichever version did get loaded (no matter where it got loaded).
Upvotes: 2