如何根据模块版本有条件地更改@INC?

时间:2012-10-03 17:50:14

标签: perl module

背景:我有一个perl模块 - 让我们称之为Foo::Common已经安装在我们的一个文件服务器上(为了简单起见,我将称之为'全局'版本)。该模块包含在大约1000 perl脚本的某个地方,每15分钟启动一次。该模块有几个旧版本,但更新它将需要在几个月的时间内进行大量测试。

我正在编写一个新的perl脚本,它将在同一个处理服务器上运行,但我需要的功能只包含在最新版本的Foo::Common中。如果我只想使用较新版本的Foo::Common,我可以将Foo/Common.pm放在运行脚本的./lib目录中,然后使用以下代码:

my $lib = '';
BEGIN {
    $lib = $ENV{FOO_ENV_HOME} || '';
}
use lib "$lib/core/bin/";
use lib './lib';
use Foo::Common;

由于use lib将目录添加到@INC的开头,因此将首先找到并使用较新版本的Foo::Common

这一切都很好,但我不希望我的脚本依赖于Foo::Common的本地版本。 Foo::Common的全局版本为our $VERSION = '1.1.2',我想在本地安装的版本位于1.1.7。如果我部署上面的代码,然后我们将全局版本升级到尚未编写的1.2.0,我的脚本将停止运行1.1.7

根据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;
   }
}

我不清楚这与use MODULE VERSION语法的交互方式,尽管What does Perl do when two versions of a module are installed?的答案中的讨论表明use Module Version还不够。

我认为我将操纵@INC,但我不确定如何根据每个模块$VERSION执行此操作,尤其是因为use具有隐式BEGIN言。

我该如何处理?

2 个答案:

答案 0 :(得分:4)

在阳光下没有什么新东西。

use only::latest 'Foo::Common'

答案 1 :(得分:2)

您无法根据版本进行检查,因为$VERSION不存在,直到加载后才会执行。


所以你想要"全球"如果存在则使用的版本,否则使用本地版本?将本地路径附加到@INC而不是预先添加它。

BEGIN {
   push @INC, "$ENV{FOO_ENV_HOME}/core/bin";
      if $ENV{FOO_ENV_HOME};
}

use Foo::Common;

如果您无法重新排列@INC,因为它会影响其他内容,请在更改@INC之前尝试加载Foo :: Common。

BEGIN { eval { require Foo::Common; }; }
use lib ...;
use Foo::Common;
use ...;

use Foo::Common;如果先前已加载模块,则不会加载该模块,并且它将从加载的任何版本(无论加载到何处)导入。