モダンPerlの世界へようこそ

第23回Module::Build:MakeMakerの後継者を目指して

今年もよしなにお願いいたします

連載第18回ではlocal::libの話題を取り上げましたが、今回はそのときにもちらと紹介した、モジュールをインストールするときに利用するいくつかのモジュールについて簡単にまとめてみます。

ExtUtils::MakeMakerが生まれるまで

Perlがバージョン3でコンパイル時にユーザ独自のライブラリを組み込んで本体機能を拡張できるようになったとき(1990年⁠⁠、おそらくもっとも喜んだのがデータベースを使っていたユーザでした。彼らはいそいそと自分の使っていたデータベースのライブラリをPerlに組み込み、それとわかる名前をつけて公開しました。当時の記録によれば、Oracleに対応したOraperlやPostgreSQLに対応したPgperlなど、データベース関連だけで8つないし9つの専用Perlがあったようです[1]⁠。

でも、このアプローチには問題もありました。さまざまなライブラリを組み込んだバリエーションが生まれたことでメンテナンスコストは高くなりましたし、データベースライブラリのAPIには互換性がなかったため、特定のデータベースライブラリを組み込んだPerl用に書かれたアプリケーションを別のデータベースライブラリを組み込んだPerl用に移植するのは一苦労でした。

そのため1992年には早くもバズ・モシェッティ(Buzz Moschetti)氏らを中心にデータベースまわりの問題を議論するユーザグループが生まれ、それがやがていまのDBIへと発展していくのですが(DBIはPerl 5のベータ時代から存在していた最古のモジュール群のひとつです⁠⁠、このDBI構想を実現させるためには、Perl本体の外にPerlとCの世界をつなぐインタフェースを用意してデータベースライブラリとリンクさせる必要がありました。そのため、Perl 5ではPerl本体だけでなく外部のライブラリやモジュールも(当時のPerlユーザにとっては)おなじみのmakeを使って別途、再帰的にコンパイルできるようになります。その前処理を行うためにつくられたのがExtUtils::MakeMaker (EUMM)でした。

ExtUtils::MakeMakerの使い方

ExtUtils::MakeMakerを使うと典型的なモジュールのMakefile.PLはたとえばこのように書けます。

use strict;
use warnings;
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME          => 'MyModule::Name',
    AUTHOR        => 'Some Person <person@example.com>',
    VERSION_FROM  => 'lib/MyModule/Name.pm',
    ABSTRACT_FROM => 'lib/MyModule/Name.pm',
    PREREQ_PM     => {
        'Some::Module'  => 0,
        'Other::Module' => 0,
        'Test::Module'  => 0.88,
    },
    ($ExtUtils::MakeMaker::VERSION > 6.30
        ? ( LICENSE => 'perl' )
        : ()
    ),
);

ExtUtils::MakeMakerの問題点

Perl 5のWikiによると、このExtUtilsという名前空間は「外部ユーティリティ」を意味する「Ext(ernal) Util(itie)s」ではなく、Perlソースのext/ディレクトリ以下に配置されている「Ext(ension)用のUtil(itie)s」という意味だそうですが、ExtUtils::MakeMakerは、そのext/ディレクトリに格納されている(Perl 4の時代から利用されてきた)各種DBMやPOSIX層とのつなぎをしただけでなく、いまも例としてあげたDBI/DBDや、日時計算を行うCライブラリ、Tkのような大きなGUIライブラリ群などのつなぎ役としても利用されるようになりました。また、その余録として、一般の、Cとは無関係のPerlモジュールをインストールするツールとしても活用されました(ディストリビューション内のファイルはいずれにしても適切な位置に配置しなければなりませんから、この転用はごく自然なことでした⁠⁠。

ところが、時が過ぎて、Cとは無関係なモジュールが増えてくるにつれ、⁠自分たちはCを使うのがめんどうだからPerlを使っているのに、どうしてわざわざCのツールを使う必要があるのだろう」という問いかけが行われるようになってきました。移植性の低いMakefileや、入っているかどうかもわからないmakeを使わなくても、ファイルの再配置くらいPerlでできるし、Perlを使ったほうが移植性が高まるじゃないか、というわけです。

実際、Unix系のプラットフォームとは異なるシェルを採用しているWindows環境などでは、MakefileのなかにシェルのコマンドではなくExtUtils::Commandを利用したPerlのワンライナーが埋め込まれていました。Windowsというと「またか」という顔をする方もいるでしょうが、移植性の問題を抱えているのはWindows環境だけではありません。ExtUtils::MakeMakerにはほかにもAIX、BeOS、Cygwin、DOS、Darwin(Mac OS X⁠⁠、⁠メンテナンスが放棄された)Mac OS(Classic⁠⁠、NW5、OS2、QNX、U/WIN、VMS、VOSといったプラットフォームのためにMakefileを微調整する仕掛けが用意されています。

このようなバッドノウハウの塊こそがExtUtils::MakeMakerの肝なのですが、そのメンテナンスが大変なことは誰もが認めていました。そのため、21世紀に入るとExtUtils::MakeMakerにかわる新しい代替品を模索する動きが活発化します。その第一弾として登場したのがケン・ウイリアムズ(Ken Williams)氏が2001年にリリースしたModule::Buildでした。

Module::Buildの誕生

「makeいらず」を売りにしていたModule::Buildは、その前年に始まったPerl 6プロジェクトの影響もあってか、ExtUtils::MakeMakerとの互換性は考慮せず、自分が理想的と思う設定の書き方を追求していきました。先ほどの例をModule::Buildで書き直してみると、たとえばこのように書けます。

use strict;
use warnings;
use Module::Build;

my $builder = Module::Build->new(
    module_name => 'MyModule::Name',
    dist_author => 'Some Person <person@example.com>',
    dist_version_from => 'lib/MyModule/Name.pm',
    requires => {
        'Some::Module'  => 0,
        'Other::Module' => 0,
    },
    build_requires => {
        'Test::Module'  => 0.88,
    },
#   license => 'perl',
);

$builder->license('perl') if $Module::Build::VERSION > 0.06;

$builder->create_build_script;

このようにオブジェクトを明示することでモジュールの継承などがしやすくなるほか、ExtUtils::MakeMakerを使ったMakefile.PLでよく見かける三項演算子のかわりに、条件によって値がかわる設定をあとからメソッド経由で変更したりオブジェクトの内部に保存されている値を取り出しやすくなったりする、というのがModule::Buildのひとつの売りでした。

Module::BuildとMETA.yml

また、Module::Buildはメタ情報の設定にも熱心でした。モジュールをインストールする際にMakefile.PLなどを実行するのはやむをえないとしても、たとえばCPANの検索サイトに情報を載せるとき、あるいはCPANモジュールを別のパッケージ管理システムに登録するとき、依存モジュールを知るためにいちいちMakefile.PLなどを実行するのは面倒なだけでなく、セキュリティ的にも問題があります(Makefile.PLのなかにシステムに障害をもたらすコードが紛れ込んでいない保証はどこにもありません⁠⁠。そのため、Module::Buildチームは本体の開発と平行してMETA.ymlという従来Makefileのコメントなどに埋め込んでいたディストリビューションの概要やバージョン、依存関係などを記述するための静的な設定ファイルの設計と導入を進めていきます(YAMLを選択したのはプログラム的な処理のしやすさだけでなく、人間が手書きすることもできるようにという配慮からでした⁠⁠。すべてのメタ情報に対応していたのはModule::Buildだけでしたが、META.ymlについてはその有用性からExtUtils::MakeMakerのほうでも可能な範囲で対応が行われ、CPAN全体に広まっていきました[2]⁠。

理想と現実

もっとも、Module::Build本体のほうは当初期待されたほど順調には浸透できませんでした。理想に燃える人たち、とりわけExtUtils::MakeMakerのメンテナであったマイケル・シュワーン(Michael Schwern)氏自身が2003年にMakeMaker Is DOOMED!という発表などを通じて強く移行をすすめたこともあって、進歩的な層を中心に少しずつ利用者は増えていったものの、一般的なモジュール作者にとってはExtUtils::MakeMakerで十分用事は足りていましたし、Module::Buildが(理由はどうあれ)10年近くも使われ、広く知られていた従来のインストール方法(perl Makefile.PL && make && make test && make install)や各種の設定名を踏襲しなかったことも普及の障害となりました(周辺ツールの作者は同じ処理をさせるのにExtUtils::MakeMaker用のルーチンとModule::Buildのルーチンを別々に書かなければならなくなりましたし、ユーザの立場からすると同じ処理をさせるのに同じような設定を二度行わなければならなくなってしまいました⁠⁠。CPANを利用するうえでもっとも基本的なツールであるCPAN.pmは長らくModule::Buildのサポートを行いませんでしたし、ローカル環境にモジュールをインストールするための仕掛けがうまく動作しない、構成の複雑なCライブラリ群はコンパイルできない、すでにModule::Buildがインストールされている環境でないとModule::Buildをうまくインストールできない、といった実装面での問題も噴出し、2003年頃までの絶賛から一転して、2006年頃までには多くの人がModule::Buildに負の印象を持つようになりました。

Module::Installの台頭

そんなModule::Buildにかわって使われるようになってきたのが、Module::Installでした。その原型は2002年にインギー・ドット・ネット(当時のブライアン・インガーソン)氏がリリースしたCPAN::MakeMakerで、名前からわかるようにこれもExtUtils::MakeMakerの置き換えをねらってつくられたモジュールのひとつですが、こちらはあまり理想主義には走らずに、makeに頼る伝統的な手法は踏襲したうえで、モジュール作者の目に触れる部分をよりモダンな手法で置き換えようとしたのが特徴です。その差はModule::Installを使って書かれたMakefile.PLを見れば明らかでしょう(use strict;やuse warnings;が消えているのは、Mooseなどの例と同じくinc::Module::Installのほうでよしなに計らってくれるためです⁠⁠。

use inc::Module::Install;

name 'MyModule-Name';
all_from 'lib/MyModule/Name.pm';
requires 'Some::Module';
requires 'Other::Module';
build_requires 'Test::Module' => 0.88;
license 'perl';

WriteAll;

ただし、Module::Installは一般のユーザがCPANからインストールして使うことを前提としたモジュールではありません。インストールする必要があるのはモジュールをつくる人だけ。一般のユーザは、モジュールの作者が個々のモジュールに同梱したModule::Installを利用するのがポイントです。この方法はもともと氏がModule::Buildのチームに提案して一蹴されたのですが、必要なモジュールを同梱しておけば、CPANの設定が済んでいなかったりネットワークの環境がなかったりしても同梱したモジュール(と、コアモジュール)を使ってインストールやテストを進められます。auto_bundleなどのコマンドを利用するとModule::Installだけでなくほかの依存モジュールも同梱できるので、アプリケーションをまるごと配布するようなときにも使えます。

Module::Installは一般的なモジュール作者にもわかりやすいメリットが多かったため、ExtUtils::MakeMakerには不満があるけれど(当時問題が多かった)Module::Buildに移行する気にはなれないという層を中心に支持を広げていきます。CatalystJiftyMooseのように、2000年代後半を彩る大きめのフレームワークやアプリケーションがこぞってModule::Installを採用したのも利用に拍車をかけることになりました。

Module::Installに対する批判

もちろんModule::Installにも批判はあります。それぞれのディストリビューションにModule::Installを同梱することは、Module::Installに問題が見つかったとき、本体の修正版が出るのを待たなくても独自のパッチをあてたModule::Installを同梱してリリースしやすくなる、と肯定的に見ることもできますが、実際にはModule::Install本体のバグフィックスが行われるたびに、Module::Installを同梱しているモジュールが何十、何百という単位で無意味に更新されることにつながりますし、そのような更新が行われない場合、逆に古いModule::Installと新しいModule::Installが衝突したり、古いバグあり版が管理されないままいつまでも残ることにもなります(Module::Installにはまだ修正しきれていないバグがいくつも残っています⁠⁠。

プラガブルになっているのはよいものの、Module::Installのコマンドやその説明がどこにあるのかよくわからないという声もよく聞きますし、かつてはもてはやされたauto_installコマンドのように、CPANのツールチェーンをいたずらに混乱させるので使わないように求められている機能もいくつかあります。また、結局外部のmakeツールを必要とすることにもかわりはありません。

結局どれを?

このようにExtUtils::MakeMakerにもModule::BuildにもModule::Installにも、それぞれ一長一短があるのですが、おおざっぱにまとめるとこんなところでしょうか。

  1. この分野における新しい仕様の策定などは常にModule::Buildを前提に行われます。新しいメタ情報をいち早く使っていきたい、という方はModule::Buildが選択肢になるでしょう。ただし、これらの仕様はCPANなどの周辺ツールが採用してはじめて意味を持つものがほとんどです。周辺ツールが採用する頃にはExtUtils::MakeMakerなどのほうでも対応が行われるのがふつうですから、Module::Buildの利用はどちらかというと政治的な色彩のほうが強い、というべきかもしれません。なお、Module::Buildは2006年8月にリリースされたPerl 5.9.4からコアに入っています。また、最近では品質管理チームのデイヴィッド・ゴールデン(David Golden)氏らが積極的にメンテナンスを行っているため、今回紹介した問題の多くは改善されました。氏らは現在新しいメタ情報の制定などにも取り組んでいます。

  2. 今後ExtUtils::MakeMakerに独自の新機能が追加されていくことはありませんが、⁠いまのところ)メンテナンスは放棄されていません。META.ymlのようにCPAN界全体にかかわる新しい仕様が定められたときには必要に応じてゆっくりと追従していますし、XS/CまわりではいまでもExtUtils::MakeMakerにしかない機能があるため、結局ExtUtils::MakeMakerを使い続けている人も少なくありません(Module::BuildプロジェクトからもExtUtils::CBuilderExtUtils::ParseXSといった関連モジュールが生まれましたが、これらはまだExtUtils::MakeMakerのすべてをカバーするところまでは行っていないようです⁠⁠。また、連載第1回でも紹介したように、いまでこそ最新版はPerl 5.6以降にしかインストールできないようになっていますが、ExtUtils::MakeMakerはPerl 5.0のコアモジュールなので、5.6以前のPerlもサポートしたければ事実上ほかに選択肢はないといってもよいでしょう。

  3. Module::Installは本質的にはExtUtils::MakeMakerのラッパにすぎませんが、こちらはプラガブルな構造になっているため、いまでも愛用している人が独自の周辺モジュールを作成しています(有名どころではPODテストのようにモジュール作者だけが実行すればよいテストを指定するModule::Install::AuthorTestsや、Cライブラリの存在チェックを行ってくれるModule::Install::CheckLibなどがありますが、CPANにはほかにも40個ほどの独自コマンドが用意されています⁠⁠。また、Module::InstallはExtUtils::MakeMakerがベースになっていますが、Module::Buildを使ってModule::Install風の記述を可能にするModule::Build::Functionsというモジュールもリリースされました。

これらのモジュールには「すべてのCPANモジュールを正しくインストールできるようにする」という明確なゴールがあるため、本質的な部分ではどれを使っても大差はありません。筆者は小さめなモジュールにはExtUtils::MakeMaker、大きめのアプリケーションを書くときはModule::Install、という具合に使い分けていますが、もちろんほかのやり方もあることでしょう。

ただし、これらのモジュールは単独で使われることはめったにありません。一見便利そうに見える機能も、ほかとのからみを考えると使いづらかったりすることもあります。次回はそのあたりの問題にももう少し触れてみたいと思います。

おすすめ記事

記事・ニュース一覧