[ C++で開発 ]
GNU Automakeを使用すると、簡単な記述でMakefileを生成してくれます。類似ツールにimakeがありますが、imakeは事実上X Window Systemの一部となっており、Xアプリケーション用に調整されているので、非Xな環境やアプリケーション開発に使う際にはやや面倒です。
Automakeは、makeコマンドでビルドするための設定記述ファイルMakefileを、極めて簡単に記述するためのツールです。通常Makefileには、コンパイルする際の諸設定、リンクする際の諸設定、処理順序などを逐一記述しておかなくてはなりません。これは、よっぽどMakefileに慣れていないと苦痛ですし、慣れていても面倒な作業です。そこで、Makefileよりもずっと簡単な設定ファイルMakefile.amだけを記述しておいて、あとの細かな設定記述はツールにやらせてしまいます。ここで使用するツールというのが、Autoconf、Automake、Libtoolなのです。
トップディレクトリで作業 | ソースファイルのあるディレクトリで作業 |
---|---|
(Makefile.amの作成) ↓ [Makefile.am] ↓ (autoscan実行) ↓ [configure.scan生成] ↓ (configure.scanの修正) ↓ [configure.ac] ↓ (aclocal実行) ↓ (automake -a実行)-------- ↓ 設定ファイル[*1]が生成 ↓ (autoheader実行) ↓ [config.h.in生成] ↓ (autoconf実行) ↓ [configure生成] ↓ (./configure実行)------- ↓ 設定ファイル[*2]が生成 |
(Makefile.amの作成) ↓ [Makefile.am] | | | | | | | | | | | -------->+ ↓ [Makefile.in生成] | | | | | | | | | -------->+ ↓ [Makefile生成] |
[*1] Makefile.inの他、COPYING, INSTALL, decomp, install-sh, missing, mkinstalldirsなど
[*2] Makefile、config.statusなど
automakeを使ったビルド例をいくつかのパターンについて紹介します。
一つの実行ファイルだけを作成します。ソースコードは1つのディレクトリ内だけに管理されています。
hello +----- src +--- hello.cc +--- hello.h +--- main.cc |
Makefile.amを、プロジェクト直下のディレクトリとその下ソースファイルが置かれているディレクトリまで各階層毎に作成します。
configure.acをプロジェクト直下のディレクトリに作成します。ゼロから作成してもよいですが、autoscanコマンドを実行して雛型configure.scanを吐き出させて、これをファイル名変更して修正すると楽ができるでしょう。
このディレクトリではビルドするソースは存在しないので、存在するサブディレクトリを列挙します。
SUBDIRS = src |
インストール対象であるプログラムhelloを作成します。プログラムhelloを構成するソースファイルを列挙します。
bin_PROGRAMS = hello hello_SOURCES = Hello.cc Hello.h main.cc |
autoscanを実行します。すると、雛型configure.scanが生成されます。
hello$ autoscan hello$ |
hello$ autoscan autoscan: Couldn't find configure.ac nor configure.in file run /usr/auto*/bin/autoscan directly hello$ |
hello$ /usr/autotool/devel/bin/autoscan hello$ |
autoscanを実行して生成された雛型configure.scanを、configure.acに名前変更します。このファイルに追加記述を行います。今回追加修正したのは、AC_INITの引数をプロジェクト名hello、バージョン1.0、バグ報告先メールアドレスに書き換えた点とAM_INIT_AUTOMAKEを追加したことです。
-*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.57) AC_INIT(hello, 1.0, [bug-report@xxx.yyy.zz]) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_SRCDIR([src/Hello.cc]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CXX AC_PROG_CC # Checks for libraries. # Checks for header files. # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST # Checks for library functions. AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT |
ここまで設定ファイルを作成したら、プロジェクトディレクトリは次のようになっています。
hello +----- Makefile.am +----- configure.ac +----- src +--- Makefile.am +--- hello.cc +--- hello.h +--- main.cc
雛型設定ファイルが配置完了したので、いよいよconfigure実行までの準備を行います。
config.h.inを生成します。これはconfig.hの雛型ファイルで、./configure時に必要なファイルです。
hello$ autoheader hello$ |
aclocal.m4を生成します。これは・・・
hello$ aclocal hello$ |
各ディレクトリにあるMakefile.amからMakefile.inを生成します。また、インストール作業時に必要なスクリプトファイルを生成しています。
hello$ automake --add-missing --copy configure.ac: installing `./install-sh' configure.ac: installing `./mkinstalldirs' configure.ac: installing `./missing' src/Makefile.am: installing `./depcomp' hello$ |
configureスクリプトを生成します。
hello$ autoconf hello$ |
ここまで設定ファイルを作成したら、プロジェクトディレクトリは次のようになっています。
hello +----- Makefile.am +----- Makefile.in +----- aclocal.m4 +----- auto4te.cache/ +----- config.h.in +----- configure +----- configure.ac +----- decomp +----- install-sh +----- missing +----- mkinstalldirs +----- src +--- Makefile.am +--- Makefile.in +--- hello.cc +--- hello.h +--- main.cc
実行する環境を調査して必要なコンパイラ、ライブラリ、ヘッダファイル、コマンドなどの有無を確認し、それに基づいてconfig.hや各Makefileを作成します。
hello$ ./configure checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for g++... g++ checking for C++ compiler default output... a.exe checking whether the C++ compiler works... yes checking whether we are cross compiling... no checking for suffix of executables... .exe checking for suffix of object files... o checking whether we are using the GNU C++ compiler... yes checking whether g++ accepts -g... yes checking for style of include used by make... GNU checking dependency style of g++... gcc3 checking for gcc... gcc checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ANSI C... none needed checking dependency style of gcc... gcc3 checking for an ANSI C-conforming const... yes configure: creating ./config.status config.status: creating Makefile config.status: creating src/Makefile config.status: creating config.h config.status: executing depfiles commands hello$ |
ここでの、プロジェクトディレクトリは次のようになっています。
hello +----- Makefile +----- Makefile.am +----- Makefile.in +----- aclocal.m4 +----- auto4te.cache/ +----- config.h +----- config.h.in +----- config.log +----- config.status +----- configure +----- configure.ac +----- decomp +----- install-sh +----- missing +----- mkinstalldirs +----- src +--- .deps/ | +--- Hello.Po | +--- main.Po +--- Makefile +--- Makefile.am +--- Makefile.in +--- hello.cc +--- hello.h +--- main.cc
上のconfigure実行によって、Makefileが生成されます。ここで生成されたMakefileに記述されるターゲットのうち主要なものを以下に紹介します。
いよいよプログラムのビルドを行います。
hello$ make cd . && /bin/bash /work/hello/missing --run aclocal-1.7 cd . && \ /bin/bash /work/hello/missing --run automake-1.7 --foreign Makefile cd . && /bin/bash /work/hello/missing --run autoconf /bin/bash ./config.status --recheck running /bin/bash ./configure --no-create --no-recursion checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for g++... g++ checking for C++ compiler default output... a.exe checking whether the C++ compiler works... yes checking whether we are cross compiling... no checking for suffix of executables... .exe checking for suffix of object files... o checking whether we are using the GNU C++ compiler... yes checking whether g++ accepts -g... yes checking for style of include used by make... GNU checking dependency style of g++... gcc3 checking for gcc... gcc checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ANSI C... none needed checking dependency style of gcc... gcc3 checking for an ANSI C-conforming const... yes configure: creating ./config.status cd . && /bin/bash ./config.status Makefile config.status: creating Makefile cd . && /bin/sh /work/hello/missing --run autoheader touch ./config.h.in cd . && /bin/sh ./config.status config.h config.status: creating config.h make all-recursive make[1]: Entering directory `/work/hello' Making all in src make[2]: Entering directory `/work/hello/src' cd .. && \ /bin/bash /work/hello/missing --run automake-1.7 --foreign src/Makefile cd .. && /bin/bash ./config.status src/Makefile depfiles config.status: creating src/Makefile config.status: executing depfiles commands make[2]: Leaving directory `/work/hello/src' make[2]: Entering directory `/work/hello/src' if g++ -DHAVE_CONFIG_H -I. -I. -I.. -g -O2 -MT Hello.o -MD -MP -MF ".deps/He llo.Tpo" \ -c -o Hello.o `test -f 'Hello.cc' || echo './'`Hello.cc; \ then mv -f ".deps/Hello.Tpo" ".deps/Hello.Po"; \ else rm -f ".deps/Hello.Tpo"; exit 1; \ fi if g++ -DHAVE_CONFIG_H -I. -I. -I.. -g -O2 -MT main.o -MD -MP -MF ".deps/mai n.Tpo" \ -c -o main.o `test -f 'main.cc' || echo './'`main.cc; \ then mv -f ".deps/main.Tpo" ".deps/main.Po"; \ else rm -f ".deps/main.Tpo"; exit 1; \ fi g++ -g -O2 -o hello.exe Hello.o main.o make[2]: Leaving directory `/work/hello/src' make[2]: Entering directory `/work/hello' make[2]: Leaving directory `/work/hello' make[1]: Leaving directory `/work/hello' hello$ |
一つの静的リンク用アーカイブライブラリファイルを作成します。ソースコードは1つのディレクトリ内だけに管理されています。動的リンク用共有ライブラリファイルの作成は、コンパイル時にリロケータブルなコードを生成するようにオプションを追加する必要がありますが、これがコンパイラ種類によって違っていたりと大変なので、別途紹介します。
message +----- src +--- Message.cc +--- Message.h |
Makefile.amを、プロジェクト直下のディレクトリとその下ソースファイルが置かれているディレクトリまで各階層毎に作成します。
このディレクトリではビルドするソースは存在しないので、存在するサブディレクトリを列挙します。
SUBDIRS = src |
インストール対象であるライブラリmessageを作成します。ライブラリmessageを構成するソースファイルを列挙します。
lib_LIBRARIES = libmessage.a libmessage_a_SOURCES = Message.cc Message.h |
autoscanを実行します。すると、雛型configure.scanが生成されます。
message$ /usr/autotool/devel/bin/autoscan message$ |
autoscanを実行して生成された雛型configure.scanを、configure.acに名前変更します。このファイルに追加記述を行います。今回追加修正したのは、AC_INITの引数をプロジェクト名message、バージョン1.0、バグ報告先メールアドレスに書き換えた点とAM_INIT_AUTOMAKEを追加したこと、AC_PROG_RANLIBを追加したことです。
-*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.57) AC_INIT(hello, 1.0, [bug-report@xxx.yyy.zz]) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_SRCDIR([src/Hello.cc]) AC_CONFIG_HEADER([config.h]) # Checks for programs. AC_PROG_CXX AC_PROG_CC AC_PROG_RANLIB # Checks for libraries. # Checks for header files. # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST # Checks for library functions. AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT |
雛型設定ファイルが配置完了したので、いよいよconfigure実行までの準備を行います。
config.h.inを生成します。これはconfig.hの雛型ファイルで、./configure時に必要なファイルです。
message$ autoheader message$ |
aclocal.m4を生成します。これは・・・
message$ aclocal message$ |
各ディレクトリにあるMakefile.amからMakefile.inを生成します。また、インストール作業時に必要なスクリプトファイルを生成しています。
message$ automake --add-missing --copy configure.ac: installing `./install-sh' configure.ac: installing `./mkinstalldirs' configure.ac: installing `./missing' src/Makefile.am: installing `./depcomp' message$ |
configureスクリプトを生成します。
hello$ autoconf hello$ |
実行する環境を調査して必要なコンパイラ、ライブラリ、ヘッダファイル、コマンドなどの有無を確認し、それに基づいてconfig.hや各Makefileを作成します。
message$ ./configure checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for g++... g++ checking for C++ compiler default output... a.exe checking whether the C++ compiler works... yes checking whether we are cross compiling... no checking for suffix of executables... .exe checking for suffix of object files... o checking whether we are using the GNU C++ compiler... yes checking whether g++ accepts -g... yes checking for style of include used by make... GNU checking dependency style of g++... gcc3 checking for gcc... gcc checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ANSI C... none needed checking dependency style of gcc... gcc3 checking for ranlib... ranlib checking for an ANSI C-conforming const... yes configure: creating ./config.status config.status: creating Makefile config.status: creating src/Makefile config.status: creating config.h config.status: executing depfiles commands message$ |
いよいよプログラムのビルドを行います。
message$ make cd . && /bin/bash /work/message/missing --run autoheader touch ./config.h.in cd . && /bin/bash ./config.status config.h config.status: creating config.h make all-recursive make[1]: Entering directory `/work/message' Making all in src make[2]: Entering directory `/work/message/src' if g++ -DHAVE_CONFIG_H -I. -I. -I.. -g -O2 -MT Message.o -MD -MP -MF ".deps/ Message.Tpo" \ -c -o Message.o `test -f 'Message.cc' || echo './'`Message.cc; \ then mv -f ".deps/Message.Tpo" ".deps/Message.Po"; \ else rm -f ".deps/Message.Tpo"; exit 1; \ fi rm -f libmessage.a ar cru libmessage.a Message.o ranlib libmessage.a make[2]: Leaving directory `/work/message/src' make[2]: Entering directory `/work/message' make[2]: Leaving directory `/work/message' make[1]: Leaving directory `/work/message' message$ |
一つの実行ファイルを作成します。実行ファイルは、いくつかのライブラリを利用しています。それぞれのライブラリも同じプロジェクトの中で生成します。ソースコードはライブラリ・実行ファイルそれぞれごとのディレクトリ内に管理されています。
実行ファイルhellを作成します。ライブラリとして、message、personの2つを利用します。
hello +----- src +--- prog +--- message +--- person |
bin_PROGRAMS = hello hello_SOURCES = hello.cc : hello_CXXFLAGS = -Wold-style-cast -Weffc++ |
hello_CXXFLAGSには、ターゲットとしてhelloをコンパイルするときにコンパイルオプションを追加する指定を記述します。リンク時には反映されません。
bin_PROGRAMS = hello hello_debug hello_SOURCES = hello.cc hello_CXXFLAGS = -DNDEBUG hello_debug_SOURCES = hello.cc hello_debug_CXXFLAGS = -DDEBUG |
同じソースファイルから、デバッグ用ビルドの実行ファイルhello_debugとリリース用ビルドの実行ファイルhelloを生成します。デバッグ用コンパイルでは-DDEBUGを指定しています。リリース用コンパイルでは-DNDEBUGを指定しています。
ソースファイルの数が多いときは、別途変数にソースファイルリストを記述すればよいでしょう。
srcs = hello.cc world.cc welcome.cc hello_SOURCES = $(srcs) hello_debug_SOURCES = $(srcs)