$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

cpanmとModule::AutoInstallとoptional_feature

 今日もcpanmのことについて書きます。

 今日は以下の様な状況が起こりました。

  • verbose付きで手元のcpan mirrorからインストールすると失敗する
    • AUTOINSTALLのoptionや/dev/nullを使って、verboseだけどinteractiveではない特殊なことをしていた
    • PERL_AUTOINSTALL=--defaultdeps cpanm --verbose --mirror-only --mirror file:///$PWD/cpan MIME::Charset < /dev/null
  • 同じようなコマンドをverboseなしで実行するとインストールに成功した
  • ちなみにmirrorはcpanmとorepanで作っていた

 そもそもかなり特殊なことをしているのが良くないという事なんですが、とりあえずいろいろ調べました。調べてみると、Module::AutoInstallとoptional_featureの関係で挙動に変化が起きていました。

Module::AutoInstall

 まずModule::AutoInstallはfeatureという機能を使うことでoptionで入れるモジュールを設定できます。定義は以下のとおり。

include 'Module::AutoInstall';
feature 'Extended mappings by Japanese codepages',
  -default          => 0,
  'Encode::EUCJPASCII' => '0.02';

 この場合、-defaultを0にしているので、PERL_AUTOINSTALL=--defaultdepsをつけた時、インストールしないようにします。

optional_feature

 続いてoptional_featureです。Module::AutoInstallのfeatureを使った際にMYMETA.jsonに何が出力されるかというと、optional_featureという仕様で出力されます。例えば以下の様な感じ。

{
  // ...
    "optional_features" : {
      "ja_extended" : {
         "description" : "Extended mappings by Japanese codepages",
         "prereqs" : {
            "runtime" : {
               "requires" : {
                  "Encode::EUCJPASCII" : "0.02"
               }
            }
         }
      },
      // ...
    }
  // ...
}

 optional_featuresはhttps://metacpan.org/module/CPAN::Meta::Spec#optional_features に書いてある仕様です。

cpanm --verboseでの挙動の変化

 それでModule::AutoInstallとoptional_featureがどのように関係してくるかというと、Module::AutoInstallのfeatureという機能を使うと、cpanmで--verboseを使うか使わないかでデフォルトにインストールするモジュール群が変わります。
 今回の場合、MIME::CharsetはModule::AutoInstallのfeatureでEncode::EUCJPASCIIの依存を書いています。この時、上に書かれたようにMYMETA.jsonにoptional_featuresという形式で出力されています。

 cpanmは--interactive状態にしておくと、MYMETA.jsonに書かれたoptional_featureのrequiresを全てインストールしようとします。しかし--interactive状態でない場合、optinal_featureは無視されます。
 さらにcpanmは--verboseを付けておくと、--interactiveもonになった状態になってしまいます。

(2013/8/12追記)
 コメントで指摘があったので追記します。cpanmは--interactive状態にしておくと、MYMETA.jsonに書かれたoptional_featureのrequiresでpromptを出し、defaultがyの状態になっています。しかし--interactive状態でない場合、optinal_featureは無視されます。これに対して/dev/nullを与えると必ずdefaultが使われてしまいます。
 Module::AutoInstallの場合はPERL_AUTOINSTALL=--defaultdepsをつけると、defaultの依存関係のままインストールします。-defaultが0の場合、そのモジュールはインストールしません。
 結果として、--verboseオプションのあるなしでインストールされるモジュールが異なってしまいます。

 その結果、上のようにMakefile.PLのfeatureを使っていると--verboseをつけた時は、つけていない時よりも多くモジュールが入ってしまいます。この時、--verboseを付けずに作ったcpan mirrorから、--verboseを付けてモジュールをインストールしようとすると、mirrorに無いモジュールをインストールしようとしまい、最初に書いたようにインストールが失敗する状況が起こってしまうわけです。

 ちなみに--verboseをつけた状態でfeatureをインストールしないようにするには以下のように--nointeractiveをつければ良いです。この時nointeractiveをverboseより前に付けてはいけません。

cpanm --verbose --nointeractive ...

(2013/8/12追記)
 このような場合は、--without-all-featuresを使うのが良いそうです。

まとめ

 verboseつけた状態でinteractiveな部分をなくすために、/dev/nullとか、PERL_AUTOINSTALLとか特殊なことをせずに、cpanm --installdeps . したらよい。