automake & autoconf 続き
さて、例として簡単なQt/EmbeddedをリンクするDLLを作ってみることにする。組込み向けっぽくクロスコンパイルする。
ソース類はこんな感じ。
とても適当な感じだが、ようするに関数fooとhogeをexportするということである。
$ cat src/hoge.cpp
#include
#include "fuga.h"QPushButton *
hoge()
{
QPushButton *button = new QPushButton("hoge", 0);return button;
}$ cat src/foo.cpp
#include
#include "fuga.h"QPushButton *
foo()
{
QPushButton *button = new QPushButton("foo", 0);return button;
}$ cat src/include/fuga.h
#ifndef __FUGA_H__
#define __FUGA_H__//dummy
#define LIBNAME FOOHOGE
#ifdef __cplusplus
extern "C" {
#endifextern QPushButton *foo();
extern QPushButton *hoge();#ifdef __cplusplus
}
#endif#endif
初期状態でのディレクトリ構成はこのように。
$ find
.
./src
./src/foo.cpp
./src/include
./src/include/fuga.h
./src/hoge.cpp
上のツリーにはMakefileの雛形が全く含まれていない。当然必要となるので、Makefile.am、src/Makefile.amを書く
最終的にlibhogefoo.soを作りたいのである。
$ cat src/Makefile.am
lib_LTLIBRARIES = libhogefoo.la
libhogefoo_la_SOURCES = foo.cpp hoge.cpp
libhogefoo_la_CPPFLAGS = -Iinclude
トップディレクトリにはとりあえずこれだけを書いておく。
$ cat Makefile.am
SUBDIRS = src
Makefile.amの準備ができたらautoscanする。
autoscanすると、configure.scanができているのでconfigure.acの雛形として使う。failしているようだがとりあえず気にしない。
$ autoscan
autom4te: configure.ac: no such file or directory
autoscan: /usr/bin/autom4te failed with exit status: 1
$ ls
autoscan.log configure.scan src
configure.acに書き足す内容としては、
- automakeを使うので、AM_INIT_AUTOMAKEが、
- libtoolを使うので、AC_PROG_LIBTOOLが
それぞれ必要である。
$ cat configure.ac
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.AC_PREREQ(2.59)
AC_INIT(libfoo, 0.1, no-define)
AC_CONFIG_SRCDIR([src/foo.c])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_HEADER([config.h])# Checks for programs.
AC_PROG_CC
AC_PROG_LIBTOOL# Checks for libraries.
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
autoreconfでconfigureをgenerate!
configure.acを書き終えたら、autoheader、aclocal、libtoolize、automake、autoconfをそれぞれ走らせるわけ
だが、最近のautotoolsではautoreconfでまとめてやってくれるようだ。
これで一応の体裁は整った。しかし、当然ながらこれだとQt/Embeddedを探しに行ってくれない。ということで以下をconfigure.ac
$ autoreconf --install
configure.ac: installing `./install-sh'
configure.ac: installing `./missing'
src/Makefile.am: installing `./compile'
src/Makefile.am: installing `./depcomp'
$ ls
Makefile.am Makefile.in aclocal.m4 autom4te.cache autoscan.log compile
config.guess config.h.in config.sub configure configure.ac
configure.scan depcomp install-sh ltmain.sh missing src
に追加する。
--with-qte=[Qteの場所]で、Qt/Embeddedのインストールされている場所を指定することにする。
# Checks for libraries.
AC_ARG_WITH([qte],
[AS_HELP_STRING([--with-qte=/path/to/qte], [point to location where Qte exists])],
[LDFLAGS=-L$withval/lib
CFLAGS="$CFLAGS -I$withval/include -DQT -DQWS -fno-rtti -D_GNU_SOURCE"
CXXFLAGS="$CXXFLAGS -I$withval/include -DQT -DQWS -fno-rtti -D_GNU_SOURCE"
AC_CHECK_LIB([qte], [qt_get_screen], [],
[AC_MSG_ERROR([Qte was not found!])])],
[AC_MSG_ERROR([Qte is required!])])AC_ARG_WITH([qtthreaded],
[AS_HELP_STRING([--with-qtthreaded], [Using thread functions of Qt])],
[CFLAGS="$CFLAGS -DQT_THREAD_SUPPORT"
CXXFLAGS="$CXXFLAGS -DQT_THREAD_SUPPORT"],
[])
--with-qtthreadedでQteのスレッドサポートの可否も選べるようにしておく。
また、g++を使うのと、libtoolがstatic libraryも作るためにranlibとarを使うことから以下も追加。前述のように今回はクロスコンパイルなので、configure時にCC、CXXなどの環境変数を適宜設定する必要がある。
完成したconfigure.acはこのようになった。
AC_PROG_CXX
AC_PROG_RANLIB
AC_CHECK_TOOL([AR], [ar], [:])
configure.acに変更を加えた際には、autoreconfすると変更が反映され、configureが新しくなる。ビルドするにはまずconfigureスクリプトを実行するわけだが、今回はターゲットがpower pc linuxである。コンパイラにつくprefixはppc_405-である。(ppc_405-gcc、ppc_405-g++など)
$ cat configure.ac
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.AC_PREREQ(2.59)
AC_INIT(libfoo, 0.1, no-define)
AC_CONFIG_SRCDIR([src/foo.cpp])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_CONFIG_HEADER([config.h])# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
AC_PROG_RANLIB
AC_CHECK_TOOL([AR], [ar], [:])
AC_PROG_LIBTOOL# Checks for libraries.
AC_ARG_WITH([qte],
[AS_HELP_STRING([--with-qte=/path/to/qte], [point to location where Qte exists])],
[LDFLAGS=-L$withval/lib
CFLAGS="$CFLAGS -I$withval/include -DQT -DQWS -fno-rtti -D_GNU_SOURCE"
CXXFLAGS="$CXXFLAGS -I$withval/include -DQT -DQWS -fno-rtti -D_GNU_SOURCE"
MOC_CMD=$withval/bin/moc
AC_CHECK_LIB([qte], [qt_get_screen], [],
[AC_MSG_ERROR([Qte was not found!])])],
[AC_MSG_ERROR([Qte is required!])])
AC_SUBST([MOC_CMD])AC_ARG_WITH([qtthreaded],
[AS_HELP_STRING([--with-qtthreaded], [Using thread functions of Qt])],
[CFLAGS="$CFLAGS -DQT_THREAD_SUPPORT"
CXXFLAGS="$CXXFLAGS -DQT_THREAD_SUPPORT"],
[])
# Checks for header files.
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
./configureにはデフォルトでクロスコンパイルターゲットを指定する--hostオプションがあるが、config.subが知っているバリエーションでないとうまくいかない。
結局今回は
とした。
CC=ppc_405-gcc CXX=ppc_405-g++ RANLIB=ppc_405-ranlib AR=ppc_405-ar ./configure --host ppc-linux --build i586-pc-linux-gnu --with-qte=/opt/qt-2.3.2
automake & autoconf & libtoolではまった/はまりそうな点
AC_FUNC_MALLOC
ソース中にmallocがある場合、autoscanの結果、configure.acにはAC_FUNC_MALLOCが定義される。このAC_FUNC_MALLOCは、autoconfで展開された結果、mallocの挙動を調査するコードとなってconfigureに含まれる。
その調査の内容だが、malloc(0)がvalidなポインタを返すかどうかをチェックし、もしvalidなポインタを返さない場合には、mallocがrpl_mallocとしてdefineされてしまう。ついでにAC_LIBOBJにmallocが追加される。autoconfの意図としては「malloc.cにて、rpl_mallocという名前で正しい挙動をするmallocラッパーを実装せよ」ということなんだろうが、Makefileを書くのが面倒だからautotoolsを使っている身分としては大きなお世話である。ということで、そもそもmallocに0なんか渡らないと確信できる現実主義者はAC_FUNC_MALLOCをconfigure.acから削ってよし。
Conditional Sources for libraries
サウンド関係など、プラットフォームによって差異が大きいものは、#ifdefで分けるよりもファイルをプラットフォーム毎に用意した方がすっきりする。こういう場合、automakeのマニュアルにはEXTRA_hogehoge_SOURCESを使うといいよと書いてある。
これをDLLのビルドに当てはめた場合は以下のように記述されている。
8.1.3.1 Conditional compilation using _LDADD substitutionsAutomake must know all the source files that could possibly go into a program, even if not all the files are built in every circumstance. Any files that are only conditionally built should be listed in the appropriate EXTRA_ variable. For instance, if hello-linux.c or hello-generic.c were conditionally included in hello, the Makefile.am would contain:
bin_PROGRAMS = hello
hello_SOURCES = hello-common.c
EXTRA_hello_SOURCES = hello-linux.c hello-generic.c
hello_LDADD = $(HELLO_SYSTEM)
hello_DEPENDENCIES = $(HELLO_SYSTEM)You can then setup the `$(HELLO_SYSTEM)' substitution from configure.ac:
...
case $host in
*linux*) HELLO_SYSTEM='hello-linux.$(OBJEXT)' ;;
*) HELLO_SYSTEM='hello-generic.$(OBJEXT)' ;;
esac
AC_SUBST([HELLO_SYSTEM])
...In this case, the variable HELLO_SYSTEM should be replaced by either hello-linux.o or hello-generic.o, and added to both hello_DEPENDENCIES and hello_LDADD in order to be built and linked in.
さて、EXTRA_libhello_la_SOURCESの方法を使ったとしよう。ここで、hello-common.cもhello-linux.cもhello-generic.cもincludeディレクトリにあるhello.hをincludeする場合にはこのようにMakefile.amを書くことになる。
8.3.4 Libtool Libraries with Conditional SourcesConditional compilation of sources in a library can be achieved in the same way as conditional compilation of sources in a program (see Conditional Sources). The only difference is that _LIBADD should be used instead of _LDADD and that it should mention libtool objects (.lo files).
So, to mimic the hello example from Conditional Sources, we could build a libhello.la library using either hello-linux.c or hello-generic.c with the following Makefile.am.
lib_LTLIBRARIES = libhello.la
libhello_la_SOURCES = hello-common.c
EXTRA_libhello_la_SOURCES = hello-linux.c hello-generic.c
libhello_la_LIBADD = $(HELLO_SYSTEM)
libhello_la_DEPENDENCIES = $(HELLO_SYSTEM)And make sure configure defines HELLO_SYSTEM as either hello-linux.lo or hello-generic.lo.
Or we could simply use an Automake conditional as follows.
lib_LTLIBRARIES = libhello.la
libhello_la_SOURCES = hello-common.c
if LINUX
libhello_la_SOURCES += hello-linux.c
else
libhello_la_SOURCES += hello-generic.c
endif
configure.ac側は普通に考えると以下のようになるだろう。LIBTOOLを使うのでオブジェクトの拡張子は.loである。
lib_LTLIBRARIES = libhello.la
libhello_la_SOURCES = hello-common.c
libhello_la_CPPFLAGS = -Iinclude
EXTRA_libhello_la_SOURCES = hello-linux.c hello-generic.c
libhello_la_LIBADD = $(HELLO_SYSTEM)
libhello_la_DEPENDENCIES = $(HELLO_SYSTEM)
これでやってみるとなぜかうまくいかない。hello-generic.cをコンパイルする際に-IincludeがCPPFLAGSから抜け落ちるのだ(筆者の環境はautomake 2.59)。生成されたMakefileをよーく眺めてみると、hello-generic.loではなくてlibhello_la-hello-generic.loに対するルールが書かれているではないか!
...
case $host in
*linux*) HELLO_SYSTEM='hello-linux.lo' ;;
*) HELLO_SYSTEM='hello-generic.lo' ;;
esac
AC_SUBST([HELLO_SYSTEM])
...
と、いうことでconfigure.acでの正しい記述は以下のようになる。
libhello_la-hello-generic.lo: hello-generic.c
if $(LIBTOOL) --tag=CC --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES)$(INCLUDES) $(libhello_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhello_la-hello-generic.lo -MD -MP -MF "$(DEPDIR)/libhello_la-hello-generic.Tpo" -c -o libhello_la-hello-generic.lo `test -f 'hello-generic.c' || echo '$(srcdir)/'`hello-generic.c; \
then mv -f "$(DEPDIR)/libhello_la-hello-generic.Tpo" "$(DEPDIR)/libhello_la-hello-generic.Plo"; else rm -f "$(DEPDIR)/libhello_la-hello-generic.Tpo"; exit 1; fi
しかしちょっとこれはいただけない(それ以前に非常に面倒だ)。たとえば、HELLO_SYSTEMの値を異なるDLLをビルドする二つのMakefile.amで参照しようとしたら破綻する。さらに、このようにlibhello_la-というprefixがつくという動作がどこまで規定されたものかがよくわからない。libtoolのマニュアルに書いてあるのかもしれないが。。面倒なことを気にしたくないのであれば、DLLのビルドの場合はAutomake conditionalsを使用するほうが吉であるようだ。
...
case $host in
*linux*) HELLO_SYSTEM='libhello_la-hello-linux.lo' ;;
*) HELLO_SYSTEM='libhello_la-hello-generic.lo' ;;
esac
AC_SUBST([HELLO_SYSTEM])
...
8.1.3.2 Conditional compilation using Automake conditionalsAn often simpler way to compile source files conditionally is to use Automake conditionals. For instance, you could use this Makefile.am construct to build the same hello example:
bin_PROGRAMS = hello
if LINUX
hello_SOURCES = hello-linux.c hello-common.c
else
hello_SOURCES = hello-generic.c hello-common.c
endifIn this case, configure.ac should setup the LINUX conditional using AM_CONDITIONAL (see Conditionals).