ExtUtils::MakeMaker::FAQ - MakeMaker 常見問題
ExtUtils::MakeMaker 的常見問題、技巧和提示。
如果您不是 Perl 管理員,您可能沒有權限將模組安裝到其預設位置。處理此問題的方法(您需要手動執行的步驟少很多)為 perlbrew 和 local::lib。
否則,您可以將其安裝到您的主目錄中,供您自己使用,如下所示
# Non-unix folks, replace ~ with /path/to/your/home/dir
perl Makefile.PL INSTALL_BASE=~
這會將模組放入 ~/lib/perl5、手冊頁放入 ~/man 和程式放入 ~/bin。
為確保您的 Perl 程式可以看到這些新安裝的模組,請將您的 PERL5LIB
環境變數設定為 ~/lib/perl5,或告訴您的每個程式使用下列方式查看該目錄
use lib "$ENV{HOME}/lib/perl5";
或者,如果未設定 $ENV{HOME} 且您不想設定它,請使用較長的方式。
use lib "/path/to/your/home/dir/lib/perl5";
Module::Build(0.28 版)支援兩種方法,可將其安裝到與 MakeMaker 相同的位置。
我們強烈建議使用 install_base 方法,這是最簡單的方法,且最接近安裝前置字元的預期行為。
1) 使用 INSTALL_BASE / --install_base
MakeMaker(6.31 版)和 Module::Build(0.28 版)都可以使用「install_base」概念安裝到相同的位置。有關詳細資訊,請參閱 ExtUtils::MakeMaker 中的「INSTALL_BASE」。若要讓 MM 和 MB 安裝到相同的位置,只需在 MM 中設定 INSTALL_BASE,並在 MB 中將 --install_base
設定為相同的位置即可。
perl Makefile.PL INSTALL_BASE=/whatever
perl Build.PL --install_base /whatever
當您指定前置字元時,這最類似於其他語言的行為。我們建議使用此方法。
2) 使用 PREFIX / --prefix
Module::Build 0.28 新增了對 --prefix
的支援,其作用類似於 MakeMaker 的 PREFIX。
perl Makefile.PL PREFIX=/whatever
perl Build.PL --prefix /whatever
我們強烈不建議使用此方法。只有在您知道自己在做什麼,且特別需要 PREFIX 行為時,才應使用此方法。PREFIX 演算法很複雜,且專注於符合系統安裝。
預設情況下,MakeMaker 的最新版本只會在類 Unix 作業系統上安裝手冊頁。若要在非 Unix 作業系統上產生手冊頁,請建立「manifypods」目標。
針對個別模組
perl Makefile.PL INSTALLMAN1DIR=none INSTALLMAN3DIR=none
如果您想禁止安裝所有模組的手冊頁,您必須重新設定 Perl,並在詢問手冊頁安裝位置時告訴它「none」。
有兩種方法。一種是正常建立模組...
perl Makefile.PL
make
make test
...然後使用 blib 讓 Perl 指向已建立但未安裝的模組
perl -Mblib script.pl
perl -Mblib -e '...'
另一種方法是在暫時位置安裝模組。
perl Makefile.PL INSTALL_BASE=~/tmp
make
make test
make install
然後將 PERL5LIB 設為 ~/tmp/lib/perl5。當您有多個模組需要使用時,這方法很好用。它也能確保模組會經歷完整的安裝程序,可能會修改模組。同樣地,local::lib 在這裡可以協助您。
讓我們使用下列測試目錄結構
t/foo/sometest.t
t/bar/othertest.t
t/bar/baz/anothertest.t
現在,在 Makefile.PL 中的 WriteMakefile()
函數內,使用 test
指令指定測試位置
test => {TESTS => 't/*.t t/*/*.t t/*/*/*.t'}
字串中的第一個項目會執行 t/ 頂層目錄中的所有測試。第二個項目會執行位於 t/ 下任何子目錄中的所有測試檔案。第三個項目會執行位於 t/ 下任何其他子目錄中的任何子目錄內的測試檔案。
請注意,您不必使用萬用字元。您可以明確指定要執行測試的子目錄
test => {TESTS => 't/*.t t/foo/*.t t/bar/baz/*.t'}
PREFIX 的行為很複雜,而且高度依賴於 Perl 的設定方式。安裝位置的結果會因機器而異,甚至同一台機器上的不同 Perl 安裝也會不同。因此,很難說明 prefix 會將模組放在哪裡。
相較之下,INSTALL_BASE 有可預測、容易說明的安裝位置。現在 Module::Build 和 MakeMaker 都具有 INSTALL_BASE,除了保留現有的安裝位置之外,幾乎沒有理由使用 PREFIX。如果您要開始新的 Perl 安裝,我們建議您使用 INSTALL_BASE。如果您有透過 PREFIX 安裝的現有安裝,請考慮將其移至與 INSTALL_BASE 相符的安裝結構,並改用該結構。
如果您想要針對當地條件設定模組檔案,或自動插入版本號碼,可以使用 EUMM 的 PL_FILES
功能,它會自動執行找到的每個 *.PL 以產生其基本名稱。例如
# Makefile.PL:
require 'common.pl';
my $version = get_version();
my @pms = qw(Foo.pm);
WriteMakefile(
NAME => 'Foo',
VERSION => $version,
PM => { map { ($_ => "\$(INST_LIB)/$_") } @pms },
clean => { FILES => join ' ', @pms },
);
# common.pl:
sub get_version { '0.04' }
sub process { my $v = get_version(); s/__VERSION__/$v/g; }
1;
# Foo.pm.PL:
require 'common.pl';
$_ = join '', <DATA>;
process();
my $file = shift;
open my $fh, '>', $file or die "$file: $!";
print $fh $_;
__DATA__
package Foo;
our $VERSION = '__VERSION__';
1;
您可能會注意到上面沒有指定 PL_FILES
,因為將每個 .PL 檔案對應到其基本檔名的預設值運作良好。
如果產生的模組是特定於架構的,您可以將上面的 $(INST_LIB)
替換為 $(INST_ARCHLIB)
,不過如果您將模組放在 lib 底下,這會需要確保模組位置前面的任何 lib/
都已移除。
正如它所說的,您缺少該檔案。MakeMaker 使用它來判斷自建立 Makefile 以來 perl 是否已重新建置。它會暫停安裝,這有點像錯誤。
有些作業系統不會在基本 perl 安裝中附帶 CORE 目錄。要解決此問題,您可能需要安裝 perl 開發套件,例如 perl-devel(CentOS、Fedora 和其他 Redhat 系統)或 perl(Ubuntu 和其他 Debian 系統)。
為什麼 MakeMaker 要重新發明建置設定的輪子?為什麼不乾脆使用 autoconf 或 automake 或 ppm 或 Ant 或 ...
原因有很多,但最主要的因素是跨平台相容性。
Perl 是有史以來移植次數最多的軟體之一。它可以在我從未聽過的作業系統上執行(有關詳細資訊,請參閱 perlport)。它需要一個可以在所有這些平台上執行,並與任何古怪的 C 編譯器和連結器搭配使用的建置工具。
沒有這樣的建置工具。即使是 make 本身也有截然不同的方言。因此,我們必須建立自己的工具。
Module::Build 是 Ken Williams 的一個專案,目的是取代 MakeMaker。它的主要優點是
純 perl。沒有 make,沒有 shell 指令
更易於自訂
更簡潔的內部
更少的雜質
Module::Build 長期以來一直是 MakeMaker 的官方繼承人。然而,近年來它的開發和採用速度都變慢了,目前尚不清楚它的未來會如何。話雖如此,Module::Build 為某個東西成為 MakeMaker 的繼承人奠定了基礎。MakeMaker 的維護人員長期以來一直表示它是一個死胡同,應該保持運作,同時謹慎地擴充新功能。
你經常想要在主模組發行版中手動設定 $VERSION,因為這是每個人在 CPAN 上看到的版本,而且你可能想要自訂它一點。但對於發行版中的所有其他模組,$VERSION 實際上只是簿記,而且重要的是每次模組變更時它都會增加。手動執行此操作很麻煩,而且你經常會忘記。
執行此操作最簡單的方法可能是使用 Perl::Version 中的 perl-reversion
perl-reversion -bump
如果你的版本控制系統支援修訂號碼(git 不容易),自動執行它的最簡單方法是使用它的修訂號碼(你正在使用版本控制,對吧?)。
在 CVS、RCS 和 SVN 中,你使用 $Revision$(有關詳細資訊,請參閱版本控制系統的說明文件)。每次檔案簽入時,$Revision$ 都會更新,更新你的 $VERSION。
SVN 使用一個簡單的整數作為 $Revision$,因此你可以像這樣調整它以符合你的 $VERSION
($VERSION) = q$Revision$ =~ /(\d+)/;
在 CVS 和 RCS 中,版本 1.9 之後是 1.10。由於 CPAN 以數字方式比較版本號碼,因此我們使用 sprintf() 將 1.9 轉換為 1.009,將 1.10 轉換為 1.010,以便正確比較。
$VERSION = sprintf "%d.%03d", q$Revision$ =~ /(\d+)\.(\d+)/g;
如果涉及分支(例如 $Revision: 1.5.3.4$),則會稍微複雜一些。
# must be all on one line or MakeMaker will get confused.
$VERSION = do { my @r = (q$Revision$ =~ /\d+/g); sprintf "%d."."%03d" x $#r, @r };
在 SVN 中,$Revision$ 應與專案中的每個檔案相同,因此它們都會有相同的 $VERSION。CVS 和 RCS 每個檔案都有不同的 $Revision$,因此每個檔案都會有不同的 $VERSION。分散式版本控制系統,例如 SVK,可能會根據誰簽出檔案而有不同的 $Revision$,導致每台機器上的 $VERSION 不同!最後,一些分散式版本控制系統,例如 darcs,根本沒有版本號的概念。
META.yml 是由 Module::Build 開創的模組元資料檔案,並自動產生為「distdir」目標(因此為「dist」)的一部分。請參閱"Module Meta-Data" in ExtUtils::MakeMaker。
若要關閉其產生,請將 NO_META
旗標傳遞給 WriteMakefile()
。
有些人驚訝於 make distclean
沒有刪除 MANIFEST 中未列出的所有內容(因此建立一個乾淨的發行版),而只是告訴他們需要刪除哪些內容。這樣做是因為這被認為太危險了。在開發模組時,您可能會寫一個新檔案,沒有將它新增到 MANIFEST 中,然後執行 distclean
,並因為您的新工作被刪除了而感到難過。
如果您真的想這樣做,您可以使用 ExtUtils::Manifest::manifind()
來讀取 MANIFEST,並使用 File::Find 來刪除檔案。但是您必須小心。以下是一個用來這樣做的指令碼。請自行承擔風險。盡情享受在您的腳上開洞的樂趣。
#!/usr/bin/perl -w
use strict;
use File::Spec;
use File::Find;
use ExtUtils::Manifest qw(maniread);
my %manifest = map {( $_ => 1 )}
grep { File::Spec->canonpath($_) }
keys %{ maniread() };
if( !keys %manifest ) {
print "No files found in MANIFEST. Stopping.\n";
exit;
}
find({
wanted => sub {
my $path = File::Spec->canonpath($_);
return unless -f $path;
return if exists $manifest{ $path };
print "unlink $path\n";
unlink $path;
},
no_chdir => 1
},
"."
);
我們建議使用 Archive::Tar 中的 ptar,版本不低於 1.66,並加上「-C」選項。
我們建議使用 InfoZIP:http://www.info-zip.org/Zip.html
XS 程式碼對模組版本號碼非常敏感,如果 Perl 模組中的版本號碼不符,它會抱怨。如果你變更模組的版本號碼,但沒有重新執行 Makefile.PL,舊版本號碼將保留在 Makefile 中,導致 XS 程式碼使用錯誤的號碼建置。
為避免這種情況,你可以透過將這項內容新增至 WriteMakefile() 參數,強制 Makefile 在你變更包含版本號碼的模組時重新建置。
depend => { '$(FIRST_MAKEFILE)' => '$(VERSION_FROM)' }
有時你會需要在同一個套件中擁有兩個或以上的 XS 檔案。有三個方法:XSMULTI
、獨立目錄,以及從另一個 XS 進行開機。
將你的模組結構化,讓它們全部都位於 lib 底下,例如 Foo::Bar
在 lib/Foo/Bar.pm 和 lib/Foo/Bar.xs 等。讓你的頂層 WriteMakefile
將變數 XSMULTI
設定為 true 值。
呃,就這樣。
將每個 XS 檔案放入獨立目錄中,每個目錄都有自己的 Makefile.PL。請確定每個 Makefile.PL 都有正確的 CFLAGS
、INC
、LIBS
等。你會需要確定頂層 Makefile.PL 使用 DIR
參照每個目錄。
假設我們有一個套件 Cool::Foo
,其中包含 Cool::Foo
和 Cool::Bar
模組,每個模組都有獨立的 XS 檔案。首先,我們使用以下 Makefile.PL
use ExtUtils::MakeMaker;
WriteMakefile(
NAME => 'Cool::Foo',
VERSION_FROM => 'Foo.pm',
OBJECT => q/$(O_FILES)/,
# ... other attrs ...
);
請注意 OBJECT
屬性。MakeMaker 會在 Makefile 中產生以下變數
# Handy lists of source code files:
XS_FILES= Bar.xs \
Foo.xs
C_FILES = Bar.c \
Foo.c
O_FILES = Bar.o \
Foo.o
因此,我們可以使用 O_FILES
變數告訴 MakeMaker 將這些物件用於共用函式庫。
這差不多就是這樣了。現在撰寫 Foo.pm 和 Foo.xs、Bar.pm 和 Bar.xs,其中 Foo.pm 開機共用函式庫,而 Bar.pm 只需載入 Foo.pm。
剩下的唯一問題是如何開機 Bar.xs。這是從 Foo.xs 執行的
MODULE = Cool::Foo PACKAGE = Cool::Foo
BOOT:
# boot the second XS file
boot_Cool__Bar(aTHX_ cv);
如果你有兩個以上的檔案,這是你應該從中開機額外 XS 檔案的地方。
以下四個檔案總結了迄今討論的所有細節。
Foo.pm:
-------
package Cool::Foo;
require DynaLoader;
our @ISA = qw(DynaLoader);
our $VERSION = '0.01';
bootstrap Cool::Foo $VERSION;
1;
Bar.pm:
-------
package Cool::Bar;
use Cool::Foo; # bootstraps Bar.xs
1;
Foo.xs:
-------
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
MODULE = Cool::Foo PACKAGE = Cool::Foo
BOOT:
# boot the second XS file
boot_Cool__Bar(aTHX_ cv);
MODULE = Cool::Foo PACKAGE = Cool::Foo PREFIX = cool_foo_
void
cool_foo_perl_rules()
CODE:
fprintf(stderr, "Cool::Foo says: Perl Rules\n");
Bar.xs:
-------
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
MODULE = Cool::Bar PACKAGE = Cool::Bar PREFIX = cool_bar_
void
cool_bar_perl_rules()
CODE:
fprintf(stderr, "Cool::Bar says: Perl Rules\n");
當然,還有非常基本的測試
t/cool.t:
--------
use Test::More tests => 1;
use Cool::Foo;
use Cool::Bar;
Cool::Foo::perl_rules();
Cool::Bar::perl_rules();
ok 1;
這個提示由 Nick Ing-Simmons 和 Stas Bekman 提供給你。
在 Gtk2::CodeGen 和 Glib::CodeGen 中可以看到實現此目的的另一種方法。
大多數人需要知道的事項(最上層為超類別)。
ExtUtils::MM_Any
|
ExtUtils::MM_Unix
|
ExtUtils::MM_{Current OS}
|
ExtUtils::MakeMaker
|
MY
實際使用的物件屬於 MY 類別,它允許您透過宣告 MY::foo() 方法,在 Makefile.PL 中覆寫 MakeMaker 的位元。
以下是它的實際運作方式
ExtUtils::MM_Any
|
ExtUtils::MM_Unix
|
ExtUtils::Liblist::Kid ExtUtils::MM_{Current OS} (if necessary)
| |
ExtUtils::Liblist ExtUtils::MakeMaker |
| | |
| | |-----------------------
ExtUtils::MM
| |
ExtUtils::MY MM (created by ExtUtils::MM)
| |
MY (created by ExtUtils::MY) |
. |
(mixin) |
. |
PACK### (created each call to ExtUtils::MakeMaker->new)
注意:是的,這很混亂。請參閱 http://archive.develooper.com/makemaker@perl.org/msg00134.html 以了解一些歷史。
注意:當載入 ExtUtils::MM 時,它會根據目前的作業系統,從 ExtUtils::MM_* 模組中為 MM 選擇一個超類別。
注意:ExtUtils::MM_{Current OS} 代表 ExtUtils::MM_* 模組之一,但 ExtUtils::MM_Any 除外,它會根據您的作業系統進行選擇。
注意:MakeMaker 使用的主要物件是 PACK### 物件,*而非* ExtUtils::MakeMaker。它實際上是 MY、ExtUtils::MakeMaker、ExtUtils::Liblist 和 ExtUtils::MM_{Current OS} 的子類別。
注意:MY 中的方法會直接複製到 PACK### 中,而不是讓 MY 成為 PACK### 的超類別。我不記得其理由了。
注意:ExtUtils::Liblist 應該從繼承層級結構中移除,並直接作為函式呼叫。
注意:File::Spec 和 Exporter 等模組已省略,以增加清晰度。
MM_Win95 MM_NW5
\ /
MM_BeOS MM_Cygwin MM_OS2 MM_VMS MM_Win32 MM_DOS MM_UWIN
\ | | | / / /
------------------------------------------------
| |
MM_Unix |
| |
MM_Any
注意:每個直接的 MM_Unix 子類別也是 MM_Any 子類別。這是暫時的權宜之計,因為 MM_Unix 會使用 Unix 特定程式碼覆寫一些 MM_Any 方法。它允許非 Unix 模組看到原始的 MM_Any 實作。
注意:File::Spec 和 Exporter 等模組已省略,以增加清晰度。
如果您有任何問題想新增到常見問答集(無論您是否有答案),請
在 MakeMaker github 儲存庫中提出拉取請求
在 MakeMaker github 儲存庫中提出問題
提交 RT 票證
寄電子郵件至 makemaker@perl.org
makemaker@perl.org 的使用者。