MacPortsのソフトウェアを一括でインストールするスクリプト書いた

先日、mac portsのソフトウェアの依存関係がどうにもおかしくなってしまっていたので、一念発起してまっさらにして入れなおしました。
アンインストールする方は結構簡単にいくんですが、アンインストールした物を再度インストールしなおすのが大変。
mac portsではローカルでコンパイルしてるらしいので、一個一個が結構時間かかるんですね。


どうにも面倒になってきたので、一括でインストールするスクリプト書きました。
とりあえず一回動けばいいので、エラー処理もテストもろくにしてないです。
自分で動かしたときにはうまくいきました。


一応依存関係には配慮して、自動でインストール順を決めるようにはしています。
ただ、ガッツリmac portsの出力依存なので、バージョン変わると動かないかも。
使うときには、インストールしたいportのソフトをvariant付きでひとつのファイルにズラズラ書き出して、それを引数に与えてスクリプトを実行すればOK。

$ port version
Version: 1.8.2
$ cat install_list.txt
gcc43
perl5 +perl5_10
#sqlite3
$ sudo ./port_install.pl install_list.txt

なかでport installを回しているだけなので、相当時間がかかるので、夜中に実行して、朝起きたら終わってる、位の感覚で実行するといいかも。


以下スクリプト本体。

#!/usr/bin/perl

use strict;
use warnings;
use IO::File;


if (scalar(@ARGV) < 1) {
    exit(1);
}

my $filename = $ARGV[0];

my $file = IO::File->new($filename, 'r');
my @not_install = ();
my %variants = ();
while (my $line = $file->getline) {
    chomp $line;
    if ($line ne "" and $line !~ /^\s*#/) {
        my ($cmd, @variant) = split(/\s+/, $line);
        $variants{$cmd} = \@variant;
        push @not_install, $cmd;
    }
}

my %dependencies = ();
for my $cmd (@not_install) {
    my ($name, @deps);
    my $out = `port deps $cmd @{$variants{$cmd}}`;
    for my $l (split(/\n/, $out)) {
        if ($l =~ /Full Name:\s([^ ]*)\s/) {
            $name = $1;
        } elsif ($l =~ /has no Build Dependencies/) {
        } elsif ($l =~ /Build Dependencies:\s+(.*)$/) {
            foreach my $dline (split(/,\s/, $1)) {
                if ($dline ne "" and $dline ne ",") {
                    chomp $dline;
                    push @deps, $dline;
                }
            }
       } elsif ($l =~ /Library Dependencies:\s+(.*)$/) {
            foreach my $dline (split(/,\s/, $1)) {
                chomp $dline;
                if ($dline ne "" and $dline ne ",") {
                    push @deps, $dline;
                }
            }
        }
    }
    $dependencies{$name} = \@deps;
}

my @installed = ();
while (scalar @not_install) {
    my $cmd = shift @not_install;
    my $dep_flag = 0;
    for my $dep (@{$dependencies{$cmd}}) {
        for my $not_install_cmd (@not_install) {
            if ($dep eq $not_install_cmd) {
                $dep_flag= 1;
                last;
            }
        }
        last if $dep_flag;
    }
    if ($dep_flag) {
        push @not_install, $cmd;
    } else {
        push @installed, $cmd;
    }
}

my @install_ok = ();
my @install_ng = ();
for my $cmd (@installed) {
    eval {
        system("port",  "install",  $cmd, @{$variants{$cmd}});
    };
    if ($@) {
        push @install_ng, "$cmd @{$variants{$cmd}}";
        eval {
            system("port",  "clean",  $cmd);
        };
    } else {
        push @install_ok, "$cmd @{$variants{$cmd}}";
    }
}

print "install OK\n" . join("\n", @install_ok) . "\n\n";
print "install NG\n" . join("\n", @install_ng) . "\n\n";