昨日 Linux 上で実験してみた PAR を Windows にも入れてみる。 PDLを使っている関係で ActivePerl は 5.6.1。
ppm install で入るパッケージは古いので、最新のものを入れておく。 ちょっと手間。
ワンライナーを pp できるところまで動作確認。
ExtUtils::Makemakerを使ってパッケージ化している開発中のモジュール(スクリプトあり、XS あり)を実行可能形式化してみる。
perl Makefile.PL nmake nmake test cd blib set PERL5LIB=lib;arch (pp の -I オプションが効かなかったので) pp -o foo.exe -a lib -a arch -M ... -c script/foo
Log::Log4perl::Appender::Screen、Jcode::Unicode::NoXS、Unicode::String、GD については依存関係を自動検出できなかったので、それぞれ -M で指定。
できた。動いた。素晴しい。 PDL や GD を使っていたのでちょっと不安だったのだがうまく動いて感激。 これでCD-ROMとか USB メモリに入れておいて一発実行とかできる。
PerlのConfiguration関連のモジュールの再チェック。 Template Toolkit で使っている AppConfig が良さそげ。 設定ファイルからの設定と、コマンドラインからのオプション指定を同じ設定オブジェクトに書き込むようになっているのが便利。Getopt::Long で取得したオプションの格納先として使うだけでも便利そうだ。
設定ファイルからの再帰的設定ファイルインクルードや、コマンドラインオプションで指定した設定ファイル中の設定を「その位置」で行う機能が標準であれば嬉しいのだがさすがに無いか。 action あたりを書けば実現は可能そう。
ちょっとしたものをのぞいて、Perl プログラムはアプリケーション部分も App 的 Perl モジュール(.pm)に入れて、実行するスクリプトファイル (.pl) では use して new して run するだけにしている。
#!/usr/bin/perl use warnings; use strict; use MyApp; exit MyApp->new->run;
@ARGV の処理は new から呼ばれているプライベートメソッドの中で local @ARGV してから、Getopt::Long::GetOptions あたりで解析処理をしている。
でテストスクリプトではこの MyApp を use_ok して local @ARGV = qw(引数組み合わせパターン...) した後に new を呼んだ結果を検査するようにしていた。
そんなところ今回「.pm の中で @ARGV いじるのやっぱり気持ち悪くない?」っていう意見をもらった。気持ちはわかる。ただ
MyApp->new(@ARGV)->run;
のように .pl 側で受け取ったものを解析すれば?」いいかというと、直接 @ARGV を使わない Getopt::Long::GetOptionsFromArray は 2.36 からしかなくて、古いバージョン の Getopt::Long でも動くようにすると結局 local @ARGV になってしまうのである。
また「@ARGV の解析は .pm の方には入れたくない」っていう意見もあったので、今回はそれらのコードは .pl 側に追い出すことにした。
そうすると次の課題は .pl にある @ARGV の解析処理のテストはどう書けばいいかなと。.pl ファイルを require したらスクリプトが走っちゃうし。試行錯誤していたらマスタリング Perl で紹介されている caller(0) を使う方法を教えてもらった。
スクリプトファイル(.pl) #!/usr/bin/perl package App; # スクリプトの実行開始エントリ sub main { # ここに実行したい処理呼び出しを書く。 } main() unless caller(0);
こうするとこのスクリプトファイルを直接実行した場合は main サブルーチンが実行され、他から require された場合は main は実行されないというようにすることができる。前者の場合は caller(0) は undef を返し、後者は 'main' (あるいその他のパッケージ名)が返されることを利用している。
これでテストファイルの中でこのファイルを require_ok できるようになるので、あとは各サブルーチンのテストを書けばよい。
それから Pod::Usage を使っているんだけれど、テストでは Pod::Usage::pod2usage で exit されてしまうと困るので exit が何もしないようにしておく。テストスクリプトでは以下のようにする。
BEGIN { *CORE::GLOBAL::exit = sub {}; require_ok('script/myscript.pl'); }
これで myscript.pl のテスト中に pod2usage が exit を呼んでもスルーさせられる。 ただし本番では exit させたい pod2usage がある場合は、その後に制御が流れていっても問題がおきないようにプログラムを書いておく必要があるので注意。
それから pod2usage で期待するメッセージが STDERR に吐かれているかをテストするには STDERR をオープンしなおせばよい。
{ my $message; local *STDERR; open STDERR, '>', \$message or die $!; # ここで pod2usage がメッセージを出すことが期待されるテストを実行。 like($message, qr/expected message/); }
以上、スクリプトのテストについての何点かのまとめ。
Naney (なにい) です。株式会社MIXIで SNS 事業の部長をしています。
※本サイトの内容は個人的見解であり所属組織とは関係ありません。