2019/12/06

zipperlapp: 実行可能なPerlスクリプトアーカイバ

また妙なものを作っています。

zipperlapp: Make an executable perl script bundle using zip archive

Perlに限らずスクリプト言語で書いたコマンドラインツールは、標準的なインタプリタさえあれば、とりあえずコピーして $PATH に放り込めば動く、というポータブルな実行形式としてのお手軽さも魅力の一つだと思っているのです。
でも、今時のモジュールをちゃんと使ったプログラミングでファイルが複数になると、導入時の配置とか考えることが増えて、だんだんポータブルでもお手軽でもなくなっていくのが不満。
「実行ファイルは /home/???/bin の下に置くと、ライブラリは名前付けて /usr/local/lib/perl の下の全プログラム共有空間に置くか、あるいは /home/???/lib の下に個別に @INC 通すスクリプトを置いて、でもそうすると置き場所で毎回書き換えが...」と。
今時だと Ruby の gem とか Python の egg にして /home の下に入れるオプションでインストールして、... とやるのが正しいかもしれないですけど、それでも C 言語のコンパイル済みバイナリのように、ファイル一発コピーでおしまいとはいかず。

この辺りを解決するのが、このzipperlappの目的です。



前回の suid_sudo みたいなものも含めたセキュリティクリティカルなスクリプトも、導入後のセキュリティ管理で変なライブラリを読み込むミスとかを防ぐためには、できるだけコンパクトに1ファイルに固まっていて欲しい時もあります。前回の suid_sudo に添付したドキュメントにある「単一のファイルに埋め込むときは、埋め込んだ範囲が分かるようにごにょごにょ」とか歯切れの悪い言及は、その辺りの切実な事情が反映されていたりします。

とはいえ、大きなファイルをモジュール切らないで管理するのも、それはそれで邪道でやってられないので、もう20年以上前から何度か Perl で「スクリプトとライブラリモジュールを1ファイルに強引にくっつけるスクリプト」とか書いていたのですが、Python には zipapp なんて便利なものがあるので、「これ、欲しい」と。Pythonの場合、細かいことを除くと、zipの先頭に "#!/usr/bin/python" 付ければ動いてしまうのが気楽さですね。

Perl 5はPython 3ほどOO的整理はされていないものの、use/require でのライブラリ読み込みに介入する Hook もありますし、最近(?)ではメモリ上のデータをファイルとして扱わせる手段も安定しているようです。さらに、ZIPファイルは以前から MS-DOS などで「自己展開ファイル (SFX)」として使われていたので、割と自由に「導入部分」を書けることも知っていたので、組み合わせれば十分実現できるぞ、と。

というわけで、サクッと(?)作ったのがこちらの zipperlapp。名前もそのまま、「Pythonのzipappの Perl版」です。Pythonのように標準組込みの便利な仕組みはないので、ZIPを自分で解釈して、import hook経由でPerlに仕込んで、元のスクリプトに制御を渡す stub loader を実装して、ZIPアーカイブのSFX部分に埋め込んでいます。この手のシステムクリティカルな処理では依存ライブラリを増やしたくないので、ZIPの解釈は自前で簡単にやっています。


できてみたらすごく便利。元のファイルもunzipコマンドで取り出せるし、zipappのデフォルトと同じく無圧縮形式でアーカイブしておけば、そもそも中のモジュールのソースもそのまま見えたりします。むしろ POD とかが透けて見えてしまうのが良いときと悪いときとあるくらい。その辺りはオプションで処理を変えられるようにしてあります。


今時Perlなの、という人は zipapp 使うか、次の投稿で。

0 件のコメント: