【问题标题】:How do I conditionally change @INC based on module version?如何根据模块版本有条件地更改@INC?
【发布时间】:2012-10-04 02:54:45
【问题描述】:

背景:我有一个 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 有一个隐含的BEGINstatement。

我该如何处理?

【问题讨论】:

    标签: perl module


    【解决方案1】:

    太阳底下没有新鲜事。

    use @987654321@ 'Foo::Common'

    【讨论】:

      【解决方案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; 不会加载该模块,如果它之前已经加载过,它将从加载的任何版本导入(无论它是从哪里加载的)。

      【讨论】:

      • 不,不完全...我想使用$ENV{FOO_ENV_HOME}/core/bin/Foo/Common.pm 当且仅当它的$VERSION 大于或等于./lib/Foo/Common.pm$VERSION
      • 如果我运行eval { require Foo::Common; }; 检查它是否为$VERSION,然后如果之前的$VERSION 不够高,则更改@INC 并重新运行eval { require Foo::Common; };? (我没有从 Foo::Common 命名空间中导出任何内容,所以我不需要使用use
      • 执行这两个模块会导致问题,完全没有必要。
      • 请确认我对问题的分析是正确的(所以您想要“全局”版本(如果存在),否则使用本地版本?
      • 这是一件很奇怪的事情,顺便说一句。
      猜你喜欢
      • 1970-01-01
      • 2021-01-30
      • 1970-01-01
      • 1970-01-01
      • 2014-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多