autotools (autoconf, automake)能自动创建用来编译代码的各种文件(configure, makefile). 有了它,我们就不需要手动写makefile了,也不用在环境变化的时候,手动的去调整makefile. 这一切autotools可以完成。
下面以一个例子来说说明。
1. C代码。下面是一个EFL的简单程序,需要先安装好 EFL的各种库。
#include <Elementary.h> static void on_done(void *data, Evas_Object *obj, void *event_info) { // quit the mainloop (elm_run function will return) elm_exit(); } EAPI_MAIN int elm_main(int argc, char **argv) { Evas_Object *win, *box, *lab, *btn; // new window - do the usual and give it a name (hello) and title (Hello) win = elm_win_util_standard_add("hello", "Hello"); // when the user clicks "close" on a window there is a request to delete evas_object_smart_callback_add(win, "delete,request", on_done, NULL); // add a box object - default is vertical. a box holds children in a row, // either horizontally or vertically. nothing more. box = elm_box_add(win); // make the box horizontal elm_box_horizontal_set(box, EINA_TRUE); // add object as a resize object for the window (controls window minimum // size as well as gets resized if window is resized) elm_win_resize_object_add(win, box); evas_object_show(box); // add a label widget, set the text and put it in the pad frame lab = elm_label_add(win); // set default text of the label elm_object_text_set(lab, "Hello out there world!"); // pack the label at the end of the box elm_box_pack_end(box, lab); evas_object_show(lab); // add an ok button btn = elm_button_add(win); // set default text of button to "OK" elm_object_text_set(btn, "OK"); // pack the button at the end of the box elm_box_pack_end(box, btn); evas_object_show(btn); // call on_done when button is clicked evas_object_smart_callback_add(btn, "clicked", on_done, NULL); // now we are done, show the window evas_object_show(win); // run the mainloop and process events and callbacks elm_run(); return 0; } ELM_MAIN()
2. configure.ac
这是 autoconf 输入文件,因此有人也写为 configure.in(一推荐不要这样命名,因为 make dist-clean 的时候,会执行 rm -rf *.in,导致被误删).
这个文件里面通常做一些测试,比如,检查某些函数或者文件是否存在。比如,如果要检查函数 gettimeofday() 是否存在,可以用:
AC_CHECK_FUNCS(gettimeofday)。
下面是这个例子的文件内容:
AC_INIT(myapp, 0.0.0, [email protected]) AC_PREREQ(2.52) AC_CONFIG_SRCDIR(configure.ac) AM_CONFIG_HEADER(config.h) AC_PROG_CCsuo AM_INIT_AUTOMAKE(1.6 dist-bzip2) PKG_CHECK_MODULES([ELEMENTARY], elementary) AC_OUTPUT(Makefile)
解释如下:
AC_INIT: autoconf 的一个宏, 三个参数分别为:包的名字, 版本,和联系邮件
AC_PREREQ: 确保 autoconf的版本至少为 version. 如果不满足条件,就报错
AC_CONFIG_SRCDIR: 这个宏用来检查参数表示的文件确实在代码目录下存在
AM_CONFIG_HEADER: 这个宏定义了一个头文件(config.h),里面通常是一些预编译的宏。
这个文件的输入文件是 config.h.in。因此,有时候也这样写:
AM_CONFIG_HEADER(config.h:config.in)
AC_PROG_CC: 定义C编译器。如果CC还没定义,就查找gcc, cc, 然后是其它的编译器,然后把CC 环境变量定义为找到的那个编译器。
AM_INIT_AUTOMAKE: 后面可以跟很多参数。在这里, 1.6表示automake版本至少要是1.6, dist-bzip2表示创建一个bzip2压缩格式的目标文件。
如果执行 make dist,就会生成这样的包。
PKG_CHECK_MODULES: 完整的格式如下:
PKG_CHECK_MODULES(prefix, list-of-modules, action-if-found, action-if-not-found)
prefix是用来作为那些保存有编译选项和库信息的变量的前缀。比如,就这个例子,ELEMENTART_CFLAGS, ELEMENTARY_LIBS
第二个参数为要检查是否存在的模块。
后面两个参数一般不需要写。
AC_OUTPUT:每个 autoconf 脚本都要以 这个宏结束。它创建一个config.status并且运行它。这个宏后面跟的参数也是要被创建的文件。
3. Makefile.am
这是 automake的输入文件。它和makefile的语法是一样的。automake会把 makefile.am 转换成 makefile.in.
AUTOMAKE_OPTIONS = 1.4 foreign MAINTAINERCLEANFILES = Makefile.in aclocal.m4 config.h.in configure depcomp install-sh missing INCLUDES = -I$(top_srcdir) bin_PROGRAMS = myapp myapp_SOURCES = main.c myapp_LDADD = @[email protected] myapp_CFLAGS = @[email protected]
解释如下:
AUTOMAKE_OPTIONS: automake的选项。 1.4表示版本至少要为1.4, foreign表示这个 package不遵循GNU的标准。标准GNU包会发布一些
额外的文件,比如,ChangeLog, AUTHORS。选择了foreign之后,即使没有这些文件,automake也不会警告。
MAINTAINERCLEANFILES
是一个make承认的变量,它保存了可以被删除的一些中间文件。比如,执行下面的命令
make maintainer-clean
这个变量指示的文件就被删除。
top_srcdir:用在#include里面,表示项目所在的最顶端目录(top-most directory).
automake会把#include 展开到makefile.in里面。
bin_PROGRAMS: _PROGRAMS表示后面的文件是makefile要编译连接出来的。bin表示编出的文件要安装在 bindir目录下
myapp_LDADD和 myapp_CFLAGS表示用到的库和头文件。也可以这样写:
myapp_LDADD = $(ELEMENTARY_LIBS) myapp_CFLAGS = $(ELEMENTARY_CFLAGS)
4. autogen.sh
#!/bin/sh echo "Running aclocal..." ; aclocal $ACLOCAL_FLAGS || exit 1 echo "Running autoheader..." ; autoheader || exit 1 echo "Running autoconf..." ; autoconf || exit 1 echo "Running automake..." ; automake --add-missing --copy --gnu || exit 1 ./configure "[email protected]"
执行 aclocal 会生成 aclocal.m4 aclocal 是需要执行的,它会收集automake需要用到一些 autoconf的宏(根据当前的configure.ac).
aclocal会扫描 *.m4文件,然后是 configure.ac文件,把所有用到宏(包括间接依赖用到的)放到 aclocal.m4文件里面。
autoheader是为了把编译配置过程中产生的宏定义都放到一个文件里面(AC_CONFIG_HEADER定义的那个对应的输入文件,config.h.in)
autoheader产生 cponfig.h.in, 然后 configure 根据 config.h.in 生成 config.h.
autoconf 根据 configure.ac生成 configure.
automake 根据 makefile.am生成 makefile.in
现在,就可以开始配置和编译了。
首先,确保上面的几个文件已经准备好:
$ ls autogen.sh configure.ac main.c Makefile.am
然后,执行 autogen.sh:
$ ./autogen.sh Running aclocal... Running autoheader... Running autoconf... Running automake... configure.ac:5: installing './compile' configure.ac:6: installing './install-sh' configure.ac:6: installing './missing' Makefile.am:3: warning: 'INCLUDES' is the old name for 'AM_CPPFLAGS' (or '*_CPPFLAGS') Makefile.am: installing './depcomp' checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking whether gcc understands -c and -o together... yes checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes /bin/bash: /home/charles/missing: No such file or directory configure: WARNING: 'missing' script is too old or missing checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for style of include used by make... GNU checking whether make supports nested variables... yes checking dependency style of gcc... gcc3 checking for pkg-config... /usr/bin/pkg-config checking pkg-config is at least version 0.9.0... yes checking for ELEMENTARY... yes checking that generated files are newer than configure... done configure: creating ./config.status config.status: creating Makefile config.status: creating config.h config.status: executing depfiles commands
可以看到,此时,生成了很多文件:
p$ ls aclocal.m4 config.h configure main.c missing autogen.sh config.h.in configure.ac Makefile stamp-h1 autom4te.cache config.log depcomp Makefile.am compile config.status install-sh Makefile.in
执行configure:
$ ./configure checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking whether gcc understands -c and -o together... yes checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes /bin/bash: /home/charles/missing: No such file or directory configure: WARNING: 'missing' script is too old or missing checking for a thread-safe mkdir -p... /bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking for style of include used by make... GNU checking whether make supports nested variables... yes checking dependency style of gcc... gcc3 checking for pkg-config... /usr/bin/pkg-config checking pkg-config is at least version 0.9.0... yes checking for ELEMENTARY... yes checking that generated files are newer than configure... done configure: creating ./config.status config.status: creating Makefile config.status: creating config.h config.status: config.h is unchanged config.status: executing depfiles commands
执行 make:
$ make make all-am make[1]: Entering directory `/home/charles/code/EFL/test/tmp' gcc -DHAVE_CONFIG_H -I. -I. -pthread -D_REENTRANT -I/usr/local/include/elementary-1 -I/usr/local/include/elocation-1 -I/usr/local/include/efl-1 -I/usr/local/include/ecore-x-1 -I/usr/local/include/eina-1 -I/usr/local/include/eina-1/eina -I/usr/local/include/eet-1 -I/usr/local/include/evas-1 -I/usr/local/include/ecore-1 -I/usr/local/include/ecore-evas-1 -I/usr/local/include/ecore-file-1 -I/usr/local/include/ecore-input-1 -I/usr/local/include/edje-1 -I/usr/local/include/eo-1 -I/usr/local/include/ethumb-client-1 -I/usr/local/include/emotion-1 -I/usr/local/include/ecore-imf-1 -I/usr/local/include/ecore-con-1 -I/usr/local/include/eio-1 -I/usr/local/include/eldbus-1 -I/usr/local/include/efreet-1 -I/usr/local/include/emile-1 -I/usr/local/include/ector-1 -I/usr/local/include/ecore-input-evas-1 -I/usr/local/include/ecore-audio-1 -I/usr/local/include/ephysics-1 -I/usr/local/include/embryo-1 -I/usr/local/include/ecore-imf-evas-1 -I/usr/local/include/ethumb-1 -I/usr/local/include/eeze-1 -I/usr/include/glib-2.0 -I/usr/lib/i386-linux-gnu/glib-2.0/include -I/usr/include/libpng12 -I/usr/include/fribidi -I/usr/include/freetype2 -I/usr//include/luajit-2.0 -I/usr/include/bullet -I/usr/include/dbus-1.0 -I/usr/lib/i386-linux-gnu/dbus-1.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/uuid -g -O2 -MT myapp-main.o -MD -MP -MF .deps/myapp-main.Tpo -c -o myapp-main.o `test -f 'main.c' || echo './'`main.c mv -f .deps/myapp-main.Tpo .deps/myapp-main.Po gcc -pthread -D_REENTRANT -I/usr/local/include/elementary-1 -I/usr/local/include/elocation-1 -I/usr/local/include/efl-1 -I/usr/local/include/ecore-x-1 -I/usr/local/include/eina-1 -I/usr/local/include/eina-1/eina -I/usr/local/include/eet-1 -I/usr/local/include/evas-1 -I/usr/local/include/ecore-1 -I/usr/local/include/ecore-evas-1 -I/usr/local/include/ecore-file-1 -I/usr/local/include/ecore-input-1 -I/usr/local/include/edje-1 -I/usr/local/include/eo-1 -I/usr/local/include/ethumb-client-1 -I/usr/local/include/emotion-1 -I/usr/local/include/ecore-imf-1 -I/usr/local/include/ecore-con-1 -I/usr/local/include/eio-1 -I/usr/local/include/eldbus-1 -I/usr/local/include/efreet-1 -I/usr/local/include/emile-1 -I/usr/local/include/ector-1 -I/usr/local/include/ecore-input-evas-1 -I/usr/local/include/ecore-audio-1 -I/usr/local/include/ephysics-1 -I/usr/local/include/embryo-1 -I/usr/local/include/ecore-imf-evas-1 -I/usr/local/include/ethumb-1 -I/usr/local/include/eeze-1 -I/usr/include/glib-2.0 -I/usr/lib/i386-linux-gnu/glib-2.0/include -I/usr/include/libpng12 -I/usr/include/fribidi -I/usr/include/freetype2 -I/usr//include/luajit-2.0 -I/usr/include/bullet -I/usr/include/dbus-1.0 -I/usr/lib/i386-linux-gnu/dbus-1.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/uuid -g -O2 -o myapp myapp-main.o -L/usr/local/lib -lelementary -lefl -leina -lpthread -leet -levas -lecore -lecore_evas -lecore_file -lecore_input -ledje -leo -lethumb_client -lemotion -lecore_imf -lecore_con -leldbus -lefreet -lefreet_mime -lefreet_trash -leio -ldl -lm make[1]: Leaving directory `/home/charles/code/EFL/test/tmp'
References:
1. http://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Input.html
2.https://autotools.io/pkgconfig/pkg_check_modules.html
3.http://www.gnu.org/software/autoconf/manual/automake.html
4.https://www.sourceware.org/autobook/autobook/autobook_36.html
5.https://docs.enlightenment.org/stable/elementary/group__Start.html
6.http://www.delorie.com/gnu/docs/automake/automake_17.html
7. http://mij.oltrelinux.com/devel/autoconf-automake/
8. http://www.seul.org/docs/autotut/
9. http://inti.sourceforge.net/tutorial/libinti/autotoolsproject.html