前言
本文根据《Autotools - A Practioner's Guide to GNU Autoconf, Automake, and Libtool》第一章翻译整理,省略了部分语句。
正文
正如序言里所讲,GNU Autotools的目的是使得最终用户的生活变得简单,而不是维护者的。虽然如此,从长远来看,作为一个工程管理者,使用Autotools会使你的工作变得简单,尽管可能不是你所怀疑的理由。Autotools框架尽可能简单,给出它所提供的功能。Autotools的真正目的是双重的:它为你的用户服务,并且使你的工程令人难以置信地可移植---甚至在你从没测试过,安装过或构建过你的代码的系统上。
在这本书中,我会经常使用词语Autotools的,虽然你在GNU归档中找不到使用这个词的软件包。我用这个词表示下列三个GNU包,它们被社区认为是GNU编译系统中的一部分
> Autoconf: 用于为工程产生配置脚本;
> Automake: 用于简化创建一致性和功能性Makefile的过程;
> Libtools: 用于为共享库的可移植创建提供一个抽象;
其它构建工具,例如开源源码包CMake和SCons,尝试提供与Autotools相同功能的但是以一个更加用户友善的方式。但是,这些工具试图躲在图形用户界面和脚本构建者之后的功能,实际上最终使它们的功能变得较弱。
谁应该使用Autotools
如果你正在写目标机是Unix或Linux的开源软件,你应该绝对正使用GNU Autotools。即使是你在为Unix或Linux写专有软件,你将会极大地受益于它们。Autotools会提供你一个构建环境,允许你的工程成功地在你将来的版本或分支构建,使用几乎没有改变的构建脚本。这是有用的,甚至你只是打算针对单一的Linux发行版,因为,说实话,你真的无法提前知道你的公司将来是否会希望你的软件运行在其它平台上。
语言的选择
在决定是否使用Autotools,你编程语言的选择是另一个重要的因素。记住Autotools由GNU设计用于管理GNU工程。在GNU社区,有两个因素决定一种计算机编程语言的重要性:
> 有没有GNU包用这个语言编写
> GNU编译工具集是否支持这个语言
基于这两个标准,Autotools为下列语言提供原生支持(由原生支持,我的意思是Autotools会在这些语言中编译,链接,和运行源码级特性检查):
> C
> C++
> Objective C
> Fortran
> Fortran 77
> Erlang
因此,如果你想构建一个Java包,你可以配置Automake来这么做(如我们在第8和第9章所见),但你不能要求Autoconf来编译,链接,或运行基于Java的检查,因为Autoconf根本就不原生地支持Java。然而,你可以找到Autoconf宏(在后续章节中,我将更为详细地涉及)来提升Autoconf的能力,以管理用Java写的工程的配置过程。
开源软件开发者正积极地在gcj编译器和工具集方面进行工作,因此一些原生Java支持可能最终会被添加到Autoconf。但是截至到编写这本书时,gcj仍然有些不成熟,目前很少有GNU包用Java编写,因此问题对于GNU社区来讲仍然不是至关重要。
生成你的软件包编译系统
GNU Autotools框架包括三个主要的包:Autoconf,Automake,和Libtool。这些包中的工具可以产生依赖于实用软件的代码和来自gettext,m4,sed,make,和perl等其它包的功能。
对于Autotools,区分维护者的系统和最终用户的系统是重要的。Autotools的设计目的指明,一个Autotools产生的编译系统,应该紧紧依赖现成的并预先安装在最终用户机器上的工具。例如,维护者使用的机器创建一个分支需要Perl解释器,但是一个最终用户运行的机器从发行分支包构建产品并不需要Perl。
一个必然的结果是,最终用户的机器不需要安装Autotools,一个最终用户的系统只需要一个合理的符合POSIX标准版本的make和Bourne shell的一些变种,以执行产生的配置脚本。当然,任何包也会需要编译器,链接器,和其它被项目维护者认为必要的工具来讲原代码文件转换成可执行二进制程序,帮助文件和其它运行时资源。
如果你曾经下载,构建和安装来自tarball(一种以.tar.gz, .tgz, .tar.bz2或其它扩展的压缩归档文件)的软件,毫无疑问你知道整体的过程。它通常看上去像这样:
- $ gzip -cd hackers-delight-1.0.tar.gz | tar xvf -
- ...
- $ cd hackers-delight-1.0
- $ ./configure && make
- ...
- $ sudo make install
- ...
Autoconf
尽管配置脚本很长又很复杂,用户只需要在执行时指定一些变量。大多数变量是简单的选择,关于组件,特性,和选项,例如:编译系统在哪里可以找到库和头文件?最终产品我想安装在哪里?我想把哪些可选的组件构建进我的产品?
Autoconf产生的配置脚本提供了一个通用的选项集,这对所有运行在POSIX系统上的可移植软件项目都是重要的。这包括修改标准位置(一个我将在第2章中涉及的概念)的选项,也包括定义在configure.ac文件的项目特定选项(我将在第3章中讨论)。
Autoconf包提供多个程序,包括下面的:
> autoconf
> autoheader
> autom4te
> autoreconf
> autoscan
> autoupdate
> ifnames
autoconf
autoconf是个简单地Bourne shell脚本。它主要的功能是确保当前shell包含必要的功能来执行M4宏处理器(我将会在第3章讨论Autoconf的M4使用)。该脚本的剩余部分解析命令行参数,并执行autom4te。
autoreconf
autoreconf工具执行每个工程所要求的autoconf,automake和libtool软件包中的配置工具。autoreconf最大限度地减少报告时间戳,特性,和项目状态变化的重生量。它被写为试图巩固现有维护者编写的,基于脚本的工具,以正确的顺序运行所有需要的Autotools。你可以认为autoreconf是那种智能的Autotools引导工具。如果所有你有的是一个configure.ac文件,你可以与运行autoreconf来执行所有你需要的工具,以正确的顺序,从而使configure会被合适地生成。
autoheader
autoheader工具产生一种C/C++兼容的头文件模板,根据configure.ac里面的多种结构。这个文件通常被称为config.h.in。当最终用户执行configure,配置脚本根据config.h.in生成config.h。作为一个维护者,你将会使用autoheader来生成会包括在你的分支软件包中的模板文件。(我们会在第3章中详细研究autoheader)。
autoscan
autoscan程序为新的工程生成一个默认的configure.ac文件;它也可以检查一个存在的Autotools工程的缺陷和机会以作增强。(我们将会在第3章和第8章中详细套路autoscan)。autoscan对于一个使用不是基于Autotools的编译系统作为一个工程的起始点是非常有用的,但是它也可能在提示增强一个现有基于Autotools工程的功能也是有用的。
autoupdate
autoupdate工具用于升级configure.ac或模板(.in)文件,以匹配当前Autotools版本所支持的语法。
ifnames
ifnames程序是一个在通常情况下并未充分使用的小工具,它可以接受命令行中的源文件名列表,显示stdout设备上的C预处理器定义列表。这个工具被设计用于帮助维护者决定放入configure.ac和Makefile.am文件中的内容,以使得它们可移植。如果你的工程考虑写成某种程度上可移植,ifnames可以帮助你决定那些试图可移植的部分在你源码树中的位置,给出潜在的可移植定义的名字。
autom4te
autom4te工具是一种为M4的智能缓存包装器,被其它大多数Autotools工具所使用。autom4te缓存减少了相继工具访问configure.ac的时间多达30%。我并不想花费太多时间在autom4te(发音automate),因为它主要由内部的Autotools的使用。它工作的唯一标志是autom4te.cache目录会在你工程的顶层目录出现,在你运行autoconf或autoreconf之后。
携手合作
前面所列的工作中,autoconf和autoheader是项目维护者在生成configure脚本时唯一会直接使用的,autoreconf是开发者唯一需要直接执行的。图1-1显示了输入文件和autoconf与autoheader相互作用,产生相应的产品文件。
注意:在本书中我都会使用图1-1中的数据流图。黑色盒子代表Autotools软件包;白色盒子自代表生成对象。方角盒子是脚本;圆角盒子是数据文件。这里大多数标签的意义应该是明显的,至少有一个需要解释下:术语ac-vars是指Autoconf特定替换文本。很快我会解释框滴渐变的aclocal.m4盒子。
这套工具的主要任务是生成一个配置脚本,可以用来配置一个工程构建目录。这个脚本不会依赖Autotools它们自己;事实上,autoconf被设计为生成可以运行在所有类Unix平台和绝大多数Bourne shell变种上的配置脚本。这意味着你可以用autoconf生成一个配置脚本,然后可以在没有安装Autotools的机器上执行这个脚本。
autoconf和autoheader程序要么直接由用户执行,要么由autoreconf间接执行。它们从你工程中的configure.ac文件和多种符合Autoconf的M4宏定义文件中获取输入,使用autom4te维护缓存信息。autoconf生成一个叫configure的配置脚本,一种非常容易移植的Bourne shell脚本,使你的工程提供许多有用的配置能力。autoheader基于在configure.ac中的特定宏定义生成config.h.in模板。
Automake
Automake的工作是将你项目构建过程的简化规范转换成样板Makefile语法,始终在第一时间正常工作,并提供所有期望的标准功能。Automake创建的工程支持在GNU编码标准中定义的准则(在第2章中讨论)。
automake软件包以Perl脚本的形式提供下列工具:
> automake
> aclocal
automake
automake程序根据高层次的生成规范文件(称为Makefile.am)生成标准makefile模板(称为Makefile.in)。这些Makefile.am输入文件基本上只是常规的makefile。如果你在一个Makefile.am文件中只放入一些需要的Automake定义,你会得到一个包含几百行的参数化的make脚本Makefile.in。
如果你添加make语法到一个Makefile.am文件,Automake会将这个代码移动到由此而来的Makefile.in文件中功能上最正确的位置。实际上,你可以编写你的Makefile.am文件,因而所有包含的是普通的make脚本,由此生成的Makefile会很好的工作。这个直通功能给予你扩展Automake功能的能力,以适合你工程的特殊需求。
aclocal
在GNU Automake手册中,aclocal工具被记载为一种临时变通,为Autoconf的某种灵活性的缺乏。Automake扩展了Autoconf,通过添加一个宏扩展集,但是Autoconf设计时真没有考虑这种层次的扩展。
添加用户定义宏到一个Autoconf工程的最初记载方法是创建一个叫aclocal.m4的宏,把用户定义宏放在这个文件中,将此文件放在configure.ac相同的目录。Autoconf然后在处理configure.ac时,自动的包括这个宏文件。Automake的设计者发现这个扩展机制太有用了让人欲罢不能;然而,用户会被要求添加一个m4_include声明到一个可能是不必要的aclocal.m4文件中,为了包括Automake宏。由于这两种用户自定义的宏和M4本身被认为是先进的概念,这被认为是要求的话是过于苛刻的。
aclocal被设计用于解决这个问题---这个工具为工程生成一个aclocal.m4文件,其中同时包含用户定义宏和所有需要的Automake宏(Automake宏拷贝到这个文件,但是写在acinclude.m4中的用户定义宏仅仅是在文件结尾引用m4_include声明)。项目维护者现在添加用户定义宏到一个名为acinclude.m4的新文件,而不是直接添加到aclocal.m4。
为了使读者清楚Autoconf并不依赖Automake(也许因为有点固执),GNU Autoconf手册原来建议,在你添加Automake到一个存在的Autoconf工程时,将aclocal.m4重命名为acinclude.m4,这种方法仍然广泛使用。aclocal的数据流在图1-2中描述。
然而,Autoconf和Automake的最新文档同时指出整个范式已经过时。开发者现在应该指定一个包含M4宏文件集的目录。目前的建议是在工程根文件目录下创建一个称为m4的目录,添加到里面的宏作为单独的.m4文件。所有在此目录里的文件在Autoconf处理configure.ac之前,将会被收集到aclocal.m4中。
现在应该更加清楚知道,为何在图1-1中的aclocal.m4盒子不能决定它所应该的颜色。当你在不通过Automake和Libtool时使用它,你手写aclocal.m4。然而,当你通过Automake使用它,这个文件由aclocal工具生成,你在acinclude.m4或一个m4目录中提供工程特定的宏。
Libtool
你如何在不同的Unix平台上构建共享库,在不添加很多平台相关的条件代码到你的编译系统和源代码?这个问题是libtool软件包试图解决的。
在类Unix平台上,有相当显著的共同功能。然而,一个非常显著且必须做的不同是,共享库如何构建,命名和管理。有些平台命名它们的共享库libname.so,其它平台使用libname.a,或甚至是libname.sl,任然存在其它甚至不提供本地共享库。一些平台提供libdi.so来允许软件在运行时动态加载和访问库功能,然而其它还有提供不同机制的,一些平台完全不提供这种功能。
Libtool的开发者已经仔细考虑了所有这些差别。Libtool支持大量平台,不仅仅提供在makefile中隐藏库命名差异的Automake宏集,也提供一个可添加到程序的动态加载程序功能的可选库。这个功能允许维护者使它们的运行时和动态对象管理代码更加可移植。
libtool软件包提供下列程序,库,和头文件:
> libtool (程序)
> libtoolize (程序)
> ltdl (静态和动态库)
> ltdl.h (头文件)
libtool
libtool软件包附带的libtool shell脚本,是libtoolize为项目生成的自定义脚本的一个通用版本。
libtoolize
libtoolize shell脚本准备你的项目来使用Libtool。它生成一个通用libtool脚本的自定义版本,添加到你的工程目录。这个自定义脚本伴随着Automake生成的makefile附带在工程中,在合适的时间在用户系统上执行这个脚本。
ltdl, Libtool 的C语言 API
libtool软件包也提供ltdl库和相关的头文件,提供跨平台的一致性运行时共享对象管理。ltdl库可能会被静态地或动态地链接进你的程序,给予它们平台之间的一致性运行时共享库访问接口。
图1-3阐述了配置和构建工程的automake,libtool脚本和用于创建产品的输入文件之间的相互作用关系。
Automake和Libtool都是标准的可插拔选项,可用一些简单的宏调用添加到configure.ac。
构建你的软件包
作为维护者,你可能经常构建你的软件包,并且你也很可能相当熟悉你工程里的组件,架构和编译系统。然而,你应该确定你的用户的构建经验比你自己的少多了。一种方式是,在构建你的软件包时,给予你用户一个简单的,容易懂的方法来遵循。在下面的部分,我会给你展示由Autotools提供的构建方法。
运行configure
在运行Autotools后,留下了一个称为configure的shell脚本和一个或多个Makefile.in文件。这些文件旨在附带在你的项目发布分发包中。你的用户会下载这些软件包,解压,和在项目的顶层目录输入./configure && make。然后,configure脚本会从由automake创建的Makefile.in模板生成makefiles(称为Makfile),从由autoheader生成的config.h.in模板生成一个config.h头文件。
Automake生成Makefile.in模板而不是make文件,是因为没有make文件,你的用户无法运行make;你不希望它们在运行configure之前运行make,这一功能防止他们这么做。Makefile.in模板同你可能手写的make文件几乎是相同的,除非你没必要这么做。它们也做了很多不仅仅是大多是人会手写的代码。另一个不附带准备运行的make文件的原因是,它给了configure机会来直接插入平台字符和用户指定选项特性到make文件。这使得它们更加适合目标平台和最终用户构建偏好。
图1-4阐述了configure和为创建make文件和config.h头文件在配置过程所执行脚本之间的关系。
configure脚本和另一个称为config.status脚本之间有一个双向关系。你可能已认为configure脚本生成了你的make文件。但事实上,除了一个日志文件之外,configure唯一生成的文件是config.status。
configure被设计为指定用户系统上可用平台特点与特性,在configure.ac中所指定。一旦它具有这个信息,它会生成config.status,后者包含所有的检查结果,然后执行这个脚本。反过来,config.status脚本使用嵌入在里面的核对信息,生成平台特定的config.h和make文件,以及任何在configure.ac中所指定的实例化信息。
注意: 如图1-4中双向箭头所示,config.status也可以调用configure。当我们使用--recheck选项,config.status会调用configure,使用原来用于生成config.status的相同的命令行选项。
configure脚本也会生成一个称为config.log的日志文件,当一个configure在用户系统中执行失败时,其中会包含非常有用的信息。作为维护者,你可以用此信息来调试。config.log也会记录configure是如何被执行的。(你可以使用config.status --version来找到用于生成config.status的命令行选项。)这一特性可以是非常方便的,比如,当一个用户从一个长假回来时,忘记了他原来用于生成工程构建目录的选项。
注意:为了生成make文件和config.h头文件,只要在工程构建目录中输入./config.status。使用原本用于生成config.status文件的选项来生成输出文件。
在源码目录外构建
Autotools构建环境的一个鲜为人知的功能是,在一个工程源码树里面生成不是必须的。就是说,如果一个用户从一个目录执行configure,而不是工程源码目录,可以在一个孤立的构建目录里生成一个完全的构建环境。
在下面的例子中,用户下载doofabble-3.0.tar.gz,解压,和创建两个同级目录,称为doofabble-3.0.debug和doofabble-3.0.release。切换到doofabble-3.0.debug目录,执行configure脚本,使用一个相对路径,用一个doofabble特定调试选项,然后在同一目录运行make。最后,切换到doofabble-3.0.release目录,做相同的事,这一次,configure没有使能调试选项。
- $ gzip -dc doofabble-3.0.tar.gz | tar zxf -
- $ mkdir doofabble-3.0.debug
- $ mkdir doofable-3.0.release
- $ cd doofabble-3.0.debug
- $ ../doofabble-3.0/configure --enable-debug
- ...
- $ make
- ...
- $ cd ../dofable-3.0.release
- $ ../doofabble-3.0/configure
- ...
- $ make
- ...
运行make
最后,运行普通的make。Autotools的设计者去了很多麻烦来确保你不必使用任何特定的make版本或分支。图1-5揭示了在构建过程中生成的make文件同make之间的内部关系。
如你所见,make运行了几个生成的脚本,但是这些所有都是对make过程辅助的。生成的make文件包含在合适条件下执行这些脚本的命令。这些脚本是Autotools的一部分,它们要么是在你的软件包里附带,要么是由你的配置脚本生成。
安装最新的Autotools
如果你正运行一个Linux的变种,并且你已选择安装编译器和工具用于开发C语言软件,你很可能已经有Autotools的一些版本安装在你的系统上了。为确定你正使用的autoconf,automake和libtool的版本,仅仅打开一个终端窗口,键入下列命令:
- $ which autoconf
- /usr/local/bin/autoconf
- $ autoconf --version
- autoconf (GNU Autoconf) 2.65
- Copyright (C) 2009 Free Software Foundation, Inc.
- License GPLv3+/Autoconf: GNU GPL version 3 or later
- <http://gnu.org/licenses/gpl.html>, <http://gnu.org/licenses/exceptions.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law.
- Written by David J. MacKenzie and Akim Demaille.
- $
- $ which automake
- /usr/local/bin/automake
- $ automake --version
- automake (GNU automake) 1.11
- Copyright (C) 2009 Free Software Foundation, Inc.
- License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-
- 2.0.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law.
- Written by Tom Tromey <tromey@redhat.com>
- and Alexandre Duret-Lutz <adl@gnu.org>.
- $
- $ which libtool
- /usr/local/bin/libtool
- $ libtool --version
- ltmain.sh (GNU libtool) 2.2.6b
- Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
- Copyright (C) 2008 Free Software Foundation, Inc.
- This is free software; see the source for copying conditions. There is NO
- warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- $
如果你选择下载,构建和安装来自GNU网站的这些软件包的最新版本,你必须为它们做相同的事,因为automake和libtool软件包安装宏到Autoconf的宏目录中。如果你还没有安装Autotools,你可以使用下列命令安装来自GNU发布版软件源档案的它们。(如有必要,确保修改版本号)
- $ mkdir autotools && cd autotools
- $ wget -q ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.65.tar.gz
- $ gzip -cd autoconf* | tar xf -
- $ cd autoconf*
- $ ./configure && make all check
- ...
- $ su
- Password: ******
- # make install
- ...
- # exit
- $ cd ..
- $
- $ wget -q ftp://ftp.gnu.org/gnu/automake/automake-1.11.tar.gz
- $ gzip -cd automake* | tar xf -
- $ cd automake*
- $ ./configure && make all check
- ...
- $ su
- Password: ******
- # make install
- # exit
- $ cd ..
- $
- $ wget -q ftp://ftp.gnu.org/gnu/libtool/libtool-2.2.6b.tar.gz
- $ gzip -cd libtool* | tar xf -
- $ cd libtool*
- $ ./configure && make all check
- ...
- $ su
- Password: ******
- # make install
- ...
- # exit
- $ cd ..
- $
总结
在这章中,我们已阐述了Autotools的高层概要,来给你一个每个事物是如何联系在一起的感觉。我也已给你展示了当你构建由Autotools编译系统创建的发布版本的软件时,可遵循的方法。最后,我已讲明如何安装Autotools,如何知道你已安装的版本。
在第二章中,我们会短暂地远离Autotools,开始为一个称为Jupiter的玩具工程创建一个手工编码的编译系统。你会学习一个合理的编译系统的需求,并且你会熟悉Autotools原始设计之后的解释。有了这个背景知识,你会开始明白Autotools所做事情的缘由。我真的无法足够去强调:第2章是本书最重要的章节之一。