MozillaブラウザのJavaScriptエンジンであるSpiderMonkeyを他のプロジェクトでもサクッと使えるように、GNU Autoconf, Automake, Libtoolでビルドできるように修正するお話。
SpiderMonkeyとは何ぞや
SpiderMonkeyはC言語で実装されてたJavaScriptエンジンで、MozillaブラウザのJavaScriptまわりの実装として利用されています。単体でも利用可能なライブラリなのでMozilla以外のWebブラウザや、Webサーバ側での動的なコンテンツ生成手段としてや、その他Web以外のアプリケーションにおける埋め込みのスクリプトエンジン(Netscape Enterprise ServerのLiveWireとか懐かしいですね)として利用されています。同エンジンの利用方法や利用事例は、MozillaプロジェクトのJavaScriptプロジェクト内のドキュメントを参照してください。また、Javaによる実装であるRhinoも利用できます。つまりPerlやRubyやPythonのエンジンを特定のプログラムに組み込んで(embedして)利用できるように、SpiderMonkeyエンジンを組み込めばあなたのプログラムはJavaScriptを解釈することができるようになります。
ちょっと困ったところ1
で、この素敵なスクリプトエンジンライブラリであるSpiderMonkeyなんですが、ビルドシステムが微妙に使いにくいのが困り者です。別にスゴく複雑な判定や処理を行っているわけではないのですが、プラットフォーム名決めウチ式ビルドシステムなため、例えばCVSからcheckoutした後FreeBSD上でビルドしようとしても、サクッとビルドできない等の問題が有ったりします。いちおうFreeBSD用にportsのパッケージが有るみたいですが、Linuxと同じルールでビルドしている模様。最新のものはしりませんがlocaltime_r関数とか有りましたっけ?できればプラットフォーム名で大括りに判定するのではなくて「○×関数は使えるか否か」と細かく判定してもらえれば、移植性やビルドが容易になるはずなのですが、この点がちょっと不満です。
ちょっと困ったところ2
また、ビルドにはLibtoolを使用しているのですが、システムにインストールされているlibtoolコマンドを使おうとしている点が個人的にとっても不満です。Mac OS Xの場合/usr/bin/libtoolにはAppleが用意した独自のlibtoolコマンドが設置されています(通常こちらが利用されます)。しかしながら僕は独自のApache 2.x向けモジュールのビルドや、その多諸々で普通の新しい目のGNU libtoolを使いたいので、それを/usr/local/bin/libtoolにインストールして優先的に使っています。これらはまったく別物で、コマンドライン引数も違います。つまりビルドシステムが期待どおり動いてくれない結果になります。別に僕が環境をそのように作っているから引っかかっているだけといえばそのとおりなんですが、配布するパッケージでlibtoolを使用する場合、ホストにlibtoolのインストールを要求したりインスト−ル済みのlibtoolを期待すると、こーいったカスタムlibtoolスクリプトを踏んでしまい困ったことが起こります。(断言はしませんが)普通は配布パッケージにltmain.shスクリプト等を添付して、独自のlibtoolスクリプトを生成し利用するのがトラブルを減らすキモだと思っています。これによってLibtoolの目的である
共有ライブラリ等のビルド作業におけるプラットフォーム間の差異を吸収し、開発者に統一されたコマンドラインインターフェイスを提供するがきちんと達成されると思います。Autoconf, Automakeと組み合わせればもっと簡単になりますし。もちろんこのソースコードとビルドシステムは(たしか)Mozillaをビルドする為の物であって、単体のライブラリとして配布する為に作られているわけではないので突っ込むところでもないような気もします。
ってまぁMac OS Xの出たばかりの頃には、これらlibtool生成時にプラットフォームの判定に利用するconfig.guessスクリプトが古くてプラットフォームを判定できない(Darwin?なにそれー)ってな問題がよくありましたけどね。
configureが失敗したら、とりあえず/usr/share/libtool/config.guess等をコピーってのはマカーの一般教養だったのが懐かしいです。ってゆうか未だにそーいう「いつまで古いlibtool使ってやがんだよ うらー」な困ったプロジェクトに当たることも稀に有りますけど。きっと大人の事情があるんでしょう。
と、前置きがやたらめったら長くなってアレですが、SpiderMonkeyのビルドをGNU Autoconf, Automake, Libtoolでザクッとできるように弄ってみた時のメモ。
Autotools化の手順
SpiderMonkey自体は複雑なビルドルールを持っているわけではないので、この手順自体は普通のプロジェクトでGNU Autoconf, Automake, Libtoolでビルドシステムを組む場合と大差ないと思います。今回は- GNU Autoconf 2.57
- GNU Automake 1.6.3
- GNU libtool 1.5.10
手順1 ディレクトリレイアウトの確認
mozillaプロジェクトのCVSからcheckoutするなりtar ballをダウンロード(今回はjs-1.5-rc6a.tar.gzを使用)すると、下記のようなレイアウトでファイルが展開されます。$ ls -F CVS/ README jsd/ src/CVSディレクトリとデバッガ周りのjsdディレクトリは無視して、今回はsrcディレクトリ配下のソースコードをだけをビルドすることにします。srcディレクトリ以下にはソースコード(*.c)とヘッダファイル(*.h)や、ビルド用のファイルがいくつか転がっています。また更に下には互換性維持用のライブラリや、JavaとJavaScriptを結びつけるLiveConnect関連のディレクトリや、PerlとJavaScriptを結びつけるPerlConnect関連のディレクトリ等が含まれています。今回はJavaScriptのライブラリだけが欲しいので、これらは無視することにします。
Autotools化にあたり、最終的に次のようなディレクトリレイアウトをとることにします。
./config/ - ビルドシステム用ファイル ./src/ - ソースコード(checkoutしたものと同じ)次のステップに進む前に、ビルドシステム用ファイルを設置するディレクトリを予め作成しておきます。
$ mkdir config $ ls -F CVS/ README config/ jsd/ src/
手順2 configure.inファイル作成
configureスクリプトのルールを決めるため、configure.inファイルを作成します。細かいチェックは今のところ置いておいて、下記のような内容で作成します。AC_INIT(SpiderMonkey, 1.5-rc6a, oyama@module.jp) AC_CONFIG_AUX_DIR(config) AM_INIT_AUTOMAKE(SpiderMonkey, 1.5-rc6a) AM_CONFIG_HEADER([src/config.h]) AC_PROG_CC AC_PROG_LIBTOOL AC_EXEEXT AC_OUTPUT(Makefile src/Makefile)AC_INIT等で指定しているパッケージ名は"SpiderMonkey"ではなく"js"等の方が良いのかもしれません。でもジェネリックすぎて良くない。あとメンテナのメールアドレスはとりあえず自分のアドレスでも書いておきましょう(いいのか)。
手順3 Makefile.amファイル作成
Makefileのひな形であるMakefile.inを生成するため、Makefile.amファイルを作成します。今回はsrcディレクトリ以下にソースコードが有るので、パッケージのトップレベルと、srcディレクトリの2カ所に作成します。トップレベルのMakefile.amは下記のようにサブディレクトリの名前を指定するだけの内容:SUBDIRS = srcそれこそ
$ echo 'SUBDIRS = src' > Makefile.amとかで作ってもおっけいです。
配下のsrcディレクトリ内には次のような内容でsrc/Makefile.amファイルを作成します。
lib_LTLIBRARIES = libjs.la libjs_la_SOURCES = jsapi.c jsarena.c jsarray.c jsatom.c jsbool.c \ jscntxt.c jsdate.c jsdbgapi.c jsdhash.c jsdtoa.c \ (中略) jsxdrapi.h prmjtime.h resource.h js.msg jsopcode.tbl bin_PROGRAMS = js js_SOURCES = js.c jsshell.msg js_LDADD = libjs.la今回はJavaScriptエンジンのライブラリとしてlibjs。JavaScriptのインタプリタコマンドとしてjsプログラムをビルドします。それぞれビルドに必要なソースコードを*_SOURCESで指定しておけば、あとはAutomakeコマンドがいい塩梅のMakefileを作ってくれます。*_PROGRAMSや*_SOURCESのSをよく忘れるので注意> 自分
というわけで先の手順2とあわせ下記3つのファイルを作成しました。
$ ls configure.in Makefile.am src/Makefile.am Makefile.am configure.in src/Makefile.am
手順4 aclocalとlibtoolizeコマンド実行
configure.inで使用するマクロをかき集めるため、aclocalコマンドを実行します。$ aclocal特記することは特に無いっす。実行するだけ。実行後、configure.inファイルで使用しているM4マクロをかき集めてaclocal.m4ファイルを作成します。
また、libtoolスクリプトを生成するconfig/ltmain.shスクリプトや、プラットフォームの判定に使用するcofig/config.guessスクリプトや、プラットフォーム名の短縮名を正規名に変換するためのconfig/config.subスクリプトを設置する為にlibtoolizeコマンドを実行します。
$ libtoolize Putting files in AC_CONFIG_AUX_DIR, `config'.実行するとconfigディレクトリ配下に次のようにファイルが設置されます。
$ ls config/ config.guess config.sub ltmain.shこれらconfigディレクトリに設置されるファイルはlibtoolのバージョンによって多少変化したような気がしますけど、気にしなくてOKっす。
手順5 automake実行
Makefile.am等をもとにMakefile.inを生成したり、Libtool生成に必要なファイルを設置する為にautomakeコマンドを実行します。$ automake --add-missing
手順6 autoheader実行
configureスクリプトの結果を元に、プラットホーム依存部分を吸収する足場となる src/config.h.inファイルを生成する為にautoheaderコマンドを実行します。$ autoheader実行後にはconfigure.inのAM_CONFIG_HEADER()で指定したsrc/config.hのひな形となる src/config.h.inファイルが作成されます。
手順7 autoconf実行
configureスクリプトを生成する為に、autoconfコマンドを実行します。$ autoconf
手順8 不足ファイルの作成
automakeコマンドで警告が出ていた、パッケージに必要なファイルたちを作成しておきます。とりあえず空でも良いので$ touch NEWS AUTHORS ChangeLogなどで作成しておきます。
普通のプロジェクトであれば、この手順8の時点で終了で、とりあえずは
$ ./configure $ make # make installとかできるようになります。ライブラリの場合はインストールするヘッダファイルを指定したりしてあげる必要が有るので、いくつか追加の作業が必要です。
SpiderMonkey固有の作業
普通のパッケージであれば上記の手続きで終了なのですが、SpiderMonkeyの場合は2点ほど注意すべきところが有りました。一つ目はビルド時にXP_UNIXなどプラットフォーム名を示す値を#define or -D して伝える必要がある。二つ目はアーキテクチャ依存の値を調べてsrc/jsautocfg.hファイルを生成してあげる必要が有る点です。追加手順9 プラットフォーム判定の定数を追加する
とりあえず今回は「僕の環境でビルドできればOK」なので、不足している定数をビルド時に追加するようconfigure.inファイルに下記のように追記します。AC_DEFINE([XP_UNIX], [1], [description]) AC_DEFINE([SVR4], [1], [description]) AC_DEFINE([SYSV], [1], [description]) AC_DEFINE([_BSD_SOURCE], [1], [description]) AC_DEFINE([POSIX_SOURCE], [1], [description]) AC_DEFINE([DARWIN], [1], [description])これらの値は、プラットフォームによって変化します。例えばSolarisの場合は
AC_DEFINE([XP_UNIX], [1], [description]) AC_DEFINE([SVR4], [1], [description]) AC_DEFINE([SYSV], [1], [description]) AC_DEFINE([SOLARIS], [1], [description]) AC_DEFINE([HAVE_LOCALTIME_R], [1], [description])という風に指定する必要があり、更にlibmをリンクする(-lm)よう指定する必要が有ります。
これらのプラットフォームごとの差異はSpiderMonkeyのビルドシステム(src/Makefile.ref, src/config/*)を参考にして、configureスクリプト内でプラットフォームを判定し設定してあげます(本来は使用する機能ごとに判定するようにするべきですけど)。プラットフォームの判定ルールと、必要な定数の設定をconfigure.inファイルにshellスクリプトとして記述してあげます。詳細は省略(おーい)しますが、調べてみると全然使ってない定数が多いので、もっと簡素化できそうです。
この状態でautoreconfiコマンドでconfigureスクリプト等をリビルドし、configureスクリプトを実行すると、AC_DEFINE()で指定した値は src/config.hファイルで#defineされます。そのためSpiderMonkeyのソースコード側でこのsrc/condfig.hファイルを#includeしてあげる必要が有ります。
修正が必要そうなファイルは
src/jsapi.h src/jstypes.hぐらいのようだったので、これらのヘッダファイルでsrc/config.hを#includeするよう修正しました。
もしsrc/config.hで#defineするのではなく、コンパイラオプションとして値を引き渡したい場合はconfigure.inファイルのAM_CONFIG_HEADERをコメントアウトしたりすればOKです。そのかわりビルド時のターミナルの画面が大変なことになりますけど。
追加手順10 jsautocfg.hファイルの生成
ビルドするプラットフォームの変数型のビット幅やエンディアンを記述したsrc/jsautocfg.hファイルは、src/jscpucfg.cを元にビルドしたjscpucfgコマンドで下記のように生成しています。$ src/jscpucfg > src/jsautocfg.hsrc/jsautocfg.hファイルはライブラリの各ソースコードで利用されるので、先に生成しておく必要が有ります。src/Makefile.amに
- jscpucfgコマンドをビルドするルール
- jsautocfg.hのビルドルール
- libjsにjsautocfg.hが依存していることを伝える
bin_PROGRAMS = jscpucfg js jscpucfg_SOURCES = jscpucfg.c jsautocfg.h: jscpucfg ./jscpucfg > jsautocfg.h jsapi.c: jsautocfg.h最後のjsapi.cにjsautocfg.hを依存させている記述がちょっと格好わるいんですが、なんかカッコいい指定方法が有ったら誰か教えてください。
手順11 autoreconf実行
configure.inファイルやMakefile.amファイルを修正したので、ビルドシステム全体を更新するためにautoreconfコマンドを実行します。$ autoreconfあとは、普通のオ−プンソースなパッケージと同様に
$ ./configure $ makeしてビルドできるようになります。ちなみにインストールが必要なヘッダファイルはsrc/Makefile.amファイルで下記のように指定しておきます。
pkginclude_HEADERS = jsapi.h jsarena.h jsarray.h jsatomh (以下略)あと配布パッケージはconfigureスクリプトの実行後に
$ make distで生成できます。あー疲れた...
一応今回作ったパッケージを置いておきます。
SpiderMonkey-1.5-rc6a.tar.gz手元のMac OS X 10.3.5と、知人宅のSolaris 8(UltraAX-i2)とこれまた別の知人宅のFreeBSD 4.7(i386)でビルドできるようにしてあります。他のプラットフォームについてはconfigure.inファイルを修正して対応してみてください。え?それだと最初の問題だったプラットフォーム判定式だと移植性がイマイチって点が改善されて無いじゃん って気もしますが、さすがにそこまで修正する気はないんで放置。
あと、ECMAScriptでは定義されていないFileクラス用のソースコード等も含まれていますが、現状では無視しています。ビルド時に-DJS_HAS_FILE_OBJECT するだけなので、./configure --enable-class=file,socket,xml みたいに指定できるようにすべきですね。
GNU Autoconf, Automake, Libtoolについてより詳しく調べたい場合はGNU Autoconf/Automake/Libtoolがお勧めです。多少内容が古くなってはいますが全体の理解にとっても役立つはずです。また、Autotoolsを使わない人でも、ソースコードの移植性の話しやビルドシステムのメンテナンスのネタが色々ちりばめてあるので楽しめるはずです。
とりあえず、とあるプロジェクトでAutotools配下でSpiderMonkeyを使いたかったので、僕の要求は満たせているっぽいです。テストはまだまだ足りてませんけど。
で、SpiderMonke(libjs)の使い方は?
で肝心のSpiderMonkey(libjs)の使い方は別の時に書きます。ドキュメントがいくつか公開されているので、それらを読めばJavaScriptエンジンの組み込み方や、C言語側から独自のクラスを追加する方法がさらっと書いてあります。AFSがApache用にmod_jsモジュールとかの開発をしていたような気がしたのですが、お蔵入りなのかしら? 単にApacheに埋め込むだけだと面白くないし嬉しくもないので、なんか楽しい応用ができると良いですね。