local::lib を切り替える
レガシーなアプリをメンテするのにレガシーな Perl モジュールが必要になることがあり、そのために古いアプリ用には専用の local::lib
ディレクトリを切っているわけですが、普段から日常的に local::lib
を使用しているため shell が上がったタイミングではデフォルトの local::lib
環境変数がセットされていて切り替えが非常に面倒だった。
これまではそれほど頻繁に使用しなかったので放置していたのだが、ここのところ頻繁に必要になるためいい加減うざくなってきて簡単に local::lib
を切り替えられるよう設定をしてみた。
zsh に次のような関数をつくり、それで local::lib
を切り替えるようにする。
function locallib () {
INSTALL_BASE=$1
if [ -d $INSTALL_BASE ]; then
eval $(~/bin/use-locallib $INSTALL_BASE)
fi
}
これは
$ locallib ~/perl5
などのように INSTALL_BASE
を指定して使う。指定されたパスが存在したら use-locallib
というコマンドを使用して環境変数を切り替えるという内容。
この use-locallib
コマンドは以下のようなソースになっていて、古い local::lib
の環境変数をクリアするとともに、新しい local::lib
の環境変数を print
するというものになっている。
#!/usr/bin/env perl
use strict;
use warnings;
use Pod::Usage;
use Config;
use File::Spec;
my $install_base = $ARGV[0]
or pod2usage(-1);
$install_base = File::Spec->rel2abs($install_base);
my $path = $ENV{PATH};
my $perl5lib = $ENV{PERL5LIB};
push @INC, File::Spec->catdir($install_base, 'lib', 'perl5');
require local::lib;
my %env = local::lib->build_environment_vars_for($install_base, 1);
# remove $PERL5LIB set by old local::lib if it exists.
if (my $old_base = $ENV{PERL_MM_OPT}) {
my %mmopt;
for my $opt (split /:+/, $old_base) {
my ($k, $v) = split /=/, $opt;
$mmopt{$k} = $v;
}
if (my $old_installbase = $mmopt{INSTALL_BASE}) {
if ($old_installbase eq $install_base) {
# do nothing if install_base is equal to old one
exit;
}
my @old_perl5lib = (
File::Spec->catdir($old_installbase, 'lib', 'perl5'),
File::Spec->catdir($old_installbase, 'lib', 'perl5', $Config{archname}),
);
$env{PERL5LIB} = do {
my @env;
ENV: for my $e (grep { $_ } split $Config{path_sep}, $env{PERL5LIB}) {
for my $old (@old_perl5lib) {
next ENV if $old eq $e;
}
push @env, $e;
}
join $Config{path_sep}, @env;
};
my $old_path = File::Spec->catdir($old_installbase, 'bin');
$env{PATH} = do {
my @p;
for my $p (grep {$_} split $Config{path_sep}, $env{PATH}) {
next if $p eq $old_path;
push @p, $p;
}
join $Config{path_sep}, @p;
};
}
}
while (my ($k, $v) = each %env) {
print qq[export $k="$v"\n];
}
=head1 NAME
use-locallib - set/switch local::lib environment
=head1 SYNOPSIS
use-locallib (MODULE INSTALL BASE)
これで local::lib
を簡単に切り替えられるようになるけれど、複数の local::lib
環境を同時に使用しているとどのシェルがどの local::lib
を使用しているかわからなくなってしまう。
そのためシェルのプロンプトに INSTALL_BASE
を表示されるようにしてしのいだ。
これは単純に以下のような PERL_MM_OPT
の INSTALL_BASE
をプリントするスクリプトを PROMPT 設定から読んでいるだけである。
#!/usr/bin/env perl
use strict;
use warnings;
my %mm_opt;
for my $opt (split /:+/, $ENV{PERL_MM_OPT} || '') {
my ($k, $v) = split /=/, $opt;
$mm_opt{$k} = $v;
}
my $install_base = $mm_opt{INSTALL_BASE};
if ($ENV{HOME}) {
$install_base =~ s/^$ENV{HOME}/~/;
}
print $install_base || 'none';