我如何解开死结?
如果Autoconf需要GNU m4并且GNU m4还有一个Autoconf configure脚本,
我如何解开这个死结?它好像是一个类似于鸡和蛋的问题!
这实际上是一种误解。虽然GNU m4带有一个由Autoconf生成的configure脚本,但在运行脚本及安装GNU m4的时候并不需要安装Autoconf。只有在你需要修改m4的configure 脚本的时候,这只是少数几个人(主要是它的维护者)必须去作的事,才需要Autoconf。
为什么不使用Imake?
为什么不用Imake来代替configure脚本?
有些人已经提出了这个问题,所以在改编之后,我把给他们的解释写在这里。
下面是对Richard Pixley的问题的回答:
由Autoconf生成的脚本经常地在它以前从未设置过的机器上工作。这就是说,它善于推断新系统的配置。而Imake不能做到。
Imake使用含有主机特定数据的通用数据库。对X11来说,这种方法具有意义是因为发布版本是由一个控制整个数据库的总管机关管理的一组工具组成的。
GNU工具并不按这种方式发行。每个GNU工具都有一个维护者;这些维护者散布在世界各地。使用统一的数据库将使维护变成噩梦。 Autoconf可能成为这类数据库,但实际上它没有。不是列举主机的依赖性,它列举的是程序的需求。
如果你把GNU套件看作一组本地工具,那么问题就很相似了。但GNU开发工具可以作为交叉工具(cross tools)而在几乎所有主机+目标机的组合中进行配置。所有的这些配置都可以同时(concurrency)安装。它们甚至可以被配置成可以在不同主机上共享与主机独立的信息的形式。Imake不能处理这些问题。
Imake模板是标准的一种形式。GNU编码标准在没有强加相同的限制的情况下,解决了相同的问题。
下面是一些由Per Bothner撰写的进一步的解释:
Imake的一个长处是它易于通过使用cpp的`#include'和宏机制生成大的Makefile。然而,cpp是不可编程的:它含有有限的条件工具,而不含有循环。而且cpp不能检查它的环境。
所有这些问题可以通过使用sh而不是cpp来解决。shell是完全可编程的、含有宏替换、可以执行(或者编制)其它的shell脚本,并且可以检查它的环境。
Paul Eggert更详细地阐述:
使用Autoconf,安装者不必假定Imake自身已经被安装并且正常地工作了。这对于习惯使用Imake的人们来说,看起来不是突出的长处。但在许多主机上,并没有安装Imake或者缺省的安装不能很好地工作,为此,要求安装Imake就阻碍了在这些主机上使用由Imake配置的软件包。例如, Imake模板和配置文件可能不能适当地安装在一个主机上,或者Imake创建过程可能会错误地假定所有的源代码文件都在一个大目录树中,或者Imake 配置可能使用某个编译器而包或者安装器需要使用另一个编译器,或者包需要的Imake的版本号与系统支持的版本号不匹配。这些问题在Autoconf中很少出现,这是因为包附带属于它自己的独立配置处理器。
还有,Imake通常会在make和安装者的C预处理器之间遇到难以预期的影响。这里的基本问题是,C预处理器是为处理C程序而不是`Makefile' 而设计的。这对Autoconf来说问题小得多,它使用通用目的预处理器m4,并且包的作者(而不是安装者)以标准的方式进行预处理。
最后,Mark Eichin解释道:
Imake还不是完全可扩展的。为了把新特征添加到Imake中,你需要提供你自己的项目模板,并且复制已经存在的特征的主要部分。这意味着对于复杂的项目来说,使用由买主提供的(vendor-provided)Imake模板不能提供任何平衡作用--这是因为它们不包括你自己的项目的任何东西(除非它是一个X11程序)。
但是,另一方面:
一个Imake胜过configure的长处是: `Imakefile'总是趋向于比`Makefile.in'简短(同样地,冗余较少)。但是,这儿有一个修正的方法--至少对于Kerberos V5树来说,我们已经在整个树中进行了修改以调用通用的 `post.in'和`pre.in' `Makefile'片断。这意味着大部分通用的东西,即使它们通常是在configure中设置的,也不必复制。
从版本1中升级
Autoconf第2版基本上与第1版是向后兼容的。但是,它给出了作某些事的更好方法,并且不再支持版本1中一些丑陋的东西。因此,根据你的 `configure.in'文件的复杂性,你可能必须作一些手工的工作以升级到版本2。本章指出了一些在升级的时候需要注意的问题。还有,可能你的 configure脚本可以从版本2中的新特征中获得一些好处;在Autoconf发布包中的`NEWS'文件概括了改变的部分。
首先,确认你安装了1.1版或者更高版本的GNU m4,最好是1.3版或者更高版本。在1.1版之前的版本含有bug 以至于它不能与Autoconf版本2一同工作。版本1.3及其后的版本比早期的版本更快一些,这是因为1.3版的GNU m4 对转换(diversions)进行了更有效的实现并且能够在可以快速读回的文件中冻结(freeze)它的内部状态。
改变了的文件名
如果你随Autoconf一起安装了`aclocal.m4'(相对于特定软件包的源代码目录中的`aclocal.m4'),你必须把它重命名为`acsite.m4'。参见用autoconf创建configure。
如果你与你的软件包一同发布`install.sh',就把它重命名为`install-sh'以便make的内置规则不会无意地从该文件创建一个称为`install'的文件。AC_PROG_INSTALL将寻找这两个名字的脚本,但最好使用新名字。
如果你使用`config.h.top'或者`config.h.bot',你仍然可以使用它们,但如果你把它们混合到 `acconfig.h'之中,将减少你的麻烦。参见用autoheader创建`config.h.in'。
改变了的Makefile
在你的`Makefile.in'文件中添加`@CFLAGS@'、`@CPPFLAGS@'和`@LDFLAGS@',以便它们可以在configure运行的时候利用环境中的这些变量的值。这样做不是必须的,但对用户来说比较方便。
对于AC_OUTPUT的每个非`Makefile'的输入文件,你还应该添加一条含有 `@configure_input@'的注释,以便输出文件将会包含一条注释以说明它们是由configure生成的。自动地为每种人们在 AC_OUTPUT中输出的文件选择正确的注释语法需要做太多的工作。
把`config.log'和`config.cache'添加到你要在distclean目标中删除的文件的列表中。
如果你的`Makefile.in'如下:
prefix = /usr/local
exec_prefix = ${prefix}
你必须把它修改成:
prefix = @prefix@
exec_prefix = @exec_prefix@
不使用`@'字符的老式的对这些变量的替换行为已经被删除了。
改变了的宏
在Autoconf第2版中,重新命名了许多宏。你仍然可以使用旧名字,但新名字更清晰,并且易于找到相关文档。关于为旧宏名提供新宏名的列表,参见陈旧的宏名。用autoupdate程序转换你的`configure.in'以使用新的宏名。参见用autoupdate更新configure。
有些宏已经被能够更好地完成工作的类似宏所代替,但在调用上并不兼容。如果你在运行autoconf时受到了关于调用过时宏的警告,你可以安全地忽略它们,但如果你按照打印的建议替换过时的宏,你的configure脚本通常可以工作的更好。特别地,报告测试结果的机制已经改变了。如果你使用了echo 或者AC_VERBOSE(可能是通过AC_COMPILE_CHECK),如果你改用AC_MSG_CHECKING和AC_MSG_RESULT,你的configure脚本的输出将更加美观。参见打印消息。这些宏能够更好地与缓存变量协同工作。参见缓存结果。
用autoupdate更新configure
程序autoupdate把使用Autoconf旧宏名的`configure.in'文件更新为使用当前宏名的文件。在Autoconf第2版中,大部分宏被重命名以使用一个更统一、更具有描述性的命名机制。关于对新的命名机制的描述,参见宏名。虽然旧宏名仍然可以工作(关于旧宏名和对应的新宏名的列表,参见陈旧的宏名),如果你更新它们以使用新的宏名,你可以使你的 `configure.in'文件更加可读并且易于使用当前的Autoconf文档。
如果没有给出参数,autoupdate就更新`configure.in',并且通过添加后缀`~' (或者在设置了环境变量SIMPLE_BACKUP_SUFFIX的时候,使用该环境变量的值)以备份原始版本。如果你带参数调用autoupdate,它就读入那个文件而不是读入`configure.in',并且把更新的文件输出到标准输出。
autoupdate接受下列选项:
--help
-h
打印命令行选项的概述并且退出。
--macrodir=dir
-m dir
在目录dir中,而不是在缺省安装目录中寻找Autoconf宏文件。你还可以把环境变量AC_MACRODIR设置成一个目录;本选项覆盖该环境变量。
--version
打印autoupdate的版本号并且退出。
改变了的结果
如果你通过检验shell变量DEFS来检验以前测试的结果,你需要把这些检验替换为对那些测试的缓存变量的检查。在configure运行的时候, DEFS不再存在;它仅仅在生成输出文件的时候才被创建。这种与第1版的不同是因为正确地对变量实行引用(quoting)实在太麻烦而且在每次调用 AC_DEFINE都要实行引用是低效的。参见缓存变量名。
例如,下面是为Autoconf第1版编写的`configure.in'的片断:
AC_HAVE_FUNCS(syslog)
case "$DEFS" in
*-DHAVE_SYSLOG*) ;;
*) # syslog is not in the default libraries.
See if it's in some other.
saved_LIBS="$LIBS"
for lib in bsd socket inet; do
AC_CHECKING(for syslog in -l$lib)
LIBS="$saved_LIBS -l$lib"
AC_HAVE_FUNCS(syslog)
case "$DEFS" in
*-DHAVE_SYSLOG*) break ;;
*) ;;
esac
LIBS="$saved_LIBS"
done ;;
esac
这里是为版本2编写的方式:
AC_CHECK_FUNCS(syslog)
if test $ac_cv_func_syslog = no; then
# syslog is not in the default libraries.
See if it's in some other.
for lib in bsd socket inet; do
AC_CHECK_LIB($lib, syslog, [AC_DEFINE(HAVE_SYSLOG)
LIBS="$LIBS $lib"; break])
done
fi
如果你通过在引号的后边添加反斜线以处理AC_DEFINE_UNQUOTED中的bug,你需要删除它们。它现在以可以预期的方式工作,并且不需要特别地处理引号(处理反斜线)。参见设定输出变量。
所有由Autoconf宏设置的布尔shell变量现在用`yes'来表示真值。虽然为了向后兼容,有些宏使用空字符串表示假,大部分宏使用`no'来表示假。如果你依赖于shell变量用诸如1或者`t'来表示真,你就需要改变你的测试。
改变了的宏的编写
在定义你自己的宏时,你现在应该使用AC_DEFUN而不是define。 AC_DEFUN自动调用AC_PROVIDE并且确保通过AC_REQUIRE调用该宏不会被其他宏所打断,从而防止在屏幕上出现嵌套的 `checking...'消息。继续按照老办法行事没有实际上的伤害,但它缺乏便利和吸引力。参见宏定义。
你可能把与Autoconf一同发行的宏作为如何解决问题的指南。看看它们的新版本将是一个好主意,因为风格已经有些改进并且它们利用了一些新的特征。
如果你利用未公开的(undocumented)Autoconf内部元素(宏、变量、变换(diversions))作了微妙的工作,就要检查你是否需要修改些什么以适应已经发生的变化。可能你甚至能够用版本2中公开(officially)支持的技术来代替你的拼装(kludging)。但也可能不能。
为了加快你自行编写的特征测试,为它们添加缓存。看看你所有的测试是否足够一般化,从而具有足够的用途以把它们封装到你可以共享的宏中去。
Autoconf的历史
你可能会困惑,最初为什么要编写Autoconf?它是如何演变到今天的形式的?(为什么它看起来就像大猩猩的吐沫?)如果你不困惑,那么本章就不包含对你有用的信息,你也可能会跳过它。如果你困惑,那就让它明白些...
起源(Genesis)
在1991年六月,我为自由软件基金会维护了许多GNU工具。由于它们被移植到更多的平台并且增加了更多的程序,用户必须在`Makefile'中选择的 `-D'选项的数目(大约20个)变得难以承受。尤其是我-- 我不得不在许多不同的系统上对每个新的发布版本进行测试。所以我编写了一个简单的shell脚本为fileutils包猜测一些正确的设置,并且把它作为 fileutils 2.0的一部分进行发布。这个configure能够胜任工作,因此,我在下个月中,手工对其进行了修改以用于其他几个GNU工具包,从而创建了相似的 configure脚本。 Brian Berliner也修改了我的脚本以用与它的CVS修订控制系统。
那个夏天以后,我得知Richard Stallman和Richard Pixley正在开发用于GNU编译器工具的类似脚本;所以我对我的 configure进行了修改以支持它们的进化的界面:把名为`Makefile.in'的文件当作模板;添加`+srcdir',作为许多选项的第一个选项;并创建`config.status'文件。
出发(Exodus)
由于我从用户那里获得了反馈,我组合了许多改进,使用Emacs进行搜索和替换、剪切(cut)和粘贴(paste),在每个脚本中进行类似的修改。随着我修改更多的GNU工具包以使用configure脚本,完全用手工更新它们就不可能了。Rich Murphey,GNU图形工具的维护者,在给我发送的邮件中说configure脚本很好,并问我是否有一个可以生成它们的工具可以发给他。没有,我想,但我将会有!所以我开始考虑如何生成它们。这样,从手工编写configure脚本的苦力向功能强大而易于使用的Autoconf前进的旅程开始了。
Cygnus configure,它大约也在那个时候被开发,是表驱动的;这意味着用少量的大体上不可猜测的特征来处理离散数量的系统类型(例如目标文件格式的细节)。Brian Fox为Bash开发的自动配置系统采用了类似的方法。为了统一用法,我好像必须绝望地试图为每个操作系统的变种的特征维护一个及时更新的数据库。更容易和更可靠的办法是不检查大多数特征--特别是在那些人们已经在本地深入地研究或者安装了买主提供的补丁的杂合的系统。
我考虑到使用与Cygnus configure相似的结构,就是提供一个单独的configure脚本,在运行时读入`configure.in'的片断。但是我不想让每个包都发布所有的特征测试,所以我选择了使用预处理器从每个`configure.in'中创建不同的configure。这个方法还提供了更多的控制和便利。
我简要地察看了被Larry Wall、Harlan Stenn和Raphael Manfredi采用的Metaconfig包,但我为了几个原因而不采用它。这种方式生成的Configure脚本是交互式的,我认为太不方便了;我不喜欢它测试某些特征的方式(例如库函数);我不知道它是否还有人维护,并且我所见到的Configure脚本在许多现代系统(例如System V R4和NeXT)中都不能工作;设置在支持某个特征或者不支持该特征时所进行的动作也不是很方便;我发现它难于学习;并且对于我的需要,它太大、太复杂了(我没有意识到Autoconf最终将变得多么大)。
我考虑过使用Perl来生成我的风格的configure脚本,但显然m4更加适合于简单的文本替换工作:由于输出是隐含的,它的工作比较少。还有,每个人都已经拥有它了。(一开始,我并不依赖于 GNU对m4的扩展。)我在Maryland大学的一些朋友最近用一些程序,包括tvtwm,制作了m4的前端,并且我也有兴趣试试一种新语言。
上路(Leviticus)
因为我的configure在没有与用户进行交互的条件下自动地确定了系统的能力,我决定把生成它们的程序称作Autoconfig。但附加了版本号之后,这个名字对于老式的UNIX文件系统来说就太长了,所以我把它缩短成Autoconf。
在1991年秋天,我召集了一群期望获得移植性的家伙(alpha测试者)以给我提供反馈从而使我可以压缩(encapsulate)我用m4宏写的脚本并且继续添加特征、改进检查中采用的技术。测试者中的杰出人物有Pinard,他提出了创建一个`autoconf'来运行m4并且检查找不到的宏调用的想法;还有Richard Pixley,他建议通过运行编译器而不是在文件系统中寻找引入文件和符号,以获得更精确的结果;还有Kerl Berry,他使得Autoconf可以配置 Tex并且把宏索引添加到文档中;还有Ian Taylor,他增加了对创建C头文件的支持以代替在`Makefile'中添加 `-D'选项的方法,以便他可以把Autoconf用于他的UUCP包。alpha测试者愉快地、一次又一次地随着 Autoconf不同发布版本中的Autoconf名称和宏调用惯例的改变而调整他们的文件。他们都贡献了许多特定的检查、绝妙的想法,以及对bug的修正。
发展(Numbers)
在1992年七月,在alpha测试之后一个月,我发布了Autoconf 1.0,并且修改了许多GNU包以使用它。我对它带来的正面作用感到很吃惊。很多人,包括那些编写并不属于GNU工程的软件(例如TCL、FSP和 Kerberos V5)的人们,开始使用它,以至于我无法跟踪他们了。随着很多使用configure脚本的人报告他们所遇到的问题,Autoconf继续快速地得到改进,
Autoconf成为考验m4实现的酷刑般的测试。由于Autoconf定义的宏的长度,UNIX m4开始失败(dump core),同时也发现了GNU m4中的一些bug。最终,我们意识到我们需要使用一些只有 GNU m4才提供的特征。特别的,4.3BSD m4含有一组增强了的内置宏;System V版本更好一些,但仍然不能提供我们所需要的所有东西。
随着Autoconf得到人们越来越多的重视,对Autoconf进行了更多的开发(并且有了我不能预见的用途)。Karl Berry添加了对X11的检查。david zuhn贡献了对C++的支持。Pinard使Autoconf能够诊断非法的参数。Jim Blandy勇敢地用它配置了GNU Emacs,并且为某些未来的改进打下了基础。Roland McGrath用它配置了GNU C库,编写了autoheader 脚本以自动创建C头文件模板,并且为configure添加了一个`--verbose'选项。 Noah Friedman添加了`--macrodir'选项和环境变量AC_MACRODIR。(他还提出了术语 autoconfiscate,用来表示“调整软件包以使用Autoconf”。)Roland和Noah改进了AC_DEFINE 中的引用保护并且修正了许多bug,特别是在1993年二月到六月间我们对处理移植性问题感到厌倦的时候。
现状(Deuteronomy)
在积累了一个关于希望添加的主要特征的长长的列表,并且在几年之中各式各样的人们提供的补丁残留了古怪的效果之后。在1994年四月,处理对Cygnus 的支持时,我开始对Autoconf进行一次主要的修订。我添加了大部分Cygnus configure 有,而Autoconf缺少的特征,主要是在david zuhn和Ken Raeburn的帮助下改编Cygnus configure的相关部分。这些特征包括对使用`config.sub'、`config.guess'、`--host'和 `--target'的支持;创建对文件的连接;以及在子目录中运行configure脚本。添加这些特征使得Ken可以放弃GNU as,Rob Savoye可以放弃DejaGNU,而改用Autoconf。
作为对其他人的要求的回应,我添加了更多的特征。许多人要求configure脚本能够在不同的运行中共享检查的结果,这是因为它们实在太慢了(尤其是像 Cygnus那样在配置一个大的源代码树的时候)。 Mike Haertel建议增加与位置有关的初始化脚本。发布必须在MS-DOS中解包(unpack)的软件的人们要求提供一种覆盖那些诸如 `config.h.in'那样的、含有两个点的文件名中的`.in'扩展名的方法。 Jim Avera通过AC_DEFINE和AC_SUBST中的引用扩展了对程序的检测;他的洞察力带来了重要的改进。Richard Stallman要求把编译器的输出送到`config.log'中,而不是送到`/dev/null'中,以帮助人们调试Emacs configure脚本。
由于我对程序质量的不满,我进行了一些其他的修改。我减少了用于显示检查结果的消息的二义性,总是打印结果。我识别宏的名字并且消除编码风格的不一致性。我添加了一些我所开发的附加工具以助于修改源代码包以使用Autoconf。在Pinard的帮助下,我创建了不会在彼此的消息中导致冲突的宏。(这个特征暴露了他草率地修正的、GNU m4 中的一些性能瓶颈!)我重新组织了人们需要解决的问题的文档。并且我开始了一组测试(testsuite),这是因为经验已经表明:在我们修改 Autoconf的时候,它有明显的回归倾向。
一些alpha测试者再次给出了难以估量的反馈,特别是 Pinard、Jim Meyering、Karl Berry、Rob Savoye、Ken Raeburn和Mark Eichin。
最后,2.0版本准备好了。而且我们也很高兴。(我们又有闲暇时间了。我想。哇,很好。)