Gauche:ABICompatibility
Shiro(2009/11/23 18:21:34 PST): ABI互換性について気をつけることメモ。
目的
Gaucheのmicro version up (X.Y.Z の Z が変わる変更) については、 Gaucheを入れ替えるだけで拡張ライブラリなどlibgauche.soに依存するものは そのまま使えるようにしたい。
考慮すべきは以下の点。
- ライブラリのサーチパス: 今は.soのサーチパスにバージョンがフルで埋め込まれちゃってるので 拡張ライブラリの.soが見つけられなくなる。
- 拡張ライブラリからのlibgaucheのバージョンチェック: 拡張ライブラリは、 ロードされた際にロード主が自分とABIコンパチであるかどうかチェックしたい。 今はノーチェック。
- mainからのlibgaucheのバージョンチェック: libgaucheをリンクする組み込み ライブラリが、実行時に動的リンクされたlibgaucheとABIコンパチであるかどうか チェックしたい。今はScm_Initでバージョンの完全マッチをチェックしてるので、 micro versionが違うと実行できない。
新しいAPIの追加は許す。つまり、コアを 0.9 -> 0.9.1 にした時:
- 0.9向けにコンパイル済みの拡張ライブラリやmainはそのまま動く
- 0.9.1向けにコンパイルし直せば0.9.1で追加されたAPIが使える。 そうした拡張ライブラリやmainは0.9コアでは動かない。
ライブラリのロードパス
一番簡単なのはロードパス中のバージョン番号埋め込みをmajorとminor (X.Y) だけに することだな。
ただ、この場合、既にGaucheが入ってるところに上書きインストールすると、 既に ${exec_prefix}/lib/gauche/X.Y/ 以下に作られている階層に 上書きすることになる。それはそれでちょっと気持ち悪い。 特に、何か問題が出てダウングレードしたい場合に困りそうだ。 (この階層にはGauche-gl等コア以外のモジュールの.soも入ってるので、 どれとどれを旧バージョンに戻したら良いのかすぐにわからない、とか)
バージョンX.Y.ZのコアはZを一個づつ減らしたパスを全部load-pathに 入れちゃうって手もある。これだと今と同じディレクトリ構造で、 さらに旧バージョン向けにコンパイルされた拡張ライブラリも見える。 でもmicro versionが増えるとロードパスがやたらに長くなるし、 うっかり古いの消したら今使ってる拡張ライブラリも消えちゃったって ことになりそうだ。
少々強引なハックだけれど、Gaucheコアインストール時に 旧バージョン用のディレクトリがあったらコア以外の.soを新バージョンディレクトリから ハードリンクしてしまうってのはどうだ。 インストール時の操作がややこしくなるが、特に問題は生じないような気もする。 (ハードリンクが使えないOSではコピーすることになるのがちょっと嫌かも しれないが)。 これを試してみよう。
いや、だめだ。拡張パッケージが何らかのパッケージマネージャで管理されてる場合、 コピーやハードリンクしちゃうとパッケージマネージャからのアンインストールで ファイルが消えてくれなくなる。
要求事項をまとめてみる。
- Gauche本体のファイルは本体バージョンごとに分けてインストールしときたい。
- 拡張モジュールのファイルはABI versionにつき一ヶ所。コピーとかリンクとか無し。
- 探しに行くディレクトリが際限無く増えてくのはやだ。
ってことは、アーキテクチャ依存ファイルの置き場を こうしてみたらどうだろう。
- 現在:
- ${exec_prefix}/gauche/${gauche_version}/${arch}/* ;; *.so
- ${exec_prefix}/gauche/${gauche_version}/include/* ;; *.h
- ${exec_prefix}/gauche/site/${gauche_version}/${arch}/* ;; *.so
- ${exec_prefix}/gauche/site/${gauche_version}/include/* ;; *.h
- 案:
- ${exec_prefix}/gauche/${ABI_version}/${gauche_version}/${arch}/* ;; 本体の*.so
- ${exec_prefix}/gauche/${ABI_version}/${gauche_version}/include/* ;; 本体の*.h
- ${exec_prefix}/gauche/${ABI_version}/site/${arch}/* ;; 本体以外の*.soなど
- ${exec_prefix}/gauche/${ABI_version}/site/include/* ;; 本体以外の*.h
今はGauche-glのファイルとかがsiteじゃない方(本体と一緒)に入ってるけど、 本体以外のは全部siteに入れることにする。
アーキテクチャ非依存の方はディレクトリ構成は今までどおりとして、 本体以外のは全部site以下に入れる、という点だけ変更する。
- ${datadir}/gauche/${gauche_version}/* ;; 本体の*.scm
- ${datadir}/gauche/site/* ;; 本体以外の*.scm
拡張ライブラリからのバージョンチェック
初期化ルーチンがマクロSCM_INIT_EXTENSIONを呼んでるので、 マクロ定義をちょっといじってABI versionのチェックを忍ばせることは できそうだ。
mainからのバージョンチェック
Scm_Init()に渡してるsignatureにバージョンが含まれるので、 それを解析してチェックする。
gauche versionとabi version
ABI_versionはどのように指定するのが良いか。
- (案1) gauche_versionのmajor.minorに合わせる。 例えばgauche versionが0.9.2なら ABI versionは0.9, gauche versionが1.0.13ならABI versionは1.0
- (案2) ABI_versionが変わればlibgaucheのsonameも変わる。sonameに含める インタフェースバージョン番号は1桁だから、ABI_versionもそっちに合わせちゃう。 gauche version 0.9.xの間はABI_version=0。gauche version 1.0になったら ABI_version=1、gauche version 1.1になったらABI_version=2、and so on.
案1の方が 一見わかりやすそうではあるのだけれど、 よく考えるとそうでもない。
- アーキテクチャ依存ファイル置き場のパスにはABI_versionとgauche_versionが 含まれるのでまぎらわしい。
- 1.0と1.1でABIを変えるならsonameも変えないとならないので、libgauche.so.X.Y.Z のX.Y.Zがgauche_versionと一致するわけではない。無関係に単調増加する番号を 振るか、1.0.xのABIは100、1.1.xのABIは101、1.13.xのは113、みたいにエンコードするか。
- もし1.0の手前にプレリリースとして0.99とかを出したいと思った場合、ABI versionとの 齟齬が発生する。
まあsonameについては、gauche_version X.Yをlibgauche-X.so.Y にマップしてしまう、 という手が無くはない。
案2については、プレリリースの0.99でABIバージョン1を実装する、というような 融通がきかせられるという利点がある。(あと、どうしてもmicro version upで ABIを変更せざるを得ない止むに止まれぬ事情があった場合、とか。)
折衷案でこういうのはどうだ(案3)。
- ABI_versionは原則としてgauche_versionのmajor.minorと一致。
- アーキテクチャ依存ファイル置き場のパスを
${exec_prefix}/gauche-${ABI_version}/...
とする。例えば0.9の間は/usr/lib/gauche-0.9/...。1.0になったら/usr/lib/gauche-1.0/...。 - ライブラリのsonameはlibgauche-X.Y.so.0 とする。
- ライブラリのrealnameはlibgauche-X.Y.so.0.Z とする。Zはmicro version。
- 特例として、0.99.wみたいなプレリリースの場合、ABI versionは先取りして1.0とし、 ライブラリのrealnameはlibgauche-1.0.so.0.0.w という具合にする。1.0正式リリース の際はlibgauche-1.0.so.0.0.5とかになるかもしれない。
major.minorまでをそんなに頻繁にアップデートするとも思えないので、 ${exec_prefix}以下にディレクトリが無闇に増殖してしまうってことは多分ないだろう。