[ C++で開発 ]

Automakeでmakeする

GNU Automakeを使用すると、簡単な記述でMakefileを生成してくれます。類似ツールにimakeがありますが、imakeは事実上X Window Systemの一部となっており、Xアプリケーション用に調整されているので、非Xな環境やアプリケーション開発に使う際にはやや面倒です。

Automakeの超簡単原理

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の構築例

automakeを使ったビルド例をいくつかのパターンについて紹介します。

シンプルプログラム

一つの実行ファイルだけを作成します。ソースコードは1つのディレクトリ内だけに管理されています。

hello
  +----- src
          +--- hello.cc
          +--- hello.h
          +--- main.cc

設定ファイルの準備

Makefile.amを、プロジェクト直下のディレクトリとその下ソースファイルが置かれているディレクトリまで各階層毎に作成します。

configure.acをプロジェクト直下のディレクトリに作成します。ゼロから作成してもよいですが、autoscanコマンドを実行して雛型configure.scanを吐き出させて、これをファイル名変更して修正すると楽ができるでしょう。

hello/Makefile.am

このディレクトリではビルドするソースは存在しないので、存在するサブディレクトリを列挙します。

SUBDIRS = src
hello/src/Makefile.am

インストール対象であるプログラムhelloを作成します。プログラムhelloを構成するソースファイルを列挙します。

bin_PROGRAMS = hello
hello_SOURCES = Hello.cc Hello.h main.cc
hello/configure.ac

autoscanを実行します。すると、雛型configure.scanが生成されます。

hello$ autoscan
hello$
注記1
Cygwinのautoconfパッケージをインストールしていると、古いバージョンと新しいバージョンを切り替えるwrapperスクリプトが呼ばれます。まっさらな(configure.acが存在していない)状態で実行すると、wrapperスクリプトは古いバージョンと新しいバージョンとどちらを実行すべきか判断できず、以下のようなエラーを表示します。メッセージにあるディレクトリとは別になりますが、/usr/autotool/devel/bin/autoscanをフルパスで指定して実行します。
Cygwin:新規にautoscanを実行するとエラーが発生
hello$ autoscan
autoscan: Couldn't find configure.ac nor configure.in file
run /usr/auto*/bin/autoscan directly
hello$
Cygwi:autoscanの実パスを直接指定して実行
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の実施までの手順

雛型設定ファイルが配置完了したので、いよいよconfigure実行までの準備を行います。

autoheader実行

config.h.inを生成します。これはconfig.hの雛型ファイルで、./configure時に必要なファイルです。

hello$ autoheader
hello$
aclocal実行

aclocal.m4を生成します。これは・・・

hello$ aclocal
hello$
automake実行

各ディレクトリにある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$
autoconf実行

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

configure実行

実行する環境を調査して必要なコンパイラ、ライブラリ、ヘッダファイル、コマンドなどの有無を確認し、それに基づいて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

makeの実施

上のconfigure実行によって、Makefileが生成されます。ここで生成されたMakefileに記述されるターゲットのうち主要なものを以下に紹介します。

all
デフォルトのターゲット。プログラムのコンパイル・リンクを実施する
Makefile
Makefileを生成しなおす
tags
タグファイル(ctags/etags)を生成する
check
テストを実施する
install
インストールを実施する
uninstall
アンインストールを実施する
clean
ビルド時の中間生成物を削除する
distclean
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を、プロジェクト直下のディレクトリとその下ソースファイルが置かれているディレクトリまで各階層毎に作成します。

messag/Makefile.am

このディレクトリではビルドするソースは存在しないので、存在するサブディレクトリを列挙します。

SUBDIRS = src
message/src/Makefile.am

インストール対象であるライブラリmessageを作成します。ライブラリmessageを構成するソースファイルを列挙します。

lib_LIBRARIES = libmessage.a
libmessage_a_SOURCES = Message.cc Message.h
message/configure.ac

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の実施までの手順

雛型設定ファイルが配置完了したので、いよいよconfigure実行までの準備を行います。

autoheader実行

config.h.inを生成します。これはconfig.hの雛型ファイルで、./configure時に必要なファイルです。

message$ autoheader
message$
aclocal実行

aclocal.m4を生成します。これは・・・

message$ aclocal
message$
automake実行

各ディレクトリにある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$
autoconf実行

configureスクリプトを生成します。

hello$ autoconf
hello$

configure実行

実行する環境を調査して必要なコンパイラ、ライブラリ、ヘッダファイル、コマンドなどの有無を確認し、それに基づいて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$

makeの実施

いよいよプログラムのビルドを行います。

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

AutomakeのTips

コンパイルオプションの制御

ターゲットプログラムに特有のオプションを追加する

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)

参考文献・サイト

入門、チュートリアル

マニュアル