名前¶
PAR::Intro - Introduction to Perl Archive Toolkit
PAR::Intro - Perl Archive Toolkit の導入
概要¶
# これはプレゼンテーションであって、モジュールではない。
Note that a more extensive tutorial is now available online as http://aut.dyndns.org/par-tutorial/ and has superceded materials in this introduction.
より発展的なチュートリアルはhttp://aut.dyndns.org/par-tutorial/ からオンラインで利用可能だ。これはこのイントロダクションの素材よりも できの良いものになってきている。
説明¶
PAR (Perl Archive Toolkit) とは何か?¶
Do what JAR (Java Archive) does for Perl
JAR (Java Archive) がやることをPerlで行なう
Platform-independent, compressed file format (zip)
プラットフォームに依存しない、圧縮ファイル形式(zip)
Aggregates modules, scripts and other files into one file
モジュール、スクリプト、その他のファイルを一つのファイルにまとめる
Easy to generate, update and extract
生成、更新、取り出しが容易
Benefits of using PAR:
PARを利用する利点:
Reduced download and deployment time
ダウンロードと展開の時間を短縮
Saves disk space by compression and selective packaging
圧縮と選択的なパッケージ化によりディスクスペースを節約
Version consistency: solves forward-compatibility problems
バージョンによらず一貫性が保たれる:前方互換の問題を解決
Community support:
par@perl.org
コミュニティによるサポート:
par@perl.org
You can also turn a PAR file into a self-contained script
PARファイルを自己完結なスクリプトにすることもできる
Bundles all necessary 3rd-party modules with it
必要な全てのサードパーティモジュールを一緒にバンドル
Requires only core Perl to run on the target machine
必要なのは、当該マシン上で実行されるコアなPerlのみ
If you use
pp
to compile the script...pp
を使ってスクリプトをコンパイルすれば…...you get an executable not even needing core perl
…コアとなるperlさえ必要としない実行ファイルを得る
Getting Started¶
First, generate a PAR file with modules in it:
最初にモジュールの入ったPARファイルを生成する:
% zip foo.par Hello.pm % zip -r foo.par lib/ # grab all modules in lib/
Using modules stored inside a PAR file:
PARファイルの中に格納されたモジュールを使う:
% perl -MPAR=./foo.par -MHello % perl -MPAR=./foo -MHello # the .par part is optional
Or put it in @INC and use it just like a directory:
あるいは@INC内にファイルを置き、ディレクトリのように利用する:
% perl -MPAR -Ifoo.par -MHello % perl -MPAR -Ifoo -MHello # 同上
コマンドラインツール¶
Use
pp
to scan scripts and store dependencies as a PAR file:pp
を使ってスクリプトを走査し、PARファイルの依存関係にあるものを格納する:% pp -p source.pl # makes 'source.par' % pp -B -p source.pl # bundles core modules too
Use
par.pl
to run files from a Perl Archive:par.pl
を使ってPerlアーカイブからファイルを実行する:% par.pl foo.par # looks for 'main.pl' by default % par.pl foo.par test.pl # runs script/test.pl in foo.par
Use
parl
orparl.exe
to run files from a Perl Archive:parl
あるいはparl.exe
を使ってPerlアーカイブからファイルを実行する:% parl foo.par % parl foo.par test.pl
バイナリ実行ファイルの作成 Making Binary Executables¶
The
pp
utility can also generate binary executables:pp
ユーティリティはバイナリ形式の実効ファイルも生成できる:% pp -o packed.exe source.pl # self-contained .exe % packed.exe # runs anywhere with the same OS
You can also bundle additional modules:
追加のモジュールをバンドルすることもできる:
# packs CGI + its dependencies, too % pp -o packed.exe -M CGI source.pl
Or pack one-liners:
あるいは一行スクリプトのパック:
# turns one-liner into executable % pp -o packed.exe -e 'print "Hi!"'
Some notes:
諸注意:
The command-line options of
pp
are almost identical toperlcc
'spp
のコマンドラインオプションはperlcc
のものとほぼ同じであるModules are read directly from the PAR file, not extracted
モジュールはPARファイルから直接読み込まれるのであって、展開されるのではない
Shared object files (aka dll) are extracted with File::Temp
共有オブジェクトファイル(例えばdll)はFile::Tempを使って展開される
Tested on Win32, FreeBSD, Linux, AIX, Solaris, Darwin and Cygwin.
Win32, FreeBSD, Linux, AIX, Solaris, Darwin そして Cygwinでテストされた
PARファイル解剖¶
Modules can reside in different directories in a PAR file:
モジュールはPARファイル内において様々なディレクトリに属する:
/lib/ # standard location /arch/ # for creating from blib/ /i386-freebsd/ # i.e. $Config{archname} /5.8.0/ # i.e. Perl version number /5.8.0/i386-freebsd/ # combination of the two above / # casual packaging only
Scripts are stored in one of the two locations:
スクリプトは次の二つのロケーションのうちの一つに格納される:
/script/ # standard location / # casual packaging only
Shared libraries may be architecture- or perl-version-specific:
共有ライブラリはarchitecture-かperl-version-specificに:
/shlib/(5.8.0/)?(i386-freebsd/)?
PAR files may recursively contain other PAR files:
PARファイルは他のPARファイルを再帰的に含む:
/par/(5.8.0/)?(i386-freebsd/)?
Special files:
特殊なファイル:
/MANIFEST # index of the PAR's contents /SIGNATURE # digital signature(s) /META.yml # dependency, license info, etc. /Build.PL # self-contained installer
Programs can use
PAR::read_file($filename)
to read file contents inside PARプログラムは
PAR::read_file($filename)
を使ってPAR内の ファイルコンテンツを読むことが出来るPrograms can use
PAR::reload_libs()
to reload modules within changed PARsプログラムは
PAR::reload_libs()
を使って、変更されたPAR内のモジュールを 再読み込みすることができる。
派生モジュール¶
Apache::PAR
Nathan Byrd's attempt to make self-contained Perl Handlers
Nathan Byrdによる自己完結したPerlハンドラ作成の試み
Same as the WAR files for Java Servlets
Java ServletにおけるWARファイルと同じもの
Includes PerlRun and Registry handlers
PerlRunとRegistryハンドラを含む
App::Packer::Backend::PAR
Support module of Mattia Barbon's App::Packer suite
Mattia BarbonのApp::Packerセットのサポートモジュール
Makes it easy to pick-and-choose dependency scanners and packers
依存関係のスキャナとパッカーを選びやすくする
Fine-tuned distribution and packaging controls
よくチューニングされた配布物とパッケージ制御
CPANPLUS::Dist::PAR
Cross-platform PPM: Auto-generate PAR out of CPAN distributions
クロスプラットフォームPPM:CPANディストリビューションから自動生成されたPAR
Use the bundled Build.PL to install PAR modules into system
バンドルされたBuild.PLを使ってシステムにPARモジュールをインストールする
Apache::PARのデモ¶
In
httpd.conf
:httpd.conf
:<VirtualHost *> <IfDefine MODPERL2> PerlModule Apache::ServerUtil </IfDefine> PerlModule Apache::PAR PARDir /opt/myapp PARFile /opt/myapp/myapp.par </VirtualHost>
In
web.conf
insidemyapp.par
:myapp.par
内のweb.conf
:Alias /myapp/static/ ##PARFILE##/ <Location /myapp/static> SetHandler perl-script PerlHandler Apache::PAR::Static PerlAddVar PARStaticDirectoryIndex index.html PerlSetVar PARStaticDefaultMIME text/html </Location> Alias /myapp/cgi-perl/ ##PARFILE##/ <Location /myapp/cgi-perl> Options +ExecCGI SetHandler perl-script PerlHandler Apache::PAR::Registry </Location>
今後の開発¶
Polish
pp
's featurespp
の機能に磨きをかけるHandles corner dependency cases for LWP, Tk, DBI...
LWP, Tk, DBI...に対するcorner dependencyなケースの処理
Optional encryption support (but *not* obscuring)
暗号化オプションのサポート(オブスキュアリングではない)
Become a worthy competitor to PerlApp and Perl2Exe
PerlAppとPerl2Exeに対抗できるぐらいにする
Learning from JAR
JARから学ぶ
Making par.pl's command line interface in sync with jar's
par.plのコマンドラインインターフェースをjarのものと同期させる
Digital signatures for PAR packages using Module::Signature
Module::Signatureを使ってPARパッケージ用のデジタル署名
File layout compatibility?
ファイルレイアウトの互換性?
Learning from FreeBSD Bento
FreeBSDのBentoから学ぶ
Smoke test and make PAR automatically for each CPAN upload
CPAN uploadのための自動的なスモークテストとPARの作成
Provide binary packages for users without a compiler
コンパイラを持っていないユーザーのためにバイナリファイルの提供
PAR.pmの実装に関する概観¶
Here begins the scary part
ここより危険地帯
Grues, Dragons and Jabberwocks abound...
グルー、ドラゴン、ジャバウォックがいっぱい…
You are going to learn unpleasant things about Perl internals
あなたはPerlの内部に関する喜ばしくないものを学ぼうとしている
Go home now if you have heart condition or digest problems
心臓に持病があるか胃の調子が悪いなら今すぐお帰りなさい
PAR invokes five areas of Perl arcana:
PARはPerl奥義の5つの領域を召還する
@INC code references
@INCコードリファレンス
On-the-fly source filtering
オンザフライなソースフィルタリング
Faking <DATA> filehandle with PerlIO::scalar and IO::Scalar
PerlIO::scalarとIO::Scalarを使って<DATA>ファイルハンドリングをだます
Overriding DynaLoader::bootstrap to handle XS modules
DynaLoader::bootstrapをオーバーライドしてXSモジュールを操作
Making self-bootstrapping binary executables
自己ブートストラップ型バイナリ実行ファイルの生成
The first two only works on 5.6 or later
最初の二つは5.6以降でのみ動作する
PerlIO::scalar is 5.8-specific; IO::scalar only needs 5.005
PerlIO::scalaは5.8の仕様;IO::scalaは5.005を必要とするだけ
DynaLoader and %INC are there since Perl 5 was born
DynaLoader と %INC は Perl 5 以降存在している
PAR currently needs 5.6, but a 5.005 port is possible
PARは現在5.6が必要だが、5.005ポートは可能
@INC内のコードリファレンス¶
On 1999-07-19, Ken Fox submitted a patch to P5P
1999-07-19 に Ken Fox がP5Pにパッチを提起した
To "enable using remote modules" by putting hooks in @INC
@INCにフックを置くことでリモートにあるモジュールを利用できるようにする
It's accepted to come in Perl 5.6, but only get documented by 5.8
Perl 5.6で受け入れられたが、5.8になってからドキュメント化された
Type 'perldoc -f require' to read the nitty-gritty details
'perldoc -f require'とタイプすれば詳細を読むことができる
Code references in @INC may return a filehandle, or undef to 'pass':
@INC内のコードリファレンスは'pass'に対してファイルハンドルかundefを返す:
push @INC, \&my_sub; sub my_sub { my ($coderef, $filename) = @_; # $coderef は \&my_sub open my $fh, "wget http://example.com/$filename |"; return $fh; # リモートホストのモジュールを使う、本当に! }
Perl 5.8 let you open a file handle to a string, so we just use that:
Perl 5.8では、文字列へのファイルファンドルをオープンできる。 よって次のように:
open my $fh, '<', \($zip->memberNamed($filename)->contents); return $fh;
But Perl 5.6 does not have that, and I don't want to use temp files...
しかしPerl 5.6ではできない。一時ファイルは使いたくないし…
Filter::* モジュールを使わないソースフィルタリング¶
... Undocumented features to the rescue!
…救済のための機能がドキュメント化されていない!
It turns out that @INC hooks can return *two* values
結局@INCのフックは*2つ*の値を返すことがわかる
The first is still the file handle
一番目はファイルファンドル
The second is a code reference for line-by-line source filtering!
二番目は一行毎のソースフィルタリングのためのコードリファレンス!
This is how
Acme::use::strict::with::pride
works:これは
Acme::use::strict::with::pride
の動作の仕方:# 全てのモジュールにstrictとwarningsを使わせる open my $fh, "<", $filename or return; my @lines = ("use strict; use warnings;\n", "#line 1 \"$full\"\n"); return ($fh, sub { return 0 unless @lines; push @lines, $_; $_ = shift @lines; return length $_; });
But we don't really have a filehandle for anything
しかし我々は何らかのものに対するファイルハンドルを本当に持っていない
Another undocumented feature to the rescue
もう一つのドキュメント化されていない救済機能
We can actually omit the first return value altogether:
我々は一番目の戻り値を全て捨てることが実際可能だ:
# PAR内のファイルから一行毎に内容を全て返す my @lines = split /(?<=\n)/, $zip->memberNamed($filename)->contents; return (sub { $_ = shift(@lines); return length $_ });
<DATA>ハンドルをだます¶
The @INC filter stops when it sees
__END__
or__DATA__
@INCフィルタは
__END__
か__DATA__
を見つけるとストップするAll contents below are lost
そこから下の内容は全て失われる
Breaks modules that read from the <DATA> filehandle
<DATA>ファイルハンドルから読み込むモジュールを壊してしまう
The same problem appears when we
eval
the main.pl script同様の問題がmain.plスクリプトを
eval
するときに現れる
Therefore, we insert a line before the final token to fake *DATA
それゆえ、最後のトークンの前に一行挿入して*DATAをごまかす
It has to be the final line to belong to the correct package
正しいパッケージに属する最終行でなければならない
It has to happen in compile time but not inside a BEGIN block
コンパイル時に発生しなければならないが、BEGINブロックの中でではない
Here is what I came up with (but no longer needed in recent versions):
私が考え出しのはこのような方法だ(だがもはや最近のバージョンでは必要ない):
$DATACache{$file} = $1 if ($program =~ s/^__DATA__\n?(.*)//ms); if (eval {require PerlIO::scalar; 1}) { "use PerlIO::scalar". " ( open(*DATA, '<:scalar', \\\$PAR::DATACache{'$key'}) ? () : () )"; } elsif (eval {require IO::Scalar; 1}) { # This will first load IO::Scalar, *then* tie the handles. "use IO::Scalar". " ( tie(*DATA, 'IO::Scalar', \\\$PAR::DATACache{'$key'}) ? () : () )"; } else { # only dies when it's used "use PAR (tie(*DATA, 'PAR::_data') ? () : ())\n"; } sub PAR::_data::TIEHANDLE { return bless({}, shift) } sub PAR::_data::AUTOLOAD { die "Please install IO::Scalar first!\n" }
DynaLoader::bootstrapのオーバーライド¶
XS modules have dynamically loaded libraries (
.so
or.dll
)XSモジュールは動的にライブラリ(
.so
や.dll
)をロードするThey cannot be loaded as part of a zip file, so we extract them out
それらはzipファイルの一部としてロードされない。だから展開すことになる
But I don't want to make any temporary
auto/
directoriesだが、一時的な
auto/
ディレクトリはつくりたくないSo we have to intercept DynaLoader's library-finding process
そこでDynaLoaderのライブラリ探査処理を横取りしなければならない
Module names are passed to
bootstrap
for XS loadingモジュール名はXSローディングのために
bootstrap
に渡されるDuring the process, it calls
dl_findfile
to locate the fileこの処理の間に、ファイルの位置を把握するため
dl_findfile
を呼び出すSo we wrap around both functions:
そのために両関数をラップする:
no strict 'refs'; no warnings 'redefine'; $bootstrap = \&DynaLoader::bootstrap; $dl_findfile = \&DynaLoader::dl_findfile; *{'DynaLoader::bootstrap'} = \&_bootstrap; *{'DynaLoader::dl_findfile'} = \&_dl_findfile;
Our
_bootstrap
just checks if the library is in PARs我々の
_bootstrap
は、PARの中にライブラリがあるかどうかチェックを行なうIf yes, extract it to a File::Temp temp file
もしあるなら、File::Tempの一時ファイルに展開される
The file will be automatically cleaned up when the program ends
プログラムが終わると、ファイルは自動的にクリンナップされる
It then pass the arguments to the original
$bootstrap
それから引数は元の
$bootstrap
に渡されるFinally, our
_dl_findfile
intercepts known filenames and return it最後に、我々の
_dl_findfile
は知ったファイル名を補足し、それを返す
自己内包型PAR実行ファイルの解剖¶
The par script ($0) itself
parスクリプト($0)自身は
May be in plain-text (par.pl)
プレインテキスト(par.pl)
Or native executable format (par or par.exe)
あるいは実行可能形式(parやpar.exe)である
Any number of embedded files
任意の数のファイルを埋め込める
Typically used for bootstrapping PAR's various XS dependencies
典型的にはPARの様々なXSの依存関係物をブートストラップするために利用される
Each section begins with the magic string "FILE"
各セクションはマジック文字列"FILE"で始まる
Length of filename in pack('N') format and the filename (auto/.../)
pack(N)形式のファイル名の長さとファイル名(auto/.../)
File length in pack('N') and the file's content(not compressed)
pack(N)されたファイルサイズとファイルの内容(圧縮されない)
One PAR file
一つのPARファイル
This is just a zip file as usual
これは単に普通のzipファイルだ
Beginning with the magic string
"PK\003\004"
マジック文字列
"PK\003\004"
で始まる
Ending section
終了セクション
A pack('N') number of the total length of FILE and PAR sections
ファイルとPARセクション全体のサイズをpack('N')した数
Finally, there must be a 8-bytes magic string:
"\012PAR.pm\012"
最後に、8バイトのマジック文字列がなければならない:
"\012PAR.pm\012"
Self-Bootstrappingのトリック¶
All we can expect is a working perl interpreter
期待することはperlインタプリタとして動作することだ
The self-contained script *must not* use any modules at all
自己完結的スクリプトはどんなモジュールも使っては*ならない*
Not even strict.pm or DynaLoader.pm
strict.pmやDynaLoader.pmでさえもだめだ
But to process PAR files, we need XS modules like Compress::Zlib
しかしPARファイルを処理するためにはCompress::ZlibのようなXSモジュールが必要だ
A chicken-egg problem
鶏が先か卵が先か
Solution: bundle all module and object files needed by PAR.pm
解決方法:PAR.pmによって必要とされる 全てのモジュールとオブジェクトファイルをバンドルする
That's what the
FILE
section in the previous slide is for先のスライドにおける
FILE
セクションはこのためのものであったLoad modules to memory, and write object files to disk
メモリにモジュールをロードし、オブジェクトファイルをディスクに書き込む
Then use a local @INC hook to load them on demand
そして必要なときにローカルな@INCフックを使用してそれらをロードする
We want to minimize the amount of temporary files
一時ファイルの量は最小化したいものだ
First, try getting PerlIO::scalar loaded
まず最初に、PerlIO::scalarのロードを試してみる
So everything else can be in-memory
そうすれば全部メモリ内における
Next, try getting File::Temp loaded for better
tempfile()
次に、
tempfile()
よりも良いFile::Tempをロードするのを試みるSet up an END hook to unlink all temp files up to this point
ENDフックをセットアップして、この時点までの一時ファイルを全てunlinkする
Load all other bundled files
その他の全てのバンドルされているファイルをロードする
Finally we are able to look in the compressed PAR section
最終的に、圧縮されたPARセクションを見ることができる
This can be so much easier if we have a pure-perl
inflate()
もしピュアPerlの
inflate()
を持っているなら、もっと簡単にできるPatches welcome!
パッチ大歓迎!
SEE ALSO¶
http://www.autrijus.org/par-tutorial/
http://www.autrijus.org/par-intro/ (English version)
http://www.autrijus.org/par-intro.zh/ (Chinese version)
ex::lib::zip, Acme::use::strict::with::pride
App::Packer, Apache::PAR, CPANPLUS, Module::Install
作者¶
Autrijus Tang <autrijus@autrijus.org>
http://par.perl.org/ is the official PAR website. You can write to the mailing list at <par@perl.org>, or send an empty mail to <par-subscribe@perl.org> to participate in the discussion.
Please submit bug reports to <bug-par@rt.cpan.org>.
著作権¶
Copyright 2002, 2003 by Autrijus Tang <autrijus@autrijus.org>.
This document is free documentation; you can redistribute it and/or modify it under the same terms as Perl itself.