autoconf和automake是啥?这要从类Unix系统的程序编译说起。一般一个真正的工程肯定不只一个文件,而多个文件的编译是件很麻烦的事情(最简单的就是用gcc或者g 后面接着多个文件),再加上要推出跨平台的(一般只是跨不同的类Unix平台),还有啥包依赖啊什么的,很麻烦嗯。于是就有了一个工具叫make,它接收一个名为Makefile的文件作为参数,自动地进行编译,还可以在Makefile里设置接受不同的选项,然后人们就可以make install、make all什么的了。
然而不同系统的编译要用不同的编译参数,但是开源软件不可能带多个Makefile,而且又难写,囧么办?于是有了configure脚本,它自动检测系统,并接受一个Makefile.in文件,根据它来生成Makefile。
然而confiure脚本和Makefile.in还是很难写,至少我不会写,而且看得也眼花。于是GNU推出了autoconf和automake,用于生成configure脚本和Makefile.in文件。其中autoconf是用来生成configure的,automake是用来生成Makefile.in的。
貌似说了很多废话。东西是拿来用的,不是拿来吹的,所以就拿来用吧。
首先看看autoconf怎么用。autoconf接受一个configure.ac的文件,根据里面的内容来生成configure脚本。怎么还有文件?嗯,总是要有输入的嘛,反正简单得多就是了(大型软件的configure.ac可能也挺复杂,不过总比自己写configure要来得轻松得多)。
现在一步一步来看看最简单的configure.ac里有些什么,首先是两句初始化命令:
AC_INIT(src/main.cpp)
AM_INIT_AUTOMAKE(main, 0.1)
这两句是所有configure.ac里都必须有的,AC_INIT的参数是你main函数所在的文件(包括路径),AM_INIT_AUTOMAKE指定了程序的名称和版本号。名称可以随意起,只要符合标识符规则就行。注意第二行的开头是AM而不是AC。
接下来定义程序语言,C语言用AC_PROG_CC,C 用AC_PROG_CXX,其他语言请自行搜索。
然后可以加一些其他选项了,我见过的是AC_PROG_INSTALL、PKG_CHECK_MODULES和AC_SUBST,目前对它们还没什么了解,先放一边。
如果要用到库(也就是有个目录的代码本身不是文件,是给其他目录include用的),就还要加上这么一句:
AC_PROG_RANLIB
最后指定要输出的Makefile文件,注意是每个有代码(或者有Makefile.am)的地方都要输出,不同的文件之间用空格隔开。大概类似这个样子:
AC_OUTPUT(Makefile src/Makefile)
这就是一个最简单的configure.ac文件了
再看看Makefile.am。
在你的代码所存放的地方要定义bin_PROGRAMS,如
bin_PROGRAMS = main
这个定义的就是编译好的可执行程序名。
然后就要定义程序文件,类似这个格式:
main_SOURCES = main.cpp a.h a.cpp
_SOURCES 前面的要与bin_PROGRAMS所定义的相一致,如果写的是bin_PROGRAMS = asdf,那么相应地要改为asdf_SOURCES。之后要包含该Makefile.am文件所在目录中所有的程序文件(当然,没用到的可以不包含),用空格隔开。不在这个目录下的(哪怕是在这个目录的子目录下)都不要包含进去。
那么子目录下的文件怎么办?我们可以用SUBDIRS指定子目录,如:
SUBDIRS = sub abc
相邻的子目录用空格分开。SUBDIRS里指定的每个子目录中都必须要有Makefile.am(相应地在configure.ac里也要加入到AC_OUTPUT中)。
要注意一点是bin_PROGRAMS定义的是一个可执行文件,也就是说之后的XXX_PROGRAMS中定义的文件必须有一个是有main函数的。
要是我们的子目录中的代码不是一个独立的程序,只是拿来给其他目录的程序include的怎么办?要用noinst_LIBRARIES来将它定义为库,如
noinst_LIBRARIES = lib.a
之后和定义了bin_PROGRAMS一样定义XXX_SOURCES,例如根据上面noinst_LIBRARIES所定义的,我们可以这样来定义:
lib_a_SOURCES = a.cpp a.h
注意“.”要改成“_”,也誻lib.a在作为XXX_SOURCES的头部时变成了lib_a
最后哪里引用了这些代码,要加入相应地语句。比方说,这个目录名字为sub,在它的上层目录要引用到这些代码,那么要在它的上层目录中的Makefile.am中加入这么一句:
LDADD = sub/lib.a
来指定调用了这个目录的代码(其实我们是把sub目录下的代码编译成了一个库,库文件为lib.a,LDADD就是指定要调用哪个库)。
最后,别忘了使用库时,要给configure.ac中加入AC_PROG_RANLIB
由于Makefile.am有多个,刚才的介绍可能有点乱,下面来总结一下Makefile.am是怎么放的:
- 根目录(configure.ac所在的目录)必须有一个Makefile.am
- 所有有需要编译的代码文件的目录下必须有一个Makefile.am
- 如果一个目录中有Makefile.am,那么必须在它的父目录中的Makefile.am里用SUBDIRS指定它
于是这两个文件大致介绍完了,当然,只是介绍了要编译一个简单程序的最基本、最通用的部分,没介绍的东西(也就是我现在还不懂的东西)多着呢。
在介绍如何使用这两个文件之前,首先要说说另一个工具:autoheader。它用来生成一个config.h文件,里面包含一些和平台相关的程序代码,供程序使用。也就是说,你可以在程序里include这个header.h,然后使用其中的某些定义好的常量什么的。由于我没用过,所以对它的介绍也就到此为止了。
现在到了使用这些文件的时候了。首先我们要运行的是autoheader(当然,如果不想要config.h的话,就跳过这一步吧)。
之后要运行的是aclocal,它的作用是生成autoconf所需要的一些宏的定义(也就是生成我们在configure.ac中所使用的扩展宏)。
然后运行autoconf。
之后理论上应该是automake,不过automake它还要求你必须有一些额外的文件,它们是:install-sh、missing、depcomp、INSTALL、NEWS、README、AUTHORS、ChangeLog、COPYING,它们是一个符合GNU规范的代码文件结构所必须的文件。automake可以通过附加--add-missing参数自动生成其中的一些文件(install-sh、missing、depcomp、INSTALL、COPYING),剩下的要自己创建。我们可以用touch命令来创建一个空文件,然后再运行automake。
之后就可以运行configure脚本来创建Makefile,再运行make来编译了。
总结一下,调用的命令应该是:
autoheader
aclocal
autoconf
automake
./configure
make
如果没有automake所需要的那些其他文件的话,应该是这样:
autoheader
aclocal
autoconf
touch NEWS README AUTHORS ChangeLog
automake --add-missing
./configure
make
可以没有autoheader(如果不需要它生成的config.h)的话
编译生成的可执行文件不一定是在程序的根目录的。若一个Makefile.am定义了bin_PROGRAMS,那么它所定义的bin_PROGRAMS相对应的可执行文件会与它在同一个目录下。
下面来看看一个简单的例子。程序总共有3个文件,分别是main.cpp、a.h和a.cpp,其中main.cpp在src目录下,a.h和a.cpp是main.cpp所需要引用的代码,位于src目录的sub子目录下。我们想为main.cpp生成名为hello的可执行文件。
首先在程序根目录下编写configure.ac文件:
AC_INIT(src/main.cpp)
AM_INIT_AUTOMAKE(hello, 0.1)
AC_PROG_CXX
AC_PROG_RANLIB
AC_OUTPUT(Makefile src/Makefile src/sub/Makefile)
然后是程序根目录下的Makefile.am文件:
SUBDIRS = src
之后是src目录下的Makefile.am:
bin_PROGRAMS = main
main_SOURCES = main.cpp
SUBDIRS = sub
LDADD = sub/lib.a
最后是src/sub目录下的Makefile.am:
noinst_LIBRARIES = lib.a
lib_a_SOURCES = a.cpp a.h
编译成功后,就可以在src目录下看到hello文件了
一些参考资料:
Using Automake and Autoconf with C
The Goat Book 中文版第五章
autoconf文档
automake文档...