Gauche:automake+libtoolize
Gauche-0.7からautomake+libtool+libltdl化する予定。 ここは作業メモに使う。
- 0.7リリースで採用した方針
- 0.7までのメモ
- automakeの困ったところ
- 自動生成されるtexinfo
- gnu make依存部分
- config.hのディレクトリを勝手に-Iに追加すること
- サブディレクトリからのファイルのインストール
- self-containedなsubmodule
- libtoolの困ったところ
- shared libを作るかstatic libを作るかを個別に指定できない
- -no-undefinedの有無
- dsoを作るときにwhole archiveの順番を変える
- .aのインストール後にranlibを走らせる
- インストール時の不必要なメッセージ
- libtoolによるDLL作成
- libtoolのdependency管理
- 雑感
- 資料
0.7リリースで採用した方針
- libtoolは採用。但し、若干手を加えた。
- ltmail.shを変更して、libtool実行時に --disable-shared/--disable-staticフラグを取る様にする。 MacOS Xにおける、「本体は強制的にstatic link、拡張モジュールはdynamic link」を 実現するために必要。
- aclocal.m4内のlibtool関連マクロに手を加え、cygwin上で always_export_symbolsがyesになるようにする。
- さらに、プラットフォームによってlibtoolに-no-undefinedオプションを 加えるか加えないかをconfigureで判断する必要がある。
- libltdlは使用せず。
- ダイナミックリンク部分はGCと同期を取る必要がある (Boehm GCでは、 dlopen()をフックしている)。したがって、libgauche側だけlibltdlに置き換えても 動かない。
- libltdlをlibgaucheに組み込んだ場合、他にlibltdlを使っている ライブラリとlibgaucheを一緒に使えなくなる。libltdlを組み込まない場合、 Gaucheがlibltdlに依存してしまう。
- automakeは使用せず。
- 標準的でないビルドやインストールプロセスの部分は、 結局hookターゲットに自分でルールを書くことになるので、 あんまりautomakeの恩恵が無い。
- automakeを使って楽になるのは、makefileを最初に書く人だけ。 最初に書くmakefileが50行だったのが5行になったとしても、 そもそもmakefileなんて最初に書いたらその後滅多にいじらないのだから、 たいした手間の節約にならん。後からmakefileをいじる人にとっては、 makefileが短いことよりも理解しやすいことの方が重要。
- CVSからビルドする人はautomakeのバージョンを揃える必要がある。
0.7までのメモ
automakeの困ったところ
自動生成されるtexinfo
info_TEXINFOSにgauche_refe.texi gauche_refj.texiを入れといても automakeを走らせる時にはそれらのファイルが無いから 文句を言われる。 *.texiが自動生成されるケースってのは対象外なのか?
gnu make依存部分
automakeの生成するMakefile.inには、dependency関係部分で gnu makeに依存した構文が挿入されることがある。automake-1.4-p5だと "-include" というディレクティブが入っていた。
automake標準の方法で "make dist" するとこういう構文は展開されて消えるので、 tarballに入るMakefile.in (から生成されるMakefile)は普通のmakeでも使える ものになる。
だが、"make dist" が使えない場合にこういう設計は問題だ。 dependencyの自動生成が難しい問題は承知しているが、やはり Makefileのイディオムの展開という機能と、 ディストリビューションパッケージの作成という機能は別にして欲しいなあ。
config.hのディレクトリを勝手に-Iに追加すること
AC_CONFIG_HEADERで指定したヘッダファイルのあるディレクトリが 勝手にインクルードパスに追加される。 #include "config.h" と書きたくないからわざわざサブディレクトリに 置いてるんじゃーそれがわからんのかー。
サブディレクトリからのファイルのインストール
サブディレクトリsubdirにあるファイルfoo.hを、例えばpkghdrdirに インストールしたいとき。
pkghdr_SCRIPTS = subdir/foo.h
とか書いてもだめ。インストール前に $(pkghdrdir)/subdir を作成してくれない。 pkghdrdirにあらかじめsubdirを含ませておくと、$(pkghdrdir)/subdirを 作った後で $(pkghdrdir)/subdir/subdir/foo.h にインストールしようとするからだめ。
install-data-hook とかは自動生成されるinstallの後に走るから ディレクトリの作成には使えない。
scriptの場合はtransformをかけるという技で何とかなるかもしれんが、 ヘッダファイルは無理。install-data-local使って自分でしこしこやるしか ないのかな。
self-containedなsubmodule
例えば gc/ 以下は単独パッケージにもなれるようなMakefile.amとconfigure.in を持っているわけだけど、これをGauche本体の一サブディレクトリとして 扱いたいんだがうまくいかない。
- トップでautomakeしてもgc/下でMakefile.inを作ってくれない。何故?
- gc/下で作られるMakefile.inはgc/下を独立パッケージとして扱ってしまうので、 例えばmake distをリカーシブに起動できない。
ああ、ドキュメントに書いてあった。configure.inがサブディレクトリに ある場合はそれぞれでautomakeを走らせなくちゃならないのか。 でもそしたらmake distとかはどうなるんだ?
libtoolの困ったところ
shared libを作るかstatic libを作るかを個別に指定できない
どっちを作るか、もしくは両方作るか、というのはconfigure時に決定されて しまう。configure.in内の指定でどちらかを強制的にoffにすることはできるが、 「libgaucheだけstaticで、extensionはsharedで」という指定はできない。
MacOS Xでlibgaucheをshared libにすると動いてくれないので、 以下のいずれにするか迷っている。
- libtoolをハックして、実行時にshared/staticの作成の可否を切替えられる ようにする。
- libtoolを捨てる
- extension用にconfigure.inとltmain.shを別に用意する
まー妥当な判断は3.だろーなー。automakeがconfigure.inを複数持つのを 気に入らないからわざわざ一本化したのに、逆戻りか。
libtool捨て、の方向にもかなり惹かれるんだが、 何らかのラッパースクリプトが必要になるのは確かなんで、 libtoolの再発明にしかならない気がする。 実際、0.6.8まではldwrap.shというアドホックなスクリプトを使ってたわけだし。
- (2003/05/13 03:46:40 PDT): 3. を試してみたが、困ったことに気づいた。 リンク時に未解決のシンボルがあるとリンクできないプラットフォームが あるため、extensionのリンク時にlibgauche.laを加えている。 ところが、goshをstatic linkするためにメインのlibtoolをAC_DISABLE_SHARED でconfigureすると、libgauche.aが出来て、 それがextensionの.soコンパイル時に取り込まれてしまうのだ。
結局、次の3つを切替えなければならないということか。
Linux, *BSD他 | MacOS X | Cygwin | |
libgaucheのコンパイル | dynamic/staticともに可 | staticのみ可 | dynamicのみ可(*) |
extensionのコンパイル時にlibgauche.laをリンク | libgaucheがdynamicなら可 | 不可 | 必要 |
(*: libgaucheをstaticで生成すると、extensionがコンパイルできない)
- (2003/05/18 01:09:37 PDT): というわけで、ltmain.shを変更して、 libtool実行時に--disable-static/--disable-dynamicを受け付けるようにした。
-no-undefinedの有無
extensionのリンク時に-no-undefinedを指定しないと、libtoolは CygwinでDLLを作ってくれない (シンボルを解決するのに必要な ライブラリを全て与えても、だ)。
一方、MacOS Xではgoshをスタティックリンクする必要上、 -no-undefinedは指定できない。
他のunixではどちらでも良い。
dsoを作るときにwhole archiveの順番を変える
DSOに、別ディレクトリで作成されたアーカイブfoo/foo.la中の オブジェクトファイルを全部含めたい場合、libtoolに渡すオブジェクトに foo/foo.laを書いておけば、自動的に--whole-archiveオプションを くっつけてリンカに渡してくれる。
$(LIBTOOL) --mode=link $(CCLD) $(OBJECTS) foo/foo.la $(LDFLAGS) $(LIBS)
問題は、それをするためにlibtoolが引数を解析する際に、通常の.loファイルと .laファイルで別々にリストを作って処理することだ。そのため、 foo/foo.la中のオブジェクトを a.lo と b.lo の間に配置したい場合、
$(LIBTOOL) --mode=link $(CCLD) a.lo foo/foo.la b.lo $(LDFLAGS) $(LIBS)
と書いておいても、実際にリンカに渡される時には
gcc -o hoge.so a.lo b.lo -Wl,--whole-archive foo/foo.la -Wl,--no-whole-archive ...
という具合に順番が変えられてしまう。 まったくもってお節介だ。
.aのインストール後にranlibを走らせる
HP-UXでテストしてて気づいたんだが、libfoo.a と libfoo.soの両方がある場合、 libtool --mode=install install -m 444 libfoo.la $(DIR) でインストールすると、 libbfoo.aのインストール後にranlibを走らせようとする。
しかし、インストールしたlibfoo.aはmodeが444にされているので書き込めない。 よってエラーとなり、makeが中断する。まあそりゃそうだわな。 なんでranlibが必要なんだろう? 必要だとしても、libtool内で permission を一時的にwritableにするくらいやってほしい。
- (2003/05/29 23:46:54 PDT): あーなるほど。old_postinstall_cmdsにranlibして chmod 644するコマンドが入っているわけね。ってことはinstallの方に -m フラグをつけるのは推奨されてないんかな。でもこれってかなりやだぞ。 インストールしたlibfoo.aファイルのパーミッションを644にするって何で 決め打ちなんだ? こういうところもお節介すぎる。 だいいちインストール後にたとえownerであってもwritableなのは嫌なんだが。
インストール時の不必要なメッセージ
automakeではなくlibtoolなんだが、mode=finishの時にデフォルトはこんな メッセージが表示される。
---------------------------------------------------------------------- Libraries have been installed in: /usr/local/lib/gauche If you ever happen to want to link against installed libraries in a given directory, LIBDIR, you must either use libtool, and specify the full pathname of the library, or use the `-LLIBDIR' flag during linking and do at least one of the following: - add LIBDIR to the `LD_LIBRARY_PATH' environment variable during execution - add LIBDIR to the `LD_RUN_PATH' environment variable during linking - use the `-Wl,--rpath -Wl,LIBDIR' linker flag - have your system administrator add LIBDIR to `/etc/ld.so.conf' See any operating system documentation about shared libraries for more information, such as the ld(1) and ld.so(8) manual pages. ----------------------------------------------------------------------
これがうざい。しかも、サブモジュール毎に繰り返し表示されるからさらにうざい。 --quietで消せるんだが、そうするとlibtoolが実行するコマンドラインの エコーも出なくなっちゃうんだよね。
こういうメッセージの情報が必要な人間はmakeの出力なんていちいち見ないし、 逆にmakeの出力を見る人間ならこういう情報は知ってるんだから 実行されてるコマンドラインとかの重要な情報に絞って出して欲しいげな。
libtoolによるDLL作成
DLLでエクスポートされるデータの参照がうまくいかんなあ。
(2003/04/22 12:07:12 PDT): ふーむ。従来のdlltoolを使う方法では、 Scm_HogeClassと_imp__Scm_HogeClassの両方がexport symbolリストに 入っていたのだが、 defの作成をbinutilsにおまかせでやると、ソース中で明示的に _imp__Scm_HogeClassが定義されている場合にScm_HogeClass自身がexport symbolsから 外されてしまうようだ。
- むー。libtool内でalways_export_symbols=yesとしておくと、 外部シンボルファイルが作成されて_imp__Scm_HogeClassも含まれる。 でもconfigureではcygwinだと無条件にalways_export_symbols=noに 設定してしまうみたいだ。何故だー。
- dlltoolを使って自分で外部シンボルリストlibgauche.defを作っておき、 libtool起動時に-export-symbols libgauche.defとしてやるとちゃんと動く。 こちらがまっとうな方法だろうか。しかしlibtoolはそういう詳細を 隠してくれるツールじゃなかったんかいな。
- 犯人はコイツだ →
http://www.cygwin.com/ml/cygwin-apps/2002-04/msg00212.html
せめてautoconfマクロの引数かなんかでalways_export_symbolsの値を 選べるようにしておいてくれ〜 aclocal.m4を生成した後にパッチあてるかな。
libtoolのdependency管理
dlopen()用に作ったfoo.la内のシンボルを、同じくdlopen()用に作ったbar.laから 参照したい場合、リンク行にそのまま並べておくことができる。 (ただ、「ポータブルではない」というwarningが出る)。
$(LIBTOOL) --mode=link $(CCLD) -o bar.la $(bar_OBJECTS) foo.la
そのままfoo.laの実態(.libs/foo.dll)をリンクすることはできないと思うんだが、 どうやってシンボル解決をやってるのか、要調査。
具体的にはext/mt-randomがext/uvectorに依存してるのね。
- (2003/05/14 21:42:46 PDT) : これだと、MacOS Xでは問題が出ることが判明。 MacOS Xではリンクオプションから-no-undefinedを削って、 uvector.soへの参照を未解決のままにしておかないとリンクが通らない。
- (2003/05/28 06:15:51 PDT): いよいよinstallも含めたテストの段階になって、またまた問題が。 ext/mt-randomのinstallの際にlibtoolがlinkをやりなおすんだが、 その段階で-luvectorをつけてリンクしようとする。 これはlibuvectorを探しに行く。 でもext/uvectorが持っているのはuvector.laなんで、リンクが失敗するのだ。 これに関してはext/uvectorがlibuvector.laを作るように変更して回避。
雑感
(2003/04/11 18:01:20 PDT): たぶん、既存のパッケージをautomake化する場合は、 全部automakeにやらせようとしないほうが良いんだろう。 automakeの方法に合うところだけautomakeにやらせて、 後は従来の方法を使えば良いと割り切ることが肝心のような気がしてきた。 Gaucheの場合、tarballの作成は今まで通りDISTスクリプトでやれば良いわけだ。
(2003/04/12 12:34:49 PDT): 今のところ、自分なりの感想はこんなかんじ。
- autoconf rocks ---シェルスクリプトでテストを行うのは現実的な 方法だ。configure.inでマクロと通常のシェルスクリプトを混ぜて書けるのは 非常に便利だ。これがマクロ展開でなくて、全く別の設定ファイルを読んで configureを生成する方向だと、さまざまな制限に悩まされていたことだろう。 問題があるとすれば、共通に使えるマクロの管理が弱いところと、 ドキュメントが少なくてしばしばconfigureの中を覗くことを余儀なくされる ことくらい。
- libtool is a necessary evil --- あの複雑さは好きじゃないが、ことshared libraryに 関して問題を解決しようと思ったら同程度に複雑になってしまうだろうと 思われる。
- automake loses --- 何か、根本的なデザインがおかしいという気が してならない。ひとつの理由は、automakeが多くのことを解決しようと しすぎているからのように思われる。パッケージの体裁のチェック (INSTALLや NEWSがあるかとか)とMakefileのcommon idiomの生成は別ツールで良いと 思うし、ディストリビューションの作成はmake distではなくやはり 別ツールが良かろう。configure.inをスキャンするというデザインも、 本来そういう意図で作られたのではないものを無理矢理やってるせいで、 configure.inの書き方に無用の制限を加えたりとか、 バージョン間でAC_マクロとAM_マクロがごちゃごちゃになったりとか、 ろくなことになっていない。
(2003/04/15 11:53:55 PDT): automakeのソースを読んでみた。結局のところ、 automakeのやることはヒューリスティクスのかたまりなんだが、 そのヒューリスティクスがソースにハードコードされているのが automakeに対して感じる違和感の根本原因であるように思えて来た。
configureのやることもヒューリスティクスのかたまりなんだけど、 autoconfは少なくとも、ヒューリスティクスそのものの記述は 処理系の外に出ている。built-in macroで足りなければ、 自分でmacroを書けて、しかも自分で書いたmacroはbuilt-in macroと対等だ。
一方、automakeでは、自分でprimaryを定義することはできない。 (新しいバージョンにはあるのだろうか? 1.6のドキュメントには 少なくとも見付けられなかったが)。 既に提供されてるprimaryの動作をちょっとラップした新しいprimaryを 作ることもできない。提供されているprimaryの動作が気に入らなければ、 自分でmake ruleを書くしかない。
(2003/04/21 23:38:02 PDT): automakeは外した。 考えてみたら、automakeで楽ができるのは基本的に「最初にMakefileを書く人間」と 「パッケージングを行うスタッフ(make distを使う場合)」であって、 他の大部分の、CVSソースをハックする人間にはほとんどメリットが無い。 それなのにCVSソースハッカー全員にautomake, autoconf, libtoolのバージョンを 揃えることを要求するなど、ばかげている。
ついで、libltdlも外す。libltdlの機能はとても欲しいのだけれど、 gaucheをライブラリとして使う場合、他にlibltdlを使っているライブラリが あると衝突してしまう。システムのlibltdlを使うようにすれば衝突は 避けられるが、基本部分に非標準の外部ライブラリへの依存を入れたくはない。
さて、残るはlibtoolなんだが… 現在cygwin上でDLL生成を試みているんだが、 これも道が遠そうだ。libtoolが実行するコマンドを選択する基準が ドキュメントされてないから、結局あのシェルスクリプトを読むはめになる。
(2003/05/18 01:09:37 PDT): ようやく、各プラットフォームでビルドが通りそうな出口が見えてきた。 しかし、configureでプラットフォームを見てコンパイルフラグや リンクフラグをごにょごにょする、という部分を無くしたいから libtoolに期待したのだけれど、結局、まともにビルドするためには、 configureでlibtool自身に与えるフラグを調整する必要があった。
ここまでしてなおlibtoolを使うメリットってなんじゃろか。 まあ、オブジェクトファイルや共有ライブラリのエクステンションの 処理はlibtool内に隠されるので、Makefileを書くときに 一様に *.lo と *.la で書けるというのはメリットだな。
しかし、本来の目的であるconfigure.in内でのプラットフォーム毎の フラグの切替えの除去というのができていない以上、 あの巨大なシェルスクリプトを持ち歩く意味はあんまり無いように 思える。
libtoolが巨大化している理由のひとつは、あらゆる可能性に 対応しようとしているからだと思う。でも、今回のような イレギュラーなケースにまで対応させようとすると、 どんどんスクリプトがふくらんで、メンテ不可能になるんじゃないか。 それなら、ケースバイケースで専用スクリプトを書いた方が よほどすっきりするだろう。
さんざ苦労したので0.7はlibtoolで行くつもりだが、 その後は独自リンクスクリプトに移行するかもしれない。
資料
- GNU Autoconf, Automake, and Libtool 全文がオンラインで読める。でも基本的なことしか書いてないなー。