Autotools

上篇,我们了解了 Makefile 的基本用法,但是要写一个能实际应用的 Makefile 还是一件很繁琐的事情,于是就有 automake 配合 autoconf 这套 autotools 工具来帮我们自动产生 Makefile。

Automake 是一个由 Makfile.am 产生 Makefile.in 的自动化工具,每一个 Makefile.am 都定义了一系列的宏。Automake 需要 perl 来产生 Makefile.in。

最开始的时候,我们手写 Makelfile 来实现程序的编译链接。但是当程序需要在不同的平台上运行时,Makefile 就需要根据平台要求来改变,比如在别的平台上编译器就不再叫 gcc(cc)了,或许是叫其他的名字,即使名字相同,要实现编译所需的选项也可能不同。1991 年,David J.Majenzie 写了一个叫做 configure 的脚本,根据环境的不同自动的产生 Makefile。也因此,现在我们编译一个包,之需要简单的执行 ./configure && make。

上面的这个过程被 GNU 项目所规范,以便更好的被使用。而我们就称这一整套的编译过程叫做 GNU Build System。目前绝大多数的包的编译都会采用该系统,遵循规范。

我们来看看最普遍的包安装方式,假设该包叫做 amhello-1.0.tar.gz:

$ tar zxvf amhello-1.0.tar.gz
$ cd amhello-1.0
$ ./configure
$ make
$ make check
$ sudo make install
$ make installcheck

首先解压缩,进入目录,执行 configure 脚本,该脚本会侦察系统的各种环境,最终产生 Makefile。接着执行 make 来构建包所需的程序,库以及脚本等。make check 会在正式安装之前进行一些检测,该步骤不是必须的。当所有的都已经准备好后,就可以安装了,这也意味着将程序,库文件,头文件,脚本以及其他的数据文件从 当前的源目录复制到他们的目的目录。默认的,所有的文件都会安装到 /usr/local/ 目录下:二进制文件会到 /usr/local/bin/ 下;库文件会到 /usr/local/lib/ 下。这些目录通常需要有 root 权限,所以在 make install 之前需要将当前的用户提升至 root,上面的程序会将 hello 程序拷贝至 /usr/local/bin/ 下。最后一个可选的命令 make installcheck 会对安装的文件进行测试。make check 会对源代码树进行测试而 make installcheck 会对已经安装的文件进行测试,不过该命令发挥的作用微乎其微。

到目前为止,我们已经运行了 4 种 make 的程序了: make,make check,make install,make installcheck。check,install,installcheck 作为 make 的选项被传递,也就是我们上文提到的目标(target),而 make 只是 make all 的简写,也就是默认的选项。下面是 GNU Coding Standard 所指定的一些常用选项:

make all:跟单独使用 make 一样,构建程序,库已经文档等
make clean:清除所有的可执行文件以及目标文件(.o)
make distclean:除了进行 make clean 的步骤,还会将 Makefile 删除,也就是说会删除一切由 ./configure 产生的文件
make install:安装可执行文件,说白了就是将在当前目录下产生的二进制文件以及一些库文件,man 文档复制到指定的目录中去
make uninstall:卸载文件,删除已安装的文件,这个需要在之前安装的目录下执行
make dist:将相应的文件打包压缩成 package-version.tar.gz 的格式
make distcheck:先检查再打包

GNU Coding Standards 还规定了安装程序目录的一些层次结构:

目录变量         默认值
prefix               /usr/local
exec_prefix      ${prefix}
bindir              ${exec_prefix}/bin
libdir               ${exec_prefix}/lib

includedir       ${prefix}/include
datarootdir     ${prefix}/share
datadir            ${datarootdir}
mandir            ${datarootdir}/man
infodir             ${datarootdir}/info
docdir             ${datarootdir}/doc/${PACKAGE}

上面定义个每一个目录都有自己的角色,比如上面的 amhello-1.0 程序,最终的可执行文件 hello 会安装到 bindir 下,默认就是 /use/local/bin/ 下。但是可以在 ./configure 的时候通过 –prefix 这个选项来修改 prefix 这个变量:

$ ./configure –prefix ~/usr
$ make
$ sudo make install

上面的会将最后所产生的文件安装到 ~/usr/ 下,也就是 hello 会被复制到 ~/usr/bin/hello 而 README 到 ~/usr/doc/amhello/README。

GNU Build System 还提供了在安装之前自动修改最终可执行文件名称的方法。这个对于一个系统中有多套同样的工具但是版本不同很有帮助。比如你想将 tar 安装为 gtar,这样可以跟原先系统中有的 tar 区分开来,通过下面的这三个参数中任意一个可以实现:

–program-prefix=PREFIX
–program-suffix=SUFFIX
–program-transform-name=PROGRAM

下面的命令会将 hello 安装为 test-hello:

$ ./configure –program-prefix test-
$ make
$ sudo make install

GNU Build System 的 make install/uninstall 可能并不能满足一个经常需要在各个不同主机上配置升级软件的系统管理员的需求,毕竟 GNU Build System 并不是包管理器。而 DESTDIR 可以比较好的解决此类问题。该变量会产生一个中间过程,后面需要接一个目录的名称,从该目录中可以很轻松得知哪些文件被安装在哪些目录:

$ ./configure –prefix /usr
$ make
$ sudo make DESTDIR=~/inst install
$ cd ~/inst
$ find . type f -print > ../files.list

从 files.list 中,我们可以看到文件被安装的路径。

接下来,我们会建立一个 hello world 的程序,该程序比较小,只需要 5 个文件。我们将主目录放在 /mnt/lfs/hello/ 目录下。
scr/main.c 是 hello 的源文件,我们将其放在 /mnt/lfs/hello/src/ 目录下:

$ cat src/main.c
#include <config.h>
#include <stdio.h>

int main (void)
{
   puts ("Hello World!");
   puts ("This is " PACKAGE_STRING ".");
   return 0;
}

README 包括一些文档:

$ cat README
This is a demonstration package for GNU Automake. Type `info Automake' to read the Automake manual.

Makefile.am 和 src/Makefile.am 包括 automake 需要的一些东西:

$ cat src/Makefile.am
bin_PROGRAMS = hello
hello_SOURCES = main.c

$ cat Makefile.am
SUBDIRS = src

最后 configure.ac 包括 autoconf 需要的内容以产生 configure:

cat $ configure.ac
AC_INIT([amhello], [1.0], [bug-automake@gnu.org])
AM_INIT_AUTOMAKE([-Wall -Werror foreign])
AC_PROG_CC
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
        Makefile
         src/Makefile
         ])
AC_OUTPUT

现在可以执行了:

$ autoreconf –version
autoreconf (GNU Autoconf) 2.59
在执行前先看下 autoreconf 的版本

$ autoreconf –install
configure.ac: installing `./install-sh'
configure.ac: installing `./missing'
src/Makefile.am: installing `./depcomp'

autoreconf 命令产生了四个主要的文件: configure,config.h.in,Makefile.in 和 src/Makefile.im,后三者是接下来 configure 脚本要调用的模版。接下来执行:

$ ./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 gcc… gcc
checking for C compiler default output file name… a.out
checking whether the C compiler works… yes
checking whether we are cross compiling… no
checking for suffix of executables…
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 ANSI C… none needed
checking for style of include used by make… GNU
checking dependency style of gcc… gcc3
configure: creating ./config.status
config.status: creating Makefile
config.status: creating src/Makefile
config.status: creating config.h
config.status: executing depfiles commands

现在可以看到 Makefile,src/Makefile 和 config.h 了。最后执行:

$ make
make  all-recursive
make[1]: Entering directory `/mnt/lfs/hello'
Making all in src
make[2]: Entering directory `/mnt/lfs/hello/src'
if gcc -DHAVE_CONFIG_H -I. -I. -I..     -g -O2 -MT main.o -MD -MP -MF ".deps/main.Tpo" -c -o main.o main.c; \
              then mv -f ".deps/main.Tpo" ".deps/main.Po"; else rm -f ".deps/main.Tpo"; exit 1; fi
gcc  -g -O2   -o hello  main.o  
make[2]: Leaving directory `/mnt/lfs/hello/src'
make[2]: Entering directory `/mnt/lfs/hello'
make[2]: Nothing to be done for `all-am'.
make[2]: Leaving directory `/mnt/lfs/hello'
make[1]: Leaving directory `/mnt/lfs/hello'

$ src/hello
Hello World!
This is amhello 1.0.

$ make distcheck

=============================================
amhello-1.0 archives ready for distribution:
amhello-1.0.tar.gz
=============================================

上面就是一个完整的 autotools 的过程了。autoreconf 是一个能自行按顺序调用 autoconf,automake 以及一些列其他命令的脚本工具。现在要知道的就是 autoconf 会负责由 configure.ac 产生 configure,而 automake 会负责由 Makefile.am(s) 和 configure.ac 产生 Makefile.in(s)。

在执行完 autoreconf –install 后,我们会发现多了目录下多了几个文件:

1.其中 Makefile.in 和 configure.h.in 是模版文件
2.aclocal.m4 定义了被 configure.ac 使用的第三方宏。我们在使用 automake 时,除了 configure.ac 里面定义的宏之外,还需要一些额外的宏,这些额外的宏就由 aclocal 来产生。执行 aclocal 会生成 aclocal.m4。
3.depcomp,install-sh,missing 是在编译过程中辅助的工具文件
4.autom4.te.cache/ 目下产生的是一些 cache 文件

现在我们来看看上面 configure.ac 里面的内容:
该文件会被 autoconf(产生 configure)以及 automake(产生各种 Makefile.ins)读取。其包含了一系列的 M4 宏(MACROS),他们会展开成 shell 的代码以最终形成 configure 脚本。
以 AC_ 为前缀的宏是 autoconf 宏,而以 AM_ 为前缀的是 automake 宏。
configure.ac 的头两行会初始化 autoconf 和 automake,AC_INIT 这个宏里面包括包的名称,版本号以及联系方式这些信息。
AM_INIT_AUTOMAKE 是一些了 automake 的选项表示,比如这里的 -Wall 表示开启所有的警告,这样不会让你漏掉任何可能出错的信息;foreign 会让 automake 不要遵循 GNU 标准,也就意味着没有必要在当前的目录下建立诸如 ChangeLog,AUTHORS 等琐碎的信息。
AC_PROG_CC 会检查 C 编译器,使 configure 脚本搜索 C 编译器,给 CC 变量赋值。
AC_CONFIG_HEADERS 会让 configure 产生以 #define 开头的宏文件 config.h。
AC_CONFIG_FILES 宏声明了一系列会由 *.in 生成的文件。
最后,AC_OUTPUT 是 configure.ac 中的最后一个宏。
注意:configure.ac 必须以 AC_INIT 开始,以 AC_OUTPUT 结束。
详细的语法可以看 GNU 文档(12)。

现在我们来看看 src/Makefile.am:
xxx_PROGRAMS 表示要产生的最终二进制文件名;xxx_SOURCES 表示源文件。

最后看看 Makefile.am 这个文件:
SUBDIRS 列出了需要编译进去的目录

下面我们来看看 autotools 所包含的一些核心工具:

autoconf
autoconf:由 configure.ac 生成 configure
autoheader:由 configure.ac 生成 config.h.in
autoreconf:按顺序执行这里提到的工具
autoscan: 会扫描当前目录的源文件以产生 configure.scan,该文件包含了一些基本的宏定义。以它为模版,将其修改为 configure.ac 或者 configure.in。后者在较老的版本中更为普遍,前者能兼容后者。建议使用 configure.ac
autoupdate:升级 configure.ac 中特定的宏
autom4te:autoconf 中的核心,由它驱动 M4,实现上述工具所提供的功能,所有执行 configure.ac 文件的都会使用 autom4te

automake
automake:从 Makefile.am,configure.ac 生成 Makefile.in
aclocal:aclocal 是一个 perl 脚本,在使用 automake 时,除了 configure.ac 里面定义的宏,还需要另外的一些宏,此时就可以通过该工具来产生另外的一部分宏,其生成的 aclocal.m4 会被 autoconf 以及 automake  使用

这里是一个幻灯片,第 46 页,也就是 "Behind 'autoreconf'" 这一小节动态的展示了 autoreconf 这个工具的实现过程。强烈推荐!

上面我们使用 autoreconf –install 很简单的就生成了 Makefile,因为它是一系列工具的有序集合。如果我们不使用 autoreconf 而一步一步的来该怎么做了?

1.autocsan 生成 configure.scan
2.将 configure.scan 该名为 configure.ac,并对其做修改
3.执行 aclocal,由 configure.ac 产生 aclocal.m4 这个宏
4.执行 autoconf 由 aclocal.m4 以及 configure.ac 来产生 configure 脚本
5.执行 autoheader 由 configure.ac 以及 configure.ac 产生 config.h.in
6.建立 Makefile.am,并做适当的修改
7.执行 automake –add-missing,由 Makefile.am,configure.ac 以及 aclocal.m4 产生 Makefile.in
8.执行 ./configure 产生 Makefile
9.执行 make 进行编译

这里(123)是几篇写的比较详细的文档,推荐阅读。
这里是一张 autotools 产生的图片。这里(12)是几个幻灯片,首推前面的那个。

  • http://aenon.tumblr.com Aenon

    COPY走了哦!