[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

3. libtoolを使用する

libtoolが人生をより単純にする方法が分かるまで,独自のパッケージで libtoolを使用することを話す意味はありません.この章の例は,標準的なラ イブラリのビルド処理と,libtoolの処理を,二つの異なるプラットフォーム で比較することで,主な特徴を紹介します.

` a23'

スタティックライブラリのみのUltrix 4.2プラットフォーム.

` burger'

共有ライブラリを持つ,NetBSD/i386 1.2プラットフォーム.

独自のプラットフォームの例をこれに続けることが可能で,それは,libtool でインストールされた,前もってコンフィグレーションされているlibtoolス クリプトを使用します(see section libtoolのコンフィグレーション).

以下の例のソースファイルは,libtool配布物の`demo'サブディレクトリ から持ってきています.ファイル`foo.c'と`hello.c'からライブラ リ`libhello'をビルドしていると仮定してください.

`foo.c'ソースファイルがcos数学ライブラリ関数を使用していて, それは通常,Cライブラリではなく単独の数学ライブラリで見つかることに注 意してください(see (libc)Trig Functions section `Trigonometric Functions' in The GNU C Library Reference Manual).そのため,`foo.o'や `foo.lo' を実行形式やライブラリにリンクするときは,常にリンク行の 最後に-lm を加える必要があります(see section ライブラリ内部の依存性).

同じ規則は,標準Cライブラリに無い関数を使用するとき,常に当てはまりま す…これらのオブジェクトに対しリンクするときは,適切な -lnameフラグをリンク行の終りに加える必要があります.

ライブラリをビルドした後,`libhello'に対して`main.o'をリンク することでプログラムを作成したいと思います.


3.1 オブジェクトファイルの作成

ソースファイルからオブジェクトファイルを作成するため,コンパイラは`-c' フラグ(とその他の必要なあらゆるフラグ)とともに呼び出されます.

 
burger$ gcc -g -O -c main.c
burger$

上記のコンパイラコマンドは,ソースファイル`main.c'からオブジェク トファイル`main.o'を生成します.

ほとんどのライブラリシステムでは,スタティックライブラリの一部となるオ ブジェクトファイルを作成することは,実行可能な形式にリンクされるオブジェ クトファイルを作成することと同じくらい単純です.

 
burger$ gcc -g -O -c foo.c
burger$ gcc -g -O -c hello.c
burger$

しかし,共有ライブラリはposition-independent code (PIC)のみから ビルドされます.そのため,標準のposition-dependent codeではなくPICを生 成するようコンパイラに伝えるため,特定のフラグを渡す必要があります.

これがライブラリ実装の詳細なので,libtoolは個別の(`.o'の代わりに `.lo'で終わる)ライブラリオブジェクトファイルを用いて,複雑なPICコ ンパイラフラグを隠蔽します.共有ライブラリが無い(または,特定のPICフラ グが無い)システムでは,これらのライブラリオブジェクトファイルは"標準 の"オブジェクトファイルと同じです.

`foo.c'と`hello.c'に対するライブラリオブジェクトファイルを作 成するため,単純に標準のコンパイルコマンドを引数として,libtoolを呼び 出してください(see section コンパイルモード).

 
a23$ libtool --mode=compile gcc -g -O -c foo.c
gcc -g -O -c foo.c
echo timestamp > foo.lo
a23$ libtool --mode=compile gcc -g -O -c hello.c
gcc -g -O -c hello.c
echo timestamp > hello.lo
a23$

それぞれの呼び出しで,libtoolが二つのファイルを作成することに注意して ください.`.lo'ファイルはライブラリオブジェクトで,それは共有ライ ブラリにビルドされ,`.o'ファイルは標準的なオブジェクトファイルで す.`a23'では,スタティックライブラリのみサポートされているので, ライブラリオブジェクトはタイムスタンプのみです.

共有ライブラリのあるシステムでは,ライブラリオブジェクトと標準オブジェ クトが異なるように,libtoolはPIC生成フラグをコンパイルコマンドに自動的 に挿入します.

 
burger$ libtool --mode=compile gcc -g -O -c foo.c
gcc -g -O -c -fPIC -DPIC foo.c
mv -f foo.o foo.lo
gcc -g -O -c foo.c >/dev/null 2>&1
burger$ libtool --mode=compile gcc -g -O -c hello.c
gcc -g -O -c -fPIC -DPIC hello.c
mv -f hello.o hello.lo
gcc -g -O -c hello.c >/dev/null 2>&1
burger$

二番目に実行されるGCCがその出力を破棄していることに注意してください. これは,コンパイラの警告がうるさく重複しないために行われます.


3.2 ライブラリのリンク

libtoolを用いない場合,スタティックライブラリを作成するため,プログラ マはarコマンドを呼び出していました.

 
burger$ ar cru libhello.a hello.o foo.o
burger$

しかしもちろん,それだけではあまりに単純すぎて,多くのシステムでは(そ れ以上のカルマや何かを与えるため)結果として生成されたライブラリ上で, ranlibコマンドを実行する必要があります.

 
burger$ ranlib libhello.a
burger$

libtoolの"ライブラリはプログラム"というアプローチであるといるこの作 業に対して,Cコンパイラを使用することはより自然に感じられます.その ため,共有ライブラリが無いプラットフォームでは,libtoolは単純にシステ ムのar(そして可能ならranlib)コマンドのラッパーとして動作 します.

また,libtoolのライブラリ名は,標準の名前(`.a'接尾子の代わりに `.la'接尾子を持ちます)とは異なります.libtoolの引数は,コンパイラで `libhello.la'という名の実行形式を生成するために使用したのと同じも のです(see section リンクモード).

 
a23$ libtool --mode=link gcc -g -O -o libhello.la foo.o hello.o
libtool: cannot build libtool library `libhello.la' from non-libtool \
                objects
a23$

あぁ!libtoolは通常のエラーを得てしまいましたた…ライブラリオブジェ クトの代わりに,標準のオブジェクトからライブラリをビルドしています.こ れはスタティックライブラリでは問題ありませんが,共有ライブラリシステム では非常に重要です.

そのため,今回はライブラリオブジェクトファイルを用いて,もう一度試して みましょう.`foo.c'がcos数学ライブラリを使用しているので, コマンドラインに-lmを加える必要があることも忘れないでください (see section libtoolを使用する).

共有ライブラリをビルドするその他の複雑なことは,(最終的に)インストール されるディレクトリパス(この場合は,`/usr/local/lib') (1)を指定する必要があることです.

 
a23$ libtool --mode=link gcc -g -O -o libhello.la foo.lo hello.lo \
                -rpath /usr/local/lib -lm
mkdir .libs
ar cru .libs/libhello.a foo.o hello.o
ranlib .libs/libhello.a
creating libhello.la
a23$

さて,共有ライブラリのプラットフォーム上で同じトリックを試してみましょ う.

 
burger$ libtool --mode=link gcc -g -O -o libhello.la foo.lo hello.lo \
                -rpath /usr/local/lib -lm
mkdir .libs
ld -Bshareable -o .libs/libhello.so.0.0 foo.lo hello.lo -lm
ar cru .libs/libhello.a foo.o hello.o
ranlib .libs/libhello.a
creating libhello.la
burger$

さてそれはかなり賢いです…libtoolは共有ライブラリを作成するため, スタティックライブラリと同様に,曖昧なldコマンドを実行しただけ です.

libtoolが,現在のディレクトリではなく,`.libs'サブディレ クトリに余分なファイルを作成することに注意してください.この機能は,ビ ルドディレクトリをきれいにするのをより簡単にするためと,たまたま libtoolの使用を忘れていて他のプログラムを実行するとき,確実に手ひどく 失敗するので役に立ちます.


3.3 実行形式のリンク

ライブラリを実行形式とリンクする前に,インストールする(恒久的な 場所にそれを配置する)場所を選択した場合,リンクするためにlibtoolを使用 する必要はありません.ライブラリの位置を指定するため,単純に適切な `-L'と`-l'フラグを使用してください.

システムのリンカによっては,結果として生じる実行形式に,共有ライブラリ の完全なディレクトリ名の符号化を強要するものもあります.libtoolは,恒 久的なディレクトリ名のみをインストールされた実行形式に書き込むことを確 実にするため,特別な魔法でこの設計ミスに関して動作する必要があります.

このバグの重要性は見落としてはなりません.それによるプログラムの暴走は 明白ではありません.それはセキュリティホールを作成し,さらに悪いことに は,パッケージのインストール後にライブラリソースコードを編集した場合, インストールされたプログラムの動作を変更してしまうでしょう!

そのため,インストールする前にライブラリとプログラムをリンクさせたい場 合,リンクするためにlibtoolを使用する必要があります.

インストールされていないライブラリとリンクする古い方法は,以下のように なります.

 
burger$ gcc -g -O -o hell.old main.o libhello.a -lm
burger$

libtoolの方法は,ほとんど同じです(2)(see section リンクモード).

 
a23$ libtool --mode=link gcc -g -O -o hell main.o libhello.la -lm
gcc -g -O -o hell main.o ./.libs/libhello.a -lm
a23$

真実としてはあまりに単純に見えます.libtoolが行うことは, `libhello.la'を`./.libs/libhello.a'に変換することが すべてですが,`a23'には共有ライブラリがないことを忘れないでくださ い.

`burger'では,状況が異なります.

 
burger$ libtool --mode=link gcc -g -O -o hell main.o libhello.la -lm
gcc -g -O -o .libs/hell main.o -L./.libs -R/usr/local/lib -lhello -lm
creating hell
burger$

さて,`libhello.la'が既にインストールされていると仮定し,新しいプ ログラムをそれとリンクしたいとします.自分でそれがある場所を探し,以下 を実行します.

 
burger$ gcc -g -O -o test test.o -L/usr/local/lib -lhello

しかし,`/usr/local/lib'が標準のライブラリ検索パスに無い場合, testを実行することはできません.しかし,既にインストールされて いるlibtoolライブラリとリンクするためlibtoolを使用する場合,それは The Right Thing (TM) (正解)となります.

 
burger$ libtool --mode=link gcc -g -O -o test test.o /usr/local/lib/libhello.la
gcc -g -O -o .libs/test test.o -Wl,--rpath
-Wl,/usr/local/lib /usr/local/lib/libhello.a -lm
creating test
burger$

libtoolが,ライブラリlibhello.laが依存している`-lm'同様,必要なラ ンタイムパスフラグを追加していることに注意してください.いいですね,ふっ ふ?

libtoolがラッパースクリプトを作成したので,インストールとデバッグにも libtoolを使用したほうがいいでしょう.しかし,プログラムはインストール されていないlibtoolライブラリには全く依存しないので,ラッパースクリプ トを用いない場合でもおそらく有用でしょう.この場合は,ラッパースクリプ トの作成を避けるため,おそらくより賢くlibtoolを作成できたでしょうが, これは読者の演習として残しておきます.

実行形式hellは,実際には`.libs'サブディレクトリに 作成されることに注意してください.そして,ラッパースクリプトは現在のディ レクトリに作成されます.

NetBSD 1.2では,libtoolは`-R/usr/local/lib'コンパイラフラグを使用 して,`libhello'のディレクトリのインストールを符号化します.そし て,ラッパースクリプトは,正しくインストールされるまで実行形式が正しい (`./.libs'にある)共有ライブラリを見つけることを保証しま す.

二つの異なるプログラムを比較してみましょう.

 
burger$ time ./hell.old
Welcome to GNU Hell!
** This is not GNU Hello.  There is no built-in mail reader. **
        0.21 real         0.02 user         0.08 sys
burger$ time ./hell
Welcome to GNU Hell!
** This is not GNU Hello.  There is no built-in mail reader. **
        0.63 real         0.09 user         0.59 sys
burger$

ラッパースクリプトは実行にかなり時間がかかりますが,共有ライブラリがイ ンストールされていなくても,少なくとも結果は正しくなります.

そのため,共有ライブラリがもたらした,全体的なスペース削減ははどうなっ ているのでしょう?

 
burger$ ls -l hell.old libhello.a
-rwxr-xr-x  1 gord  gord  15481 Nov 14 12:11 hell.old
-rw-r--r--  1 gord  gord   4274 Nov 13 18:02 libhello.a
burger$ ls -l .libs/hell .libs/libhello.*
-rwxr-xr-x  1 gord  gord  11647 Nov 14 12:10 .libs/hell
-rw-r--r--  1 gord  gord   4274 Nov 13 18:44 .libs/libhello.a
-rwxr-xr-x  1 gord  gord  12205 Nov 13 18:44 .libs/libhello.so.0.0
burger$

うーん,だめだなあ(3).おそらく, 私はこのプロジェクトを破壊し,作成中のゆりかごを取り上げたほうがいいで しょう.

実際,それは重要なことを証明しています.共有ライブラリには,それが(関 連する)複雑さのため,オーバーへッドをがあります.この状況では,ダイナ ミックの価値は8キロバイトで,報酬は約4キロバイトです.そのため,少なく とも二,三個以上のプログラムとリンクするまで,共有される `libhello'を維持することは利点になりません.


3.4 実行形式のデバッグ

`hell'が複雑なプログラムの場合,システムにインストールする前にそ れのテストとデバッグを間違いなく行いたいでしょう.上記のセクションで, libtoolラッパースクリプトが,プログラムを直接実行することを可能にする 方法を見ましたが,残念ながら,このメカニズムはデバッガの邪魔になります.

 
burger$ gdb hell
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is no warranty for GDB; type "show warranty" for details.
GDB 4.16 (i386-unknown-netbsd), (C) 1996 Free Software Foundation, Inc.

"hell": not in executable format: File format not recognized

(gdb) quit
burger$

残念です.GDBは実行形式がある場所が分からないので動作しません.そのた め,もう一度実行形式でGDBを呼び出してみてください.

 
burger$ gdb .libs/hell
trick:/home/src/libtool/demo$ gdb .libs/hell
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is no warranty for GDB; type "show warranty" for details.
GDB 4.16 (i386-unknown-netbsd), (C) 1996 Free Software Foundation, Inc.
(gdb) break main
Breakpoint 1 at 0x8048547: file main.c, line 29.
(gdb) run
Starting program: /home/src/libtool/demo/.libs/hell
/home/src/libtool/demo/.libs/hell: can't load library 'libhello.so.2'

Program exited with code 020.
(gdb) quit
burger$

あぁ.さて,GDBは,`hell'がリンクしている共有ライブラリを見つける ことができないため文句を言いました.そのため,正しいライブラリパスを設 定してデバッガを実行するために,libtoolを使う必要があります.幸い, `.libs'ディレクトリを完全に忘れて,そのままの実行形式の ラッパーで実行可能です(see section 実行モード).

 
burger$ libtool --mode=execute gdb hell
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is no warranty for GDB; type "show warranty" for details.
GDB 4.16 (i386-unknown-netbsd), (C) 1996 Free Software Foundation, Inc.
(gdb) break main
Breakpoint 1 at 0x8048547: file main.c, line 29.
(gdb) run
Starting program: /home/src/libtool/demo/.libs/hell

Breakpoint 1, main (argc=1, argv=0xbffffc40) at main.c:29
29	  printf ("Welcome to GNU Hell!\n");
(gdb) quit
The program is running.  Quit anyway (and kill it)? (y or n) y
burger$

3.5 ライブラリのインストール

libtoolが無いシステムでライブラリをインストールすることは,全く簡単で す… それらをその場所にコピーするだけです.(4)

 
burger$ su
Password: ********
burger# cp libhello.a /usr/local/lib/libhello.a
burger#

おっと,ranlibコマンドを忘れないでください.

 
burger# ranlib /usr/local/lib/libhello.a
burger#

libtoolのインストールは,同様に全く単純です.通常使用する, installcpコマンドをそのまま使用してください (see section インストールモード).

 
a23# libtool --mode=install cp libhello.la /usr/local/lib/libhello.la
cp libhello.la /usr/local/lib/libhello.la
cp .libs/libhello.a /usr/local/lib/libhello.a
ranlib /usr/local/lib/libhello.a
a23#

アンインストールでlibtoolを助け(see section アンインストールモード),リンクし (see section 実行形式のリンク),dlopenでプログラムを助ける (see section dlopenモジュール)ため,libtoolのライブラリ`libhello.la' もインストールされることに注意してください.

共有ライブラリの例は,以下のようになります.

 
burger# libtool --mode=install install -c libhello.la /usr/local/lib/libhello.la
install -c .libs/libhello.so.0.0 /usr/local/lib/libhello.so.0.0
install -c libhello.la /usr/local/lib/libhello.la
install -c .libs/libhello.a /usr/local/lib/libhello.a
ranlib /usr/local/lib/libhello.a
burger#

ライブラリインストール時にBSD互換のinstallプログラムを使用する場合, `-s'(シンボルのstrip)フラグを指定すると安全です.libtoolは `-s' フラグを無視する,またはライブラリからデバッグとコンパイラシ ンボルのみをstripするプログラムを実行します.

ライブラリを一度配置すると,使用する前に必要な追加のコンフィグレーショ ンを行います.最初に,ビルド時に使用した`-rpath'フラグと同じ場所 に,ライブラリが実際にインストールされていることを確かめる必要がありま す.

そして,`libtool -n --mode=finish libdir'を実行すると,行う ことのヒントが与えられるはずです(see section フィニッシュモード).

 
burger# libtool -n --mode=finish /usr/local/lib
PATH="$PATH:/sbin" ldconfig -m /usr/local/lib
-----------------------------------------------------------------
Libraries have been installed in:
   /usr/local/lib

To link against installed libraries in a given directory, LIBDIR,
you must use the `-LLIBDIR' flag during linking.

 You will also need to do 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 `-RLIBDIR' linker flag

See any operating system documentation about shared libraries for
more information, such as the ld and ld.so manual pages.
-----------------------------------------------------------------
burger#

これらのステップを完了した後,インストールされたライブラリの使用開始が 可能になります.作成されたライブラリに依存する実行形式もインストールで きます.


3.6 実行形式のインストール

インストールされていないlibtoolライブラリに対して,実行形式をリンクす るためにlibtoolを使用した場合(see section 実行形式のリンク),ライブラリ をインストールした後に実行形式をインストールするため,libtoolを使用す る必要があります.

それでは,Ultrixの例を対象に,以下のように実行します.

 
a23# libtool install -c hell /usr/local/bin/hell
install -c hell /usr/local/bin/hell
a23#

共有ライブラリシステムでは,libtoolはラッパースクリプトを無視し,正し いバイナリをインストールします.

 
burger# libtool install -c hell /usr/local/bin/hell
install -c .libs/hell /usr/local/bin/hell
burger#

3.7 スタティックライブラリとのリンク

libtoolの旨味を知って,arranlibの愚かさへなぜ戻るので しょう?さて,決して共有されるはずがないスタティックアーカイブをつくる ことが望ましいときもあります.最もよくある状況として,複数の異なるプロ グラムをビルドするために使用する,オブジェクトファイルの集まりを持って いるときがあります.個々のプログラムに対し,すべてのオブジェクトファイ ルをリストアップする代わりに,それらのオブジェクトから"コンビニエンス ライブラリ"を作成し,ライブラリとプログラムをリンクすることが可能です. この技術は,他のディレクトリのライブラリへのリンクをサポートするので, 他のディレクトリのソースからビルドされるオブジェクトファイルをリンクす るサポートが欠けている,GNU automakeを補うためによく使用されます.この 制限は,リリース1.4までのGNU automakeに当てはまります.より新しいリリー スは,他のディレクトリのソースをサポートするでしょう.

このコンビニエンスライブラリとプログラムをリンクしたいだけの場合,完全 にlibtoolを無視し,古いarranlibコマンド(や,対応する GNU automake `_LIBRARIES'規則)が使用可能です.(おそらく使用したく はないでしょうが)libtoolを使用して,コンビニエンスライブラリをインストー ルすることさえ可能です.

 
burger$ libtool --mode=install ./install-sh -c libhello.a /local/lib/libhello.a
./install-sh -c libhello.a /local/lib/libhello.a
ranlib /local/lib/libhello.a
burger$

スタティックライブラリのインストールにlibtoolを使用すると,ライブラリ が(`-s'フラグを使用したインストーラの場合のように)偶然stripされる ことから守り,自動的に実行される正しいranlibコマンドと同様にな ります.

しかし,libtoolライブラリは単にオブジェクトファイルの集合以上です.そ れらは古いアーカイブにはない,ライブラリの依存情報も伝えることが可能で す.libtoolのスタティックなコンビニエンスライブラリを作成したい場合, スタティックライブラリのみに興味があることを示すため,`-rpath'フ ラグを省略し`-static'を使用することができます.そのようなスタティッ クライブラリとリンクするとき,libtoolは実際にすべてのオブジェクトファ イルと依存するライブラリをプログラムにリンクします.

`-rpath'と`-static'の両方を省略した場合,libtoolは,他の libtoolライブラリで,共有ライブラリの作成にすら使用可能なlibtoolのコン ビニエンスライブラリを作成します.スタティックな場合のように,ライブラ リは一組のオブジェクトファイルと依存するライブラリの別名として動作しま すが,この場合,オブジェクトファイルは共有ライブラリに含まれるほうが適 しています.しかし,直接または間接的に,単一のプログラムやライブラリに 単一のコンビニエンスライブラリをリンクしないように注意して下さい.さも なければ,シンボル再定義に関するエラーを得るでしょう.

GNU automakeを使用するとき,`-rpath'オプションがリンク時に渡され ないように,コンビニエンスライブラリに対するlib_LTLIBRARIESの代 わりにnoinst_LTLIBRARIESを使用した方が良いでしょう.

経験的に,最大一つのlibtoolライブラリにlibtoolのコンビニエンスライブラ リをリンクし,プログラムにはリンクしないようにしてください,そして, libtoolのコンビニエンススタティックライブラリを一つのプログラムにのみ リンクし,それは,ライブラリ依存情報をコンビニエンススタティックライブ ラリのユーザに伝えることが必要な場合のみにしてください.

スタティックなリンクが適している,その他の一般的な状況は,独立したバイ ナリを作成するときです.リンクにlibtoolを使用し,`-all-static'フ ラグを加えてください.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Akihiro Sagawa on June, 15 2005 using texi2html 1.70.