GNU Build Systemによるプログラム・パッケージ化の簡略化
configure
スクリプトとMakefileファイルを生成している人は少ない。その手順を見ていくことにしよう。
configure、Makefile.in、config.h.inという名前のビルド・ファイルは他のファイルから作成される。configureスクリプトはautoconf
プログラムによってconfigure.acファイルから作成される。Makefile.inファイルはautomake
プログラムによってMakefile.amファイルから作成され、config.h.inヘッダー・テンプレートはconfigure.acファイルの内容に基づいてautoheader
プログラムによって生成される。このヘッダー・ファイルには、存在するヘッダー・ファイル、存在しない関数などの設定を含め、設定サイクルの結果が含まれる。
プログラムではこのヘッダー・ファイルをインクルードして、特定のコンピュータ上でのそのプログラムの機能を判断することができる。
これは複雑なプロセスに見えるかもしれない。特に、configure
スクリプトが確認する内容の大部分は確認の必要がないことを考えると、何のために必要なのか疑問に思うだろう。また、生成されるMakefileファイルも複雑すぎるように見える。
ビルド・システムの背後にある原動力は移植性である。さまざまな処理の方法はプラットフォームによって異なる。しかし、これらの多様なすべてのシステムに対応するために何種類ものconfigureスクリプトとMakefileファイルを構成したくはないだろう。テストのために、これらすべてのシステムにアクセスすることが不可能なことも考えられる。他のプラットフォームについては心配せずにソフトウェアのコーディングに専念できるように、これらの保守を代わりに行ってくれるツールが必要だ。そこで役に立つのがAutoconfパッケージとAutomakeパッケージである。
Automakeでは、Makefile.amファイルを通してビルド要件を指定できる。Makefile.amファイルの構文はMakefileファイルよりも単純である。Automakeは、configure
スクリプトが最終的なMakefileファイルを生成するために使用するMakefile.inファイルを生成する。Makefile.inファイルには、まだインストール・ディレクトリなどは含まれていない。この種の情報はconfigure
スクリプトを実行したときに収集される。–prefixや–bindirなどのオプションは、最終?Makefileファイルの内容に影響する。
“Hello World”プログラムをコンパイルおよびインストールするには、Makefile.amファイルに次のようなコードを入力する。
bin_PROGRAMS = hello hello_SOURCES = hello.c
これに対してautomake
を実行する。すると、make all
、make install
、make clean
など、標準的なすべてのmake
ターゲットをサポートするMakefile.inファイルが生成される。このMakefile.inファイルは、configure
スクリプトに対する–prefixオプションで指定した場所にインストールされる(指定しなかった場合はデフォルトの/usr/local/bin)。
configureスクリプトはconfigure.acファイルから生成される。autoscan
プログラムを使用してこのファイルを作成および保守できる。autoscan
プログラムはソース・コード内をスキャンして、最終的に生成するconfigure
スクリプトが確認する一般的な移植性の問題を調べる。
Makefileファイルとconfigureスクリプトを自分で作成することもできるが、とても時間がかかるので現実的ではない。自分がアクセスできないプラットフォーム上でスクリプトを動作させる必要がある場合には、なおさらである。
実際の例
それでは、configureスクリプトとMakefileファイル、これらを生成するためのすべてのファイルを備えた摂氏から華氏への変換プログラムを作成してみよう。
まずはプログラムそのものを作成する。called ctof.cというファイルを作成し、次のコードを入力する。
#include "config.h" #ifdef HAVE_STDIO_H # include <stdio.h> #endif #ifdef HAVE_STDLIB_H # include <stdlib.h> #endif int main(int argc, char *argv[]) { if (argc!=2) { printf("No Celsius value given?n"); return 1; } #ifdef HAVE_STDLIB_H int celsius = atoi(argv[1]); #else printf("No stdlib.h present means no atoi() function.?n"); printf("Defaulting to 20 Celsius as the input value.?n"); int celsius = 20; #endif double fahrenheit; fahrenheit=(celsius * (9.0/5.0)) + 32; printf("%d Celsius = %.2f Fahrenheit?n", celsius, fahrenheit); return 0; }
これはとても単純なプログラムだ。コマンド・ライン引数として渡した数値を摂氏の入力値を表す整数に変換し、その値を華氏に変換する。
コマンド・ラインで渡した文字列を整数に変換する処理にはatoi()関数を使う。この関数を使用する前に、configure
スクリプトを実行したときにstdlib.hヘッダーが見つかったことを確認しなければならない。stdlib.hが見つからなかった場合は、atoi()関数は使わず、代わりに既定値の20を使う。stdlib.hが存在するかどうかをconfigure
が確認してくれるので、私たちが自分で確認する手間は省ける。もしconfigure
がなければ、この確認を行うコードを自分で書かなければならなかっただろう。1つの関数と1つのヘッダー・ファイルくらいならば問題ではないが、数百ものファイルがある場合はどうなるか考えてみよう。1つのファイルでも数百のファイルでも、autoscan
を1回呼び出すだけで、移植性に関するすべての問題に対してconfigure
に確認を行わせることができる。
次に、ビルド・プロセスを定義するMakefile.amファイルの作成に移る。この例の場合、手順は簡単だ。ctof.cをコンパイルし、ctof
バイナリ・プログラムを作成し、それをインストールする。このためには、次のようなMakefile.amを作成する。
bin_PROGRAMS=ctof ctof_SOURCES=ctof.c
必要なのはこの2行だけである。1行目は実行可能プログラム名を、2行目はプログラムを構成するソース・ファイルを定義している。
次にautoscan
を実行する。ソース・ファイルがスキャンされ、configure.acファイルの前身となるconfigure.scanというファイルが作成される。このファイルを使う前に、いくつか修正を加えなければならない。まず、次の行を探す。
AC_INIT(FULL-PACKAGE-NAME, VERSION, BUG-REPORT-ADDRESS)
そして、これを次のように修正する。
AC_INIT(ctof, 0.1, gerard@linuxfromscratch.org)
この行の意味は簡単に理解できるだろう。最初の引数はパッケージ名、2番目の引数はパッケージのバージョン、3番目の引数はバグを報告する電子メール・アドレスである。make dist
を実行する際には、パッケージ名とバージョンを組み合わせることによって、tarアーカイブのファイル名が作成される。この例の場合は、ctof-0.1.tar.gzというtarアーカイブが作成される。
AC_INIT行の下に次の行を追加する。
AM_INIT_AUTOMAKE
AM_INIT_AUTOMAKEはAutomakeを初期化する。ここでAutomakeの動作を微調整するオプションを追加し、Makefileファイルに含める機能を指定することができる。オプションを指定しないと、無難なデフォルトのオプション・セットが使われる。ほとんどの場合はこれで問題ない。例を1つ挙げると、次のようにしてdist-bzip2オプションを使うことができる。
AM_INIT_AUTOMAKE(dist-bzip2)
こうすると、make dist
を実行したときに、package-version.tar.gzファイルに加えてpackage-version.tar.bz2ファイルが作成される。
このファイルはこれで完成だ。変更内容を保存し、configure.scanファイルの名前をconfigure.acに変更する。
mv configure.scan configure.ac
automake
を実行する前に、automake
が使用するいくつかのファイルを作成しなければならない。これらは、NEWS、README、AUTHORS、ChangeLogである。次のコマンドでこれらを作成する。
touch NEWS README AUTHORS ChangeLog
後は、各コマンドを実行してconfigureファイル、Makefile.inファイル、config.h.in ファイルを生成するだけだ。
aclocal && autoheader && autoconf && automake --add-missing
aclocal
コマンドは、以前(configure.acに名前を変更する前に)configure.scanファイルに追加したAM_INIT_AUTOMAKEマクロを定義するaclocal.m4ファイルを生成する。autoheader
コマンドは、configure
スクリプトが見つかったヘッダーや利用できない関数などの情報を保存するために使用するconfig.hファイルを作成する。autoconf
コマンドはconfigureスクリプトを生成し、automake --add-missing
コマンドはMakefile.inファイルを生成する。また、ソース・ツリー内のdepcompスクリプト、install-shスクリプト、missingスクリプト、mkinstalldirsスクリプトへのシンボリック・リンクを作成する。これらのスクリプトは、プログラムがビルドおよびインストールされるときに使われる。
make dist
を実行すると、これらのシンボリック・リンクは参照先に実際のファイルに置き換えられる。これらのヘルパー・スクリプトが配布用tarアーカイブにコピーされるのは、他のシステムに既にAutoconfとAutomakeかインストールされているという事実に依存せずに、プログラムを動作させるためである。実際、適切な方法で作成したパッケージは、パッケージをビルドするホスト上のAutoconfパッケージとAutomakeパッケージの存在に依存しない。プログラムは、./configure && make && make install
を実行するだけでインストールできるようにすべきである。これは、より堅牢で移植性の高いアプリケーションを作成するのに役立つ。
これですべて準備が整ったので、プログラムをコンパイルおよびインストールできる。
./configure --prefix=/usr && make && make install
これによってctof
がコンパイルされ、/usr/binにインストールされる。–prefix=/usr構成オプションを省略すると、プログラムは/usr/local/binにインストールされる。プログラムをアンインストールするにはmake uninstall
を実行する。
すべて正しくビルドされたことを確認したら、GNU Build Systemの非常に便利な機能を使用して、ソース・ツリーを配布用にパッケージ化することができる。make dist
を実行してtarアーカイブを作成する。make clean
やmake distclean
を実行する必要はない。tarアーカイブには、configureスクリプト、Makefile.in、config.h.in、アプリケーションのソース・コードなど、本当に必要なファイルだけが含められる。コンパイル済みオブジェクト・ファイルや実行可能プログラム、ログ・ファイルなど、その他のファイルは含められない。
動作確認
それでは、GNU Build Systemの実際の動作を見てみよう。
configure
スクリプトを通常のシステムで実行すると、出力の一部に次の行が含まれる。
checking for stdlib.h... yes
これは、プログラムでatoi()関数を使用できることを意味する。
$ ./ctof -14 -14 Celsius = 6.80 Fahrenheit
stdlib.hヘッダー・ファイルのないシステムでconfigure
を実行すると、次の行が出力される。
checking for stdlib.h... no
これは、プログラムではatoi()関数を使用しないことを意味する。
$ ./ctof -14 No stdlib.h present means no atoi() function. Defaulting to 20 Celsius as the input value. 20 Celsius = 68.00 Fahrenheit
これで、プログラムはstdlib.hヘッダー・ファイルがあるシステムでもないシステムでも実行できる。このファイルが存在するかどうかを調べるために特別な作業を行う必要はない。すべてはGNU Build Systemによって処理される。
始めるには少し余分な作業が必要となり、実際に状況は少し複雑になる。作業を単純にしたければ、Makefile.amファイルをセットアップすればよい。これは、どのソース・ファイルがどの実行可能プログラムを構成するかを他のツールに伝えるためのファイルである。