$shibayu36->blog;

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

「Cartonの内部」について社内勉強会で発表した

Perlのバージョン管理ツールであるCartonの内部について、社内勉強会で発表したので、その時の資料をそのまま公開する。けっこう雑に作ってしまったので、正確性については保証できないけど、Cartonのソースコードリーディングの際の参考になればと思う。

アジェンダ

  • Cartonとは
  • いろいろトラブル
  • carton installの挙動を理解する

Cartonとは

  • Perlのモジュール管理ツール
  • 依存モジュールを特定のバージョンに固定して、インストールしてくれる

Cartonとは(深堀)

  • 一番重要なのはcarton install
  • 基本的にはcpanmのラッパーとしてcarton installが実現
  • Module::CPANfileなど、小さいモジュールを組み合わせている

いろいろトラブルもある

  • cpanfile.snapshotが環境によって変わる
  • carton install --deploymentでインストール出来ないものがあり失敗
  • carton installの挙動を理解すると、大体なんでそういうことが起こるかわかるので紹介

carton installの登場人物

  • cpanm
  • cpanfile
    • 依存モジュールを書くファイル
  • cpanfile.snapshot
    • cartonのバージョン固定用ファイル
  • 02packages.details.txt.gz
    • CPANにおけるインデックスファイル
    • モジュールをインストールするために、どこからダウンロードすればいいか記述
  • install.json MYMETA.json
    • あるモジュールのメタ情報が記述
    • 現在のversion, 依存モジュール
    • cpanmがモジュールインストール後に作る

carton installがやっていること

  • バージョン固定のためのファイルを生成(02packages.details.txt.gz)
  • cpanmでインストール
  • cpanfile.snapshotを生成(install.json, MYMETA.jsonを利用)

バージョン固定のためのファイルを生成

  • cpanfile.snapshotからCPANのインデックスファイルを生成
    • 02packages.details.txt
  • 02packages.details.txtには、モジュール名、version、ダウンロード元が記述
  • cpanmがあるモジュールはどのversionをどこからインストールすればいいか分かる
Apache::LogFormat::Compiler 0.32                   K/KA/KAZEBURO/Apache-LogFormat-Compiler-0.32.tar.gz
Attribute::Params::Validate 1.18                   D/DR/DROLSKY/Params-Validate-1.18.tar.gz
Class::Inspector       1.28                   A/AD/ADAMK/Class-Inspector-1.28.tar.gz
Class::Inspector::Functions 1.28                   A/AD/ADAMK/Class-Inspector-1.28.tar.gz
Class::Singleton       1.5                    S/SH/SHAY/Class-Singleton-1.5.tar.gz
DateTime               1.18                   D/DR/DROLSKY/DateTime-1.18.tar.gz
DateTime::Duration     1.18                   D/DR/DROLSKY/DateTime-1.18.tar.gz
DateTime::Helpers      1.18                   D/DR/DROLSKY/DateTime-1.18.tar.gz
DateTime::Infinite     1.18                   D/DR/DROLSKY/DateTime-1.18.tar.gz
DateTime::Infinite::Future 1.18                   D/DR/DROLSKY/DateTime-1.18.tar.gz
DateTime::Infinite::Past 1.18                   D/DR/DROLSKY/DateTime-1.18.tar.gz

cpanmでインストール

  • cpanfileを使って依存モジュールを知る
  • 02packages.details.txtを使って、どのバージョンをインストールするか知る

発行されるコマンド

  • 以下の様なコマンドが発行される
cpanm -L local --mirror http://cpan.metacpan.org/ --mirror http://backpan.perl.org/ --mirror-index local/cache/modules/02packages.details.txt --cascade-search --save-dists local/cache --cpanfile cpanfile --installdeps .
  • -L local
    • local以下にインストール
  • --mirror http://cpan.metacpan.org/ --mirror http://backpan.perl.org/ --mirror-index local/cache/modules/02packages.details.txt --cascade-search
    • 02packages.details.txtのindexでインストール、そこに存在しなかったらmetacpanやbackpanにfallback
  • --save-dists local/cache
    • モジュールのtarはlocal/cacheに保存する
  • --cpanfile cpanfile
    • cpanfileの指定

cpanm内での挙動

  • cpanfileを見ながら、依存モジュールを走査
  • あるモジュールをインストールする
    • そのモジュールのどのバージョンをインストールするかは02packages.details.txtをみて決める
    • cpanfile.snapshot -> 02packages.details.txtなので、cpanfile.snapshotに記述されたバージョンが入る
    • 02packages.details.txtに存在しない -> 他のmirrorにfallbackされ、インストールされる
  • モジュールの依存がすべて解決されるまで、これを繰り返す

cpanfile.snapshotを生成する

  • 依存モジュールはすべてlocal/以下に入ってる
  • local/以下のinstall.jsonとMYMETA.jsonをすべて取ってきて、cpanfile.snapshot作る
    • local/lib/perl5/(archname)/.meta/ 以下のディレクトリに、それぞれのdistのinstall.jsonとMYMETA.jsonが保存されている
      • 例) local/lib/perl5/darwin-2level/.meta/Algorithm-Diff-1.1902/{install|MYMETA}.json
      • この中にはモジュールのversion情報やさらなる依存情報が入ってる

なぜ問題が起こるか

  • cpanfile.snapshotが環境によって変わる
  • carton install --deploymentでインストール出来ないものがあり失敗

cpanfile.snapshotが環境によって変わる

  • local/以下にインストールされているモジュールをすべて見るため
    • local/以下に手動でモジュール入れたらおかしくなる
    • 一回cpanfileに依存を増やした後、消してもlocalからは消えないので残る
    • rm -rf local/ して再インストールしないとおかしくなる...
  • MYMETA.jsonを参考にするが、動的に依存を変えるモジュールによって、環境によって違うMYMETA.jsonが作成される
    • Macの場合とLinuxの場合で依存モジュールを変えるモジュールは、MYMETA.jsonが環境によって変わる
    • cpanfile.snapshotはそれを参考にするので、変わる

carton install --deploymentでインストール出来ないものがあり失敗

  • carton install --deploymentは02packages.details.txtだけを見て、metacpanにfallbackしないモード
  • 環境によってミスったcpanfile.snapshotが作られていると死ぬ
    • 02packages.details.txtに必要なモジュールが含まれていないため

結論

  • モジュールのインストールはcpanmがやっている
    • うまく02packages.details.txtを作って、cpanmをだますことで、バージョン固定
  • cartonはcpanfile.snapshotからCPANのインデックスを作るのと、cpanfile.snapshotを作るだけ

他のパッケージ管理ツールは?

  • bundler
  • npm
  • etc

bundler

  • 基本はglobalに全部モジュール入れる、bundle exec時にPATHの通し方を工夫する
    • 同じモジュール入っていたら無視できるのでインストール速い
  • Gemfile.lockは毎回Gemfileから依存をたどって作り直す(local/から全部ではない)
  • gemに依存を全部取ってくるAPIがあって、そこから環境依存のものも全部取ってきてるっぽい(?)
    • cpanfile.snapshot, Gemfile.lockには依存が多く記述されるのは問題ないため

carmel

  • bundlerっぽい動き方を目指している
  • 5分くらい使ってみたけどなんかうまく動かなかった

参考資料