分类:
2010-06-30 20:31:19
我可以做出一个非常广泛并且彻底的断言:如果你正在写面向Unix或Linux系统的开源或免费软件,那么你应该正在使用GNU Autotools. 我知道这听起来可能有些偏颇,但事实会是这样。因为我曾经把如此多的漫漫长夜花费在那些在Autotools系统看来是缺点的东西上。正常 而言,我会非常生气,会把整个项目都丢到窗外,然后自己写一个好的makefiles和configure脚本。但事实是当下有数以千计的项目成功地 运用了Autotools,我又不得不重新回来使用Autotools.我的自豪不会让我放弃。 谁应该使用Autotools? Autotools的目的是为了让软件包的维护者的工作变得更简单,对吗?但这个问题的答案却是“非也”。不要误解我的意思---长期来看, Autotools的确会让你的生活变得更轻松,但这是出于你开始时所不能意识到的其它原因。Autotools的主要目标是不是要让维护者的工作变 得简单,虽然它在保证其功能的基础上已经是最可能简单了。这是我对Autotools最重要的体会之一,我得花点时间来解释清楚。最终,我发 现Autotools的目的有两个:一方面,让你的用户很轻松地使用你的软件,别一方面,让你的项目更加具有可移植性----甚至是可以在那些你从 未在上面测试过,从未安装过,甚至是从未编译过的系统上运行。 如果你不是在开发免费或者开源软件,是否也能使用Autotools呢?你是否也会考虑Autotools的这个目标?如果你是在Unix或Linux上开发专 属(收费)软件呢?我仍然会说,这些情况下使用Autotools,你也可以一定程度上会获得好处。即使你只是面向某个特定版本的Linux系统开发 你的软件,Autotools也可以给你提供一个足够有弹性的编译环境,让你在不改变编译脚本的情况下也能成功地在未来的Linux版本或不同的 Linux发行版本上编译你的软件。还有一种情况是,你不可能提前知道你的老板将来是否会让你的软件运行在其它平台上,这个事实足够支持 我推荐Autotools的观点。 谁不应该使用Autotools?
唯一一种Autotools不起作用的情形是,你只为非Unix系统平台开发软件,比如说,Microsoft Window.可能有人会告诉你,在Window上也
可以成功地运用Autotools来开发软件,但我的观点是,POSIX/FHS编译管理的方式不太适合Window上的开发。如果硬要在Window软件开
发中使用Autotools,这样做的开销实在是得不偿失。
我也曾经看到过一些项目管理人员开发了自己个性化的Autotools版本,让Autotools可以使用Window上的工具。这些项目的维护人员花了
大量的时间来调整Autotools工具和编译环境,以使它们能在陌生甚至相背的环境中来完成一些它们设计之初根本就没有想到过的事情。坦白
而言,Microsoft已经有了一些适合在它的平台上开发软件的最好的工具。如果是我在Window上开发软件,我只会使用Microsoft的工具。事
实上,我经常是写同时面向Linux和Window上的可移植软件,在这些软件中,我维护了两个单独的编译环境,其中一个用于Windows,一个
基于Autotools,用于其它平台的。
使用GNU工具来开发Windows上的软件,最初的原因是GNU工具是免费的。但现在这个原因不再成立,微软的部分工具现在也可以免费下载
了。这个是明知的选择,但他们花了太长的时候才发现免费的价值所在。
你的语言选择 决定你使不使用Autotools的一个重要因素是你的项目中所选用的语言。我们必须知道一个事实,Autotools工具是GNU成员开发出来 ,用来管理GNU项目的。有两个因素决定计算机语言在GNU社区中的重要性: 1. 有用这种语言写的GNU软件包吗? 2 GNU编译工具支持你选择的语言吗? Autoconf基于这两个规则本身支持以下语言: • C • C++ • Objective C • Fortran • Fortran 77 • Erlang “本身支持”---我的意思是Autoconf会对这些语言编译、链接以及做一些源代码级别的特性检查。 如果你想编译一个Java软件包,可以配置Automake来达到目的,但你不能要求Autoconf来编译、链接Java包,或者做一些基于Java的检查。 Autoconf现在还不支持Java。我想在这里,我有必要指出,Java的语言特性以及Java虚拟机规范,刚开始你基本上不需要做基于Java的Autoconf检查。 现在gcj编译器以及工具集正在开发中,所以考虑未来在Autoconf中添加一些java支持,不是没有道理的。但是现在gcj现在还不成熟,现在也 很少有GNU软件是用Java写的,所以现在这个问题对于GNU社区来说不是很关键。 也就是说,现在Automake对GNU(gcj)和非GNU Java编译器和虚拟机有一定的支持。我也曾经在我的项目中使用过gcj,它工作得很好。 只要你不尝试把它推得太远。从GNU项目的发展历程来看,可以确定地说,未来的某个时候,这个功能肯定会得到改进。 如果你正在使用像Smalltalk,ADA,Modula,LISP,Forth等非主流语言,那你可能不大会考虑将你的软件移植到很多不能平台和CPU上。 话说回来,如果你正在使用非主流语言,并且你正在考虑将你的编译环境变得更具移植性,那请你也考虑将对你的语言的支持添加到 Autotools中去,这个任务不会像你想象中那么困难。而且我敢保证,如果你完成了这个任务,那你绝对会是一个Autotools专家。如果你认 为我玩笑,那你可以看看Erlang是怎么被添加到Autotools所支持的语言队伍中的。我肯定很多人都没有听说过Erlang,但Erlang社区的成员 却认为在Autotools中添加Erlang支持很有必要。 生成你自己的包编译系统 GNU Autotools框架是由三个主要的包组成,每个包都提供和依赖几个更小的组件。这三个主要的包是Autoconf,Automake和Libtool。并且 它们是以这个顺序被开发出来的,现在已经改进了很多。另外Autotools包中的工具可能依赖或使用gettext,m4,sed,make和perl以及其 它包所提供的功能。 现在有必要区分一下一个软件包维护者的系统和软件包用户的系统。Autotools设计目标指出了一个Autotools生成的编译系统只依赖于宿主 机器上很容易获得并且已经安装好的工具。Perl只有维护者的系统上才需要,用户系统上不需要。另一种说法就是终端用户的机器不需要安装 有Autotools. 如果你曾经下载过一个"tarball"---一个以.tar.gz,.tgz或.tar.bz2为后缀的压缩包,并通过解压,编译,安装等操作来安装软件。那你应该明白 这其实是一个免费软件包安装的通用的过程,这个过程大致如下: $ gzip -cd hackers-delight-1.0.tar.gz | tar -xvf - ... $ cd hackers-delight-1.0 $ ./configure $ make all $ sudo make install 注意,我不得不假定你了解一些基本的知识,如果之前你执行过这样的命令,并且你知道这些命令都在干什么,并且你知道一些关于软件开发 过程的基本知识,那你读懂这本书应该不存在什么困难。 大多数开发者都知道并懂得Make工具的作用。但configure 脚本的作用是什么呢?随着Unix的发展,Unix以及Unix-like平台差异性也越来越 大,configure脚本开始在Unix系统上被使用了。有趣的是,过去的几十年,在Unix通常都遵循Unix内核接口标准的同时,大多数重要软件通 常都不得不跳出这些标准的界线。配置脚本是一些手写的Shell脚本,被设计用来检查特定系统的特征,并可让用户在make之前选择一些包选 项。 这种方法在过去的几十年里工作得很好。但是随着Linux发行版本的不断出现,新特性的不断更新,使得编写一个合适的可移植的配置脚本变 得非常困难,甚至比在一个新项目中写一个Makefile更困难。大多数人开始采取一个众所周知的技术----复制并且修改一个类似项目的配置脚 本。到了90年代早期,很大开发者开始觉得编写configure脚本是一件很头痛的事情,应该采取某种方法来减少编写这种繁琐的管理配置选项 的Shell脚本,这些配置选项包括与平台差异有关的选项,以及与包本身有关的选项。 Autoconf Autoconf几乎在一夜之间改变了这种状况。看一看项目中的AUTHORS文件你就可以对参与编写Autoconf项目 的开发者有所了解。Autoconf最初的作者是David MacKenzie,他在1991年开始开发Autoconf。伴随着配置脚本变得越来越长,越来越复 杂,真正需要用户指定的变量是只有区区几个,大多数变量只是一些简单的选择,比如我在什么地方可以找到库和头文件?我想把我最终的产 品安装在何处?我想把哪些部件编译进我的最终产品里?使用Autoconf,开发者不必彻夜忙于修改数以千行计的Shell脚本,或者查找Bugs, 而只需使用一个简洁的基于宏的语言,写一个小的元脚本文件,让Autoconf自己去完成一个庞大完美的配置脚本。 由Autoconf生成的脚本比我们手写的脚本更加具有可移植性、更准确和更具可维护性。另外,Autoconf通常可以找出语法或逻辑上的错误, 这些错误可能需要开发者花费数天时间才能找出来。Autoconf的另外一个好处是,由它生成的Shell代码在提供任意一种Bourne shell的系统 之间可以尽可能地移植。由不同Shell之间可移植问题引起的失误,到目前为止是最普遍并且是难被发现的。因为没有一个程序员可以了解所 有版本的Bourne-like的Shell。 Autoconf生成的配置脚本提供一个具有普遍性的选项集合。这个选项集合对于任何运行在LSB兼容系统上的免费、开源和专属(收费)软件 项目都是很重要的。这些选项包括怎样修改“标准位置”,这个概念我将会在第二章中作详细解释。Autoconf生成的配置脚本同样提供项目选 定的选项。在每个项目中,它们定义在一个叫configure.ac(或configure.in)的文件中。在第三章我会详细描述这个过程。 Autoconf本身是由Bourne Shell脚本编写的,而Autoconf包中提供的其它几个程序是由Perl脚本编写的: • autoconf • autoheader • autom4te • autoreconf • autoscan • autoupdate • ifnames Autoheader Autoheader工具从configure.ac中生成一个C临时文件,这个临时文件一般命名为config.h.in,我们将在第三章中看到Autoheader更详细的 介绍。 Autom4te Autom4te是Autotools的其它工具所使用的一个缓存管理器。在Autoconf的早期,并不需要这样一个缓存,但由于大多数Autotools工具都 用到了 configure.ac的结构,这个缓存可以使后续的程序以更快的速度访问configure.ac文件,速度大概可以提高40%或更多。我不会对 Autom4te做过多的介绍,因为它主要用于Autotools内部(顺便说下,Autom4te念成“automate”),它给你的唯一印象就是在项目的顶层目录下会有一个叫做“autom4te.cache"的文件夹。 Autoreconf Autoconf能够用来执行你的项目所需要的Autoconf、Automake和lLibtool包中的配置工具。设计Autoreconf的目的是为了减少重新生成配置脚本所需要的时间。这是通过基于时间戳、特性以及项目的状态来完成的。可以把Autoreconf看作是Autotools的一个引导工具,如果你只有一个configure.ac文件,那么你可以运行Autoreconf,这将会生成配置脚本、运行配置脚本以及运行make。 Autoscan Autoscan是用来为一个新的项目生成一个可信的configure.ac文件。我们会在第三章和第六章花些时间来介绍Autoscan。在那里我们会看到从一个基本的项目上建立Autotools的全过程。 Autoupdate Autoupdate工具是用来将你的configure.ac 以及临时文件(*.in)更新到符合当前Autotools版本的语法。第二章我会更具体地介绍Autoupdate。 Ifname Ifname是一个小的,通常没有充分得到到的程序,它在命令行中接收一个源文件列表,然后在标准输出设备上列出一系列C预处理定义和它们的包含文件。 这个工具基于可移植性目的,被设计用来帮助你决定将什么东西放入到你的configure.ac和Makefile.in中。如果你的项目中已经有一些可移植性的代码,ifname可以帮助你找出这些代码在你的源码中的位置,以及这些潜在的可移植性定义可能的名字是什么。 上面所列举的工具中,在生成配置脚本过程中,被项目维护人员用到的只有autoconf 和autoheader。其它在后面我们可以看到,真正需要直接被调用到的只有autoreconf.下图展示了输入文件和Autoconf,autoheader之间的交互关系: Figure 1: Autoconf and autoheader data flow diagram 注意:本书都遵循相同的数据流图例格式。深色的图形代表由Autotools或用户提供的对象,浅色的图形代表同相同颜色的对象所生成的对象。 这些工具的主要目的是为是生成一个配置脚本,你或别的人可以使用它来配置一个项目的编译目录。这个生成的配置脚本不会以任意方式依赖于Autotools本身。事实上,Autoconf被设计用来生成可以在所有支持Broune Shell的Unix以及类Unix平台上运行的配置脚本。正常情况下,你可以用Autoconf为你的项目生成一个配置脚本,然后可以在一个没有安装Autotools的机器上运行这个配置脚本。用不着怀疑,在免费软件的世界里,这是一种常见的使用案例(use-case)。它也被称为测试良好(well-tested)的使用案例。 从上图中你可以看出,Autoconf和autoheader是由用户来调用的。这些工具从configure.ac以及不同的Autoconf风格的m4宏定义文件中提取出它们所需的输入。它们使用autom4te来维护缓存信息。Autoconf生成你的配置脚本,一个非常具有可移植性的Bourne shell脚本,这个脚本为你的项目提供可配置的能力。Autoheader基于configure.ac中的宏定义生成一个config.h.in临时文件。 你可能也注意到aclocal.m4其实是一个自相矛盾的文件,在代表它的方格中有一抹红色----它是一个生成的文件呢?还是一个用户提供的文件?答案是它都是,我会在后面的章节中解释这个问题。 Automake Aclocal GNU手册上说,aclocal工具实际上是因为Autoconf缺乏一定的灵活性而采取的临时补救措施的中间产品。Autoconf首先被设计出来,后来 过了几年,GNU开发人员想出了Automake,作为Autoconf的扩展。但实际上Autoconf并不具有Automake所要求的扩展性。 Automake在Autoconf提供的M4宏的基础上添加了一些扩展的宏集合。往一个Autoconf项目中添加用户定义的宏的最初的方法是在与 configure.ac同级目录中添加一个叫做aclocal.m4的文件。任何用户定义的扩展宏都放在这个文件中,Autoconf在处理configure.ac文件是 会自动地从这个文件中读取用户定义的扩展宏。从 Automake设计者的角度来看,这种已存在的扩展机制实在是太好而难以放弃。但要求用户往aclocal.m4文件中添加一个m4_include行显得 有点笨拙。所以aclocal被设计用来替代这个工作,由它来创建一个项目的aclocal.m4文件,这个文件可以包含的所有要求的Automake宏。 自从aclocal接管创建aclocal.m4的工作以后,它也被设计用来读取用户自定义的名为acinclude.m4的宏文件。 本质上,aclocal的工作是从Autotool包的安装位置以及用户指定的位置搜集不同的宏文件,将它们合并到aclocal.m4文件中,这样Autoconf 就可以在一个地方找到所有它所需要的宏。 出于模块化的考虑,Autoconf实际上在很大程序上仍然不会感知aclocal工具的存在。当前版本的手册里对aclocal工具的功能应该放在什么位 置仍然颇有说辞。Automake手册最初的建议是,如果要往一个Autoconf项目中添加Automake,你就应该把aclocal.m4重命名为acinclude.m4. 这种方法在新项目中仍然得到严格地遵循。 然而,无论是Autoconf,还是Automake,它们的最近文档里都认为整个aclocal/acinclude方式已经过时,建议采用一种新的方法,这种方 法指定了一个包含M4宏文件的目录。当前推荐的方法是,在你的项目目录中,创建一个名叫m4的目录(对这种提法者来说,acinclude看上 去更适合),然后在这个目录中添加一些单独的.m4宏文件。在Autoconf处理configure.ac之前,所有这些文件都会被收集,集中到最后的 aclocal.m4文件,最终aclocal的功能会由Autoconf自己来接管。(鉴于aclocal功能的复杂性,以及其它工具都是merl语言写的,我猜测以后Autoconf也会由perl语言来写)。 图2: Aclocal数据流图 理解了aclocal的功能,我们现在可以明白为什么在图1的Autoconf数据流中,不能决定aclocal.m4方格的颜色。当不使用Automake和Libtool 时,aclocal.m4是手写的,如果连同Automake和Libtool一起使用,这个文件就是由aclocal生成的,项目特定的宏就放在了acinclude.m4 文件中。 Libtool 怎样才能在不同的Unix平台上编译共享库,而不用在编译系统和项目源码里添加过多的平台相关的条件代码?这正是Libtool要解决的问题。 在不同的Unix和类Unix平台上有许多可见的相同的功能,但是它们之间一个重要的区别就是共享库的编译、命名和管理方式。有些平台甚至不提供本地共享库 (虽然现在这种情况已不多见)。有些平台的共享库叫做libsomething.so,而有些平台上的共享库叫做something.o,有些使用libsomething.a,而另外一 些使用libsomething.sa。有些平台提供libdl库让软件可以在运行时动态地加载和访问库函数。而另外的平台提供其它机制---或者根本就不提供类似的机制。 Libtool项目的开发者们仔细地考虑了所有这些细节上的差异。现在Libtool已经支持了数十种不同的平台,并且新的平台的支持可以通过开源的方式来完成---- 关注新平台的人往Libtool的邮件列表中添加该平台的补丁代码,Libtool的维护者检查这些代码,合格则将这些代码合并到Libtool的源码中,并在下一个版本 中发布。 Libtool不仅提供了一些可以在makefile中隐藏共享库命名差异的一些Autoconf宏,同时也提供一个可选的动态装载功能的库,你可以在你的代码中使用这个 库,这样你就可以编写更具可移植性的运行时动态共享库管理的代码。 libtool包提供了一些程序,库和头文件:
• libtool (program) • libtoolize (program) • ltdl (static and shared libraries) • ltdl.h (header) libtool shell脚本是一个通用版本的Libtool。Libtool用来在你的平台上供程序所使用。对于一个项目来说,这个特殊的libtool的拷贝没有什么特别的。 Libtoolize 在你的项目使用Libtool之前,可以使用libtoolize脚本来作准备。实际上libtoolize会在你的项目目录中生成一个本地化版本的libtool脚本。然后这个生成的libtool脚本会在合适 的时候由Automake生成的makefiles所调用。 Libtool C API --- ltdl Libtool包也提供了ltdl库和头文件。这些库和头文件提供一个一致的跨平台的运行时共享对象管理器。ltdl库可以动态或静态地链接到你的程序中,在不同的平台之间提供一个统 一的运行时共享库访问接口。 下面的数据流展示了Automake以及Libtool和用户用来配置和编译其项目的输入文件之间的交互关系: 图3 Automake和Libtool数据流图例 Automake和Libtool都是一些插件式的选项,通过添加一些宏调用可以将它们加入到configure.ac中。 编译你的软件包 作为一个软件包维护者,你应该比你的用户编译软件的次数更多,你可能更熟悉你的项目的组件、体系架构和编译系统。这就是为什么你应该考虑到你的用户 的编译经验比你少得多。 运行配置文件 一旦Autotools完成了它们的工作,那么留给你的将是一个名为configure的脚本和一个或几个makefile.in文件。这些文件将会随着你的软件一起发布。你的 用户下载你的软件,解压压缩包,然后运行configure和make.configure脚本把Makefile.in文件转化成Makefile文件。它也会从由autoheader生成的config.h.in的文件生成一个叫做config.h的头文件。 那么,为什么Autotools不直接生成可以发布的Makefiles文件呢?其中一个原因是没有makefiles,你就不能运行make。这意味着你下载和解压软件包以后, 不得不运行configure脚本。makefile.in和你可能手写的makefile基本上是一样的,除了它不需要手写以外。另外makefile.in文件比大多数人做的工作更多。 另外一个原因是configure脚本可能会往makefile中插入一些平台相关的特性以及用户指定的一些选项特性,使得这些makefile文件更加适合目标平台。 下图展示了configure文件和它在创建Makefile文件以及config.h头文件过程中所执行的脚本之间的交互关系: 图4 configure 脚本数据流图 configure脚本看上去和config.status有着比较怪异的乱伦关系。我想你肯定总是会认为你的configure脚本生成了makefiles.而事实是,configure生成 的唯一的文件(除了一个log文件)就是config.status.configure脚本的作用就是确定一些在configure.ac文件中指定的平台相关的特征和特性。一旦它得到 了这些信息,它会生成config.status文件使得config.status包括所有的检查结果,然后再调用config.status.新生成的config.status文件使用这些检查信 息(现在已经嵌入在这个文件中了)来生成平台相关的config.h和makefiles,以及configure.ac中指定实例化的其它文件。双红箭头显示出config.status 文件也会调用configure.当config.status使用时带上了recheck选项,它就会用它刚生成时的同样的命令行选项来调用configure.configure也会生成一个 叫做config.log的文件,该文件记录了你在你的平台上调用configure失败的原因。作为维护者,你可以使用这些信息来帮助你的用户调试用户的问题。只需要 让你的用户发送它们的config.log文件给你。问题一般都是比较容易解决的。config.log的另一个特点是它记录了configure是怎样被执行的---哪些命令行选项 被使用。 对于用户而言,它可能会遇到一些现实问题。比如他刚从旅游回来,记不起之前他使用了哪些命令行选项来生成他的项目编译目录。Autoconf生成的配置脚本使 得这个问题很容易解决。如果你由于某种原因,需要重新生成makefiles和config.h头文件,那么你只需要简单地在你的项目编译目录下敲入./config.status, 这将会使用你之前生成config.status文件时的命令行选项来生成你所需要的输出文件。 远程编译目录 Autotools编译环境的一个不太为人所知的特性是,你不需要在你的项目的源码树下来生成你的编译目录。也就是说,一个用户可以在远程执行configure文件, 在一个远程编译目录下生成一个完全的编译环境。 在下面的例子中,Joe用户下载了软件doofable3.0并且解压它。然后他创建了两个同级目录:doofable-3.0.debug和doofable-3.0.release.然后它进入到 doofable-3.0-debug目录,用一个doofable所指定的调试选项远程地执行doofable-3.0的configure,然后运行make。最后它换到doofable-3.0.release 目录下,不用调试选项,做同样的事情: $ tar -zxvf doofable-3.0.tar.gz $ mkdir doofable-3.0.debug $ mkdir doofable-3.0.release $ cd doofable-3.0.debug $ ../doofable-3.0/configure --enable-debug $ make $ cd ../doofable-3.0.release $ ../doofable-3.0/configure $ make ... 用户一般不会关注远程编译功能,因为他们所需要做的就是去configure,make,然后make install,把你的产品安装到他的系统中。但是作为维护者而言,远 程编译功能却是非常有用的。它可以帮助你,1)维护一个不受污染的源码树,2)为你的项目维护多个编译环境,每个编译环境拥有不同的复杂的配置选项。 与重新配置一个单独的编译环境不同,维护者可以用不同的方式简单地在不同的编译目录中切换。 运行make 最后,你会运行make,就像是make简单的手写makefile一样。其实,Autotools的设计者们已经做了许多的工作来确保你可以运行任何版本的Make工具。 只要你愿意,你不需要GNU make ---- 你可以使用Solaris make,或者BSD Unix make。 下图展示了make工具和生成的makefiles之间的交互关系: 图5 make数据流图 总结 在这一章中,我系统地总结了Autotools各个部件是如何一起工作的。 在下一章中,我们开始为一个实验项目创建一个手写的编译系统。这样做的目录是让你熟悉一个合理的项目所需要做的一些事情,并且让你知道Autotools到 底可以为你做些什么。 有太多的开发者在没有通过艰苦的训练获得经验的情况下,开始使用Autotools。其实这会导致他们对Autotools产生迷惑,然后产生消极的态度。在下一章 中,你将会了解许多Autotools设计的基本原理。通过了解这些背景知识,我希望你们之前对Autotools的负面偏见会得到一定的减少。 转自:http://hi.baidu.com/zmjdx/blog/item/94aa05a8d2b9afbaca130c58.html |