@kyanny's blog

My thoughts, my life. Views/opinions are my own.

インストール済みの CPAN モジュールの一覧を取得する良い方法はないだろうか

追記:なーんだ、やっぱりありました。 cpan -a を使えばいいらしい。 id:hiboma ありがとうございました。

何台かのサーバを扱っていると、サーバによってインストール済みのモジュールのバージョンが違ったり、インストールされてなかったりしてよく困ります。デプロイしてからエラーになって気づいたりするので心臓に悪いです。

どうしたものかと考えて、パッケージ名とバージョン番号のリストだけテキストファイルに書き出しておいて、デイリーで更新するようにすれば最低限の世代管理?にはなるかな、と考えました。

先人の成果にあやかろうと CPAN を探したのですが、どうも望み通りのものが見つからない。 id:tomyhero に教えてもらった Pod::ProjectDocs はブラウザで見るにはとても便利ですが、肝心のパッケージ名が JS オブジェクトとして書かれていて、 Perl から扱うのがちょっと面倒くさそう。

結果、うまくないよなあと思いながらも自分でそれっぽいのをでっちあげてしまいました。こんなの誰もが思いつきそうなものだから、きっと探し方次第でいくらでも見つかるはずなんだけどな・・・。

#!/usr/local/bin/perl
use strict;
use File::Find;
use Module::Info;
use Getopt::Long;

my %opt;
GetOptions(\%opt, "help", "is-core", "has-version");

usage() if $opt{help};

my @inc;
push @inc, @INC;

my @cpanlist;

for my $inc (@inc) {
    find(sub {
             my $name = $File::Find::name;
             next unless $name =~ /\.pm$/;
             if ($name =~ m{^$inc/(.*)}) {
                 my $class = $1;
                 while ($class !~ m{^[A-Z]}) {
                     last if $class !~ m{/};
                     $class =~ s{^[^/]+/(.*)}{$1};
                 }
                 $class =~ s{\.pm$}{};
                 $class = join("::", split("/", $class));
                 # print $class, "\n";

                 # get module info
                 my $mod = Module::Info->new_from_module($class);
                 push @cpanlist, $mod;
             }
         }, $inc);
}

for my $mod (@cpanlist) {
    eval {
        if ($opt{"is-core"}) {
            next unless $mod->is_core;
        }
        elsif ($opt{"has-version"}) {
            next unless $mod->version;
        }
        print join q{ }, $mod->name, $mod->version, $mod->is_core ? "(*)" : "", "\n";
    };
}

sub usage {
    print <<USAGE;
Usage: $0 [--help|--is-core|--has-version]
USAGE
    exit;
}

Module::Info の version() メソッドは実際にモジュールをロードせずにバージョン番号を得られるのが利点だそうで、それにあやかって使ってみました。確かにロードしないから実行が早い。最初自分で全部 require するような書き方をしてたらいかにもまずいことをやっているような感じがしていました。 Module:: とか Pod:: とかの名前空間はそこそこ探したのだけど、他にどんなのがありがちなのかな。

@INC 以下をまるごとバージョン管理の配下に置く、なんてアイデアもあるそうで、以前どこかで聞いたことがありますが、さすがにそこまで豪快なのもなあ、と。モジュールそのものの変更差分を見たいわけではないし。ものはためしで svk に全部突っ込んでみたら果たしてどのくらいの早さで動くものなんだろうか。