perlappとUnicode::Japaneseで起こる問題

ActiveStateの"Perl Dev Kit"に含まれるperlappはPerlスクリプトをexeファイルに変換してくれる優れモノだ。pp(Perl Packager)に比べてexeファイルの起動も速く「さすが売り物」という感じで快適に使えていたのだが、使うモジュールによってはppで起こらない問題に直面することもあり、Unicode::Japaneseでは実行時に以下のような症状が見られた。

C:\hoge>hoge.exe
Tk::Error: could not open file [/Unicode/Japanese.pm] for input : Invalid argument at /Unicode/Japanese.pm line 485.
 Unicode::Japanese::PurePerl::_init_table at /
Unicode/Japanese.pm line 485
 Unicode::Japanese::AUTOLOAD at /Unicode/Japanese.pm line 213
 Unicode::Japanese::new at /Unicode/Japanese.pm line 176
(中略)
 (command bound to event)

Japanese.pmファイルの該当箇所を見るとUnicode::Japaneseは自分自身をファイルとしてopenしにいっており、"/Unicode/Japanese.pm"というパス名が引っかかってエラーになっている。短期的な解決策として以下のように変更してみた。

483:        if( $INC{$file} )
484:        {
485:          $INC{$file} =~ s/\/<.+>/C:\/Perl\/site\/lib\//;
486:          open($FH,$INC{$file}) || CORE::die("could not open file [$INC{$file}] for input : $!");
487:          last OPEN;
488:        }

openするファイルはC:/Perl/site/lib/Unicode/Japanese.pmという名前で決め打ち。これで問題が回避されたように見えるが、生成したexeファイルをPerlの環境がないマシンで動かそうとしたときに"C:/Perl/site/lib/Unicode/Japanese.pm"が存在しないせいで再び引っかかる。exeファイルと一緒にJapanese.pmも配布すればよいのだが、それは美しくない。自分が許せない。

ということで以下のようにした。

483:        if( $INC{$file} )
484:        {
485:          if(defined(&PerlApp::extract_bound_file)){
486:            $INC{$file} =~ s/\/<.+>//g;
487:            $INC{$file} = PerlApp::extract_bound_file($file);
488:          }
489:          open($FH,$INC{$file}) || CORE::die("could not open file [$INC{$file}] for input : $!");
490:          last OPEN;
491:        }

http://aspn.activestate.com/ASPN/docs/PDK/7.0/PerlApp.html によると、PerlApp::extract_bound_fileはコンパイル時に--bindオプションで割り付けられたファイルを展開し一時ディレクトリに書き出してくれる。ここではその一時ファイルをopenの対象としたため、以降のコードは全く書き換えなくてもよい。実行終了後に一時ファイルは消してくれるので、ゴミは残らない。

コンパイル時のオプションの例は以下のようになる。

C:\hoge>perlapp -exe hoge.exe hoge.pl --bind Unicode/Japanese.pm[file=/Perl/Site/lib/Unicode
/Japanese.pm,text]

perlappの作者はユーザがこんな問題に遇うことを見越してPerlApp::extract_bound_fileのような機能を用意したのだろうか? 苦肉の策だが美しい。美しいが苦肉の策だ。