2012-12-04 162 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,看看發生了什麼。