2012-12-04 135 views
2

我有三个文件:Perl - 包含来自相对路径的包,其中包含来自相对路径的另一个包?

〜/ multiFindBinTest.pl:

use FindBin; 
use lib "$FindBin::Bin/mod2"; 
use pack2; 

〜/ MOD1/pack1.pm

package pack1; 
1; 

〜/ MOD2/pack2.pm

use FindBin; 
use lib "$FindBin::Bin/../mod1"; 
use pack1; 
package pack2; 
1; 

正如你所看到的,base.pl使用pack2,它反过来使用pack1。但是,这是演示如何不使用FindBin模块:当执行base.pl时,pack2将无法找到pack1,因为它将保留从base获取的“$ FindBin :: Bin”的值特等。

所以我的问题很简单:有没有在Perl的方法“用”的“使用”的所有基于路径相对于它执行“使用”的文件另一个模块,一个模块?

+0

什么是'base.pl'? – Borodin

+0

我认为模块不应该与@INC混淆。 – ikegami

回答

2

找到模块文件位置的唯一方法是使用__FILE__FindBin$0都始终指向主脚本文件。

对于模块,这是我能想到的最好的。你对主代码的解决方案很好,但你也可以在这里使用这个替代方案。

use strict; 
use warnings; 

use File::Basename 'fileparse'; 
use File::Spec; 

my $dir; 
BEGIN { 
    $dir = (fileparse(File::Spec->rel2abs(__FILE__)))[1]; 
} 
use lib $dir.'../mod1'; 

use pack1; 

package pack2; 

1; 
+0

我更喜欢'File :: Basename :: dirname(Cwd :: realpath(__ FILE __))'。它支持更好的符号链接。 – ikegami

+0

@ikegami:我很少使用'Cwd'。当文档警告它时,我避免了'File :: Basename :: dirname',但是错误地使用了'File :: Basename :: fileparse'而不是推荐的'File :: Spec-> splitpath'。我会离开它,因为它现在是为了避免混淆 – Borodin

+0

你很少使用Cwd只有与F :: S做同样的事情才有意义。它没有。 Cwd解析符号链接(一件好事),而F :: S不能。 'fileparse'实际上是建议的替代方案,而不是F :: S的'splitpath'(尽管我相信两者都是相同的)。 – ikegami

2

如果你知道所有可能的库根,你可以将它们添加在命令行上:

perl -I~/mod1 -I~/mod2 myscript.pl 

可以将它们添加到PERL5LIB环境变量:

export PERL5LIB=~/mod1:~/mod2 

无论是方法将目录放在libaray搜索路径上。


产生额外的信息:

如果你想在那里它们的依赖生活的各个包“申报”,Perl提供的“LIB”编译:

use lib '/path/to/lib/directory'; 
+0

这需要执行脚本的人知道所有图书馆的位置。有没有办法让软件包指定在哪里找到它们的依赖关系? –

+0

是的。我会编辑上面的答案。 –

2

模块的位置必须在use语句被编译的那一刻在@INC中。最简单的方法是添加他们都在调用程序Test.pl这样

use lib "$FindBin::Bin/../mod1", "$FindBin::Bin/../mod2"; 

,那么所有的模块的编译将继续罚款。

0

你没有那个,但你可以做你自己的。

package libr; 

use strict; 
use warnings; 

use File::Spec; 

sub import { 
    shift; # invoker 
    my (@cands, @missed); 

    ARGS: 
    while (@_) { 

     # Get the next argument from the queued candidates or from the 
     # arguments 
     my $raw_path 
      = my $path 
      = @cands ? shift @cands : shift 
      ; 

     # We don't need to worry about this argument unless it has relative 
     # notation in it. 
     if (index($path, '::') > -1) { 

      # split it into parts 
      my ($mod, $rest) = split qr{(?:/(?:\.(?=/))?)+}, $path, 2; 
      $mod =~ s/^:://; # Allow for one-word relative nodes: 'Word::/'; 

      # Move it from mod notation to file... 
      my ($mod_path) = map { s|::|/|g; $_ } $mod; 

      my %set; 
      while (my $len = length $mod_path) { 

       # Remember the more specific path first 
       $set{ $_ } ||= $mod_path 
        foreach 
         # for each key that qualifies, subtract the same 
         # number of characters from the end of the value 
         map { substr($INC{ $_ }, 0, $len - length) . $rest } 

         # test each key that it starts with our string 
         grep { substr($_, 0, $len) eq $mod_path } 

         keys %INC 
        ; 
      } 
      continue { 
       # Check if our separator is in the mod path. 
       my $mark = rindex($mod_path, '/'); 
       last if $mark == -1; 

       # move the unmatched part of the substring to the 
       # ending 
       substr($rest, 0, 0, substr($mod_path, $mark)); 
       # delete it from the path 
       substr($mod_path, $mark) = ''; 
      } 
      my @sort_order 
        # We only want the first value... 
       = map { shift @$_ } 
        # sort by length of matching path first, then alphabetically 
        sort { $b->[2] <=> $a->[2] or $a->[1] cmp $b->[1] } 
        # store a collection of values for sorting: 
        # lowercase string and length of matching string 
        map { [ $_ => lc $_ => length $set{ $_ } ] } 
        keys %set 
       ; 
      ### Assemble list of candidates 
      @cands = (@sort_order, map { "$_/$mod_path$rest" } @INC); 
      next ARGS; 
     } 

     # If the path exists 
     if (-e $path) { 
      # Store the canonical path 
      push @INC, File::Spec->canonpath($path); 
      # And reset tracking arrays 
      @cands =() if @cands; 
      @missed =() if @missed; 
     } 
     elsif (@cands) { 
      # If we're trying out values, just remember the missed ones. 
      push @missed, $path; 
     } 
     else { 
      # Else, we're going to tell you we couldn't match the directory 
      # either to one or to all candidates we tried. 
      Carp::carp( 
        "A valid path cannot be determined from '$raw_path': " 
       . (@missed > 1 ? do { 
         local $LIST_SEPARATOR = "\n - "; 
         push @missed, '', $path; 
         "\n No paths:@missed\n do not exist!"; 
        } 
        : "$path does not exist!" 
       )); 
      @missed =() if @missed; 
     } # end else 
    } # end while @_ 
} 

然后你使用这样的:

package main; 

use A::Long::Package::Name; 
use Smart::Comments; 

use libr 'A::Long::Package::Name/../Foo', 'Figgy::Puddin'; 

尝试的话后,倾出@INC,看看发生了什么。