Debian Policy Manual (version 3.6.1.1, 2004-06-25) 5.6.6 'Package'
Package names must consist only of lower case letters (`a-z'), digits (`0-9'), plus (`+') and minus (`-') signs, and periods (`.'). They must be at least two characters long and must start with an alphanumeric character.
PDLを使ったPerl数値処理プログラムによりインタラクティブ性が求められるようになってきたので、一部をCで書いて高速化する事を検討。 問題は Linux でも Windows + ActivePerl でもすんなり動くかどうか。
とりあず PDL::PP のサンプルから sumit 関数あたりを MathEx.pd に書いておく。
Foo::Bar パッケージの中の Foo::Bar::Math の一部を Foo::Bar::MathEx に移して、C extension 化したい。 ということで lib/Foo/Bar/MathEx.pd として、Makefile.PL の各種設定をしてみる。
がどうもうまくいかない。 PDL::PP の Makefile.PLサポートは、Makefile.PL と同じ位置に .pd がある事を想定しているようなので、いろいろと小細工をしなければならない。 一方 Perl の XS は Foo::Bar のベース名から Bar.so を作る前提になっているようで、これまたパッケージの中の一部のモジュールをどうもXS化しにくい。
Perl の ext/SDBM_File を真似て、子 Makefile.PL を使ってみることにした。
Foo-Bar-x.xx | +- Makefile.PL | +- lib | | | +- Foo | | | +- Bar.pm | | | +- Bar | | | +- Math.pm | | | +- MathNoEx.pm | | | ... | | +- blib/... | +- MathEx | | | +- Makefile.PL | | | +- MathEx.pd ...
パッケージディレクトリの下に MathEx ディレクトリを作り、そこに Makefile.PL と MathEx.pd を置く。 Makefile.PL は MathEx.pd 専用になるので、PDL::PP の標準的なものでOKになる。
全体のパッケージング・PPM化・インストール等が面倒にならないかと心配したが、Foo-Bar パッケージ化で perl Makefile.PL、make xxx を実行すれば子Makefile.PLまできちんと面倒をみてくれる。 MathEx 以下でビルドしたものもパッケージの blib に一緒に入れてくれるし(=一緒にインストールできる・PPM化できる)。 逆に make dist の際には子Makefileの方は余計なとりまとめはしないで、親Makefileが一括して tar.gz に入れてくれる。 これはよい。 MathEx.pd もきちんと Foo/Bar/MathEx.so になった。
XSが使えない環境のために、PerlとPPの両方で関数を書いておく。 XSが使えれば MathEx を、使えなければ MathNoEx.pm を使うように。 表向きのAPIは Foo::Bar::Math とし、ここで AUTOLOAD を使ってどちらか一方を呼び出すようにする。 間接呼び出しにして遅くなるのはいやなので、シンボルテーブルを直接設定する。
use vars qw($IMPLEMENT_CLASS $AUTOLOAD); BEGIN { $IMPLEMENT_CLASS = 'Foo::Bar::MathEx'; eval "use $IMPLEMENT_CLASS"; if ($@) { warn "Can't load $IMPLEMENT_CLASS: $@"; $IMPLEMENT_CLASS = 'Foo::Bar::MathNoEx'; eval "use $IMPLEMENT_CLASS"; die $@ if $@; } } sub AUTOLOAD { my $name = $AUTOLOAD; $name =~ s/.*://; my $implement = $IMPLEMENT_CLASS . '::' .$name; no strict "refs"; *{$name} = \&{$implement}; # ここでシンボルテーブル設定 return &{$implement}(@_); }
最初は、AUTOLOAD の最後の行で die したら、trap してエラーメッセージ中のパッケージ名(Foo::Bar::MathEx や Foo::Bar::MathNoEx)を呼び出された Foo::Bar::Math に置換して die し直すようにしようかと思ったが面倒なのでやめ。
使っているWindows BOX には Visual C++ 6 が入っているので、XSも問題なくビルドでき PDL extension もうまく動いた。
PPM化までここで済ませば、他のPCにも持っていけるはず。
これでバシバシPPで書けるわけだが、PPがこれまた難解で最初は苦労しそう。
Term::ProgressBar を PREREQ_PM にしているパッケージを make_ppm すると、ppd に Term-ProgressBar ではなく Term-ProgressBar-2.06 が DEPENDENCY として出力される。
CPAN にある最新パッケージが Term-ProgressBar-2.06-r1 と、ちょっと変則的なバージョン番号がついているので誤認しているようだ。 PPM::Make::Util::version あたりでヒューリスティックにパッケージ名とバージョン番号を分離しているっぽい。
とりあえず出力されたPPDを手で直すか。
PPM::Make 0.74 が出た。CPAN::DistnameInfo を使うようになり、Term::ProgressBarのパッケージ名も誤認識しないようになった。
ちょっとしたものをのぞいて、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/); }
以上、スクリプトのテストについての何点かのまとめ。
ここ2回、急用が入ったり都合が悪かったりで聞けなかった 社内 LT に久しぶりに参加。
RubyMotion・Gource・DSP・某コードネームの由来と深いこだわり・OpenStack・MySQL・そして最高技術責任者によるインシデントハンドリングのお話。
特に印象に残ったトークの1つは某コードネーム命名へのこだわりの話。中二病的由来からスタートしつつ、名前空間に現れた時の事を意識して仕上げられた名前だぜ的な。タイプのしやすさは大切だよね。あと愛せるクールな名前かどうか。何年か前に YAPC::Asia Tokyo でパッケージ名の話が上がっていたよねぇ。どのトークだったかなあ。
もう1つはインシデントハンドリングについてのトークで、インシデントが起きたらまず「おちつきましょう」というもの。で(直接の)当事者以外は冷静に支援に回ると。
開発チームで不具合/障害に対応する時は、メインで対応している人は集中して忙しいので、他のメンバが支援に回って迅速なクローズを目指すんだけれど、この時も「おちついて」をもっと意識していけるようにしたいなと思った次第。
Fedora 17 の home 以下に GHC とHaskell Platform をインストールの、ちょっと手を入れる必要があったのでメモ。
GHC で libgmp.so.3 が必要だけれど無いので libgmp.so.10 にシンボリックリンクを作ってごまかす。
mkdir ~/lcoal/lib cd ~/local/lib ln -s /usr/lib64/libgmp.so.10 libgmp.so.3 export LD_LIBRARY_PATH=$HOME/local/lib
cd ~/install wget http://www.haskell.org/ghc/dist/7.6.3/ghc-7.6.3-x86_64-unknown-linux.tar.bz2 tar jxvf ghc-7.6.3-x86_64-unknown-linux.tar.bz2 cd ghc-7.6.3 ./configure --prefix=$HOME/local/ghc-7.6.3 make install ~/local/ghc-7.6.3/bin/ghci # インタプリタ起動してみる。 PATH=$PATH:$HOME/local/ghc-7.6.3/bin
cd ~/install wget http://lambda.haskell.org/platform/download/2013.2.0.0/haskell-platform-2013.2.0.0.tar.gz tar zxvf haskell-platform-2013.2.0.0.tar.gz cd haskell-platform-2013.2.0.0 ./configure --prefix=$HOME/local/haskell-platform-2013.2.0.0
OpenGL 関連のライブラリが入っていないホストで、かつとりあえず必要無さそうので適当に消す(なんで OpenGL いるのー)。
あとこのままだと GHC_PACKAGE_PATH 云々とエラーが出て止まるので script/build.sh をいじる。 以下をコメントアウト。
GHC_PACKAGE_PATH="${ORIG_GHC_PACKAGE_PATH}" \
あとは configure を作り直してビルド。
./autoconf ./configure --prefix=$HOME/local/haskell-platform-2013.2.0.0 make make install PATH=$PATH:$HOME/local/haskell-platform-2013.2.0.0/bin
「Vim で Perl ソースコード上でパッケージ名の上にカーソルがあるときに gf するとそのファイルを開くんだけど、……」という話が出て、あ、それ Emacs でもやりたいと思って調べて設定した。
最初は FFAP の設定。実は今まで知らなかったのだけれど ファイル名などをポイントしている時に Cx C-f するとそのファイル名を guess してくれるl FFAP というのが標準で入ってた。
(ffap-bindings)
で有効になる。
これでファイル名は OK。Perl のパッケージ名からファイル名を guess してもらうには ffap-perl-module.el を使う。
上記から ffap-perl-module.el を取ってきて load-path の通っているところに置く。で以下を設定に追加。
(eval-after-load "ffap" '(require 'ffap-perl-module))
これで Perl ソースコード中のパッケージ名が書かれているところにカーソルがある時に C-x C-f するとそのモジュールファイル名を minibuffer にデフォルトで出してくれるようになる。捗る。
なおデフォルトだと system Perl の @INC にあるものを探しにいくようになっているので、perlbrew 下だったり Carton で入れた local/ 下だったり開発中のモジュールだったりを見つけられるようにするには ffap-perl-module-path を設定しておく必要がある。
(setq ffap-perl-module-path '("/path1/to/lib" "/path2/to/lib"))
Naney (なにい)です。株式会社ミクシィで SNS 事業の部長をしています。
※本サイトの内容は個人的見解であり所属組織とは関係ありません。