autobook 3

来源:互联网 发布:股指期货软件下载 编辑:程序博客网 时间:2024/06/04 17:48

5. 一个很小的 GNU Autotools 项目

本章节描述如何通过 GNU Autotools 来管理一个很小的项目。所谓小项目是指一个非常小,但又能演示足够多相关概念的项目。通过研究一个小一点的项目,可以更容易理解这些工具之间复杂的相互作用,当处理需要更多功能的大项目时用得到。

本章中作为实例使用的工程是叫做 foonly 的伪指令解释器。foonly 是用 C 语言编写的。像许多解释器一样,它使用 lexyacc 等工具表示的词法分析器和解析器。我们开发该软件包时会使其遵循 GNU `Makefile' 标准,这也是 Automake 的缺省行为。

这个小工程并没有使用 GNU Autotools 的许多其他特性。其中最有用的是库,这个软件包不产生它自己的库,因此本章节不具体描述 Libtool。在 A Small GNU Autotools ProjectA Large GNU Autotools Project 中会展示 Libtool 是如何在构建系统中发挥作用的。本章节的目的在于在较高的层次上描述用户编写的文件以及它们之间是如何相互作用的。

 

5.1 用户提供的输入文件

最小项目只要求用户提供两个文件,创建软件包时所需的其他文件将由 GNU Autotools 产生(see section 产生的输出文件)。

  • `Makefile.am'automake 的输入文件。
  • `configure.in'autoconf 的输入文件。

我认为 `Makefile.am' 是对工程构建需要的一个高层次、精练的描述:需要构建什么,它又将被安装到何处?也许 Automake 的最大优势就在于尽可能简单的描述,但是作为最终产品的 `Makefile' 则带有一系列便利的 make 目标。

`configure.in' 是由宏调用和 shell 片段组成的模板,autoconf 通过它来产生一个 `configure' 脚本(see section Generated File Dependencies)。autoconf`configure.in' 中的内容拷贝到 `configure',在此过程中它展开其中的宏,并把文本则作为代码直接拷贝。

让我们看一下与这个最小工程中用户提供的输入文件内容。下面是 `Makefile.am'

 

## Makefile.am -- Process this file with automake to produce Makefile.inbin_PROGRAMS = foonlyfoonly_SOURCES = main.c foo.c foo.h nly.c scanner.l parser.yfoonly_LDADD = @LEXLIB@

这个 `Makefile.am' 规定了我们想要构建名为 `foonly' 的程序,并且当 make install 运行时将它安装在 `bin' 目录中。用于构建的 `foonly' 的源文件是 C 源文件 `main.c'`foo.c'`nly.c'`foo.h',以及 lex 程序 `scanner.l'yacc 语法 `parser.y'。这也指出了 Automake 的一个优势:因为 lexyacc 都从它们的输入文件中产生中间的 C 语言程序,所以 Automake 知道如何创建这些中间文件并将它们链接到最终的可执行文件。最后,如果 `configure' 需要,我们必须记得链接一个合适的 lex 库。

下面是 `configure.in' 文件:

 

dnl Process this file with autoconf to produce a configure script.AC_INIT(main.c)AM_INIT_AUTOMAKE(foonly, 1.0)AC_PROG_CCAM_PROG_LEXAC_PROG_YACCAC_OUTPUT(Makefile)

这个 `configure.in' 执行了一些必要的 Autoconf 和 Automake 初始化宏,然后调用了 AC_PROG 系列中的 Autoconf 宏以便找到合适的 C 编译器、lex 以及 yacc 程序。最后通过使用 AC_OUTPUT 宏来使产生出来的 `configure' 脚本输出一个 `Makefile' 文件。但它是从何处来呢?它是通过处理 `Makefile.in' 而产生的,而这个 `Makefile.in' 则是由 Automake 以你的 `Makefile.am' 为基础产生的(see section Generated File Dependencies)。

 

5.2 产生的输出文件

通过学习Generated File Dependencies这部分的图表,就可能明白使用哪个命令从输入文件产生上节所示的输出文件。

首先,我们产生 `configure'

 

$ aclocal$ autoconf

因为 `configure.in' 包含了 autoconf 不认识的初始宏──譬如 AM_INIT_AUTOMAKE,这时有必要收集所有的宏定义供 autoconf 产生 `configure' 时使用。这一步由 aclocal 程序来完成。这个程序因其产生 `aclocal.m4' 文件而得名。(see section Generated File Dependencies)。如果你检查 `aclocal.m4' 的内容,你会发现它包含了 AM_INIT_AUTOMAKE 宏的定义。

在执行 autoconf 之后,你将在当前目录下发现一个 `configure' 脚本。由于 automake 依赖于 `configure.in'`aclocal.m4' 的内容,所以首先运行 aclocal 是很重要的。

 

$ automake --add-missingautomake: configure.in: installing ./install-shautomake: configure.in: installing ./mkinstalldirsautomake: configure.in: installing ./missingautomake: Makefile.am: installing ./INSTALLautomake: Makefile.am: required file ./NEWS not foundautomake: Makefile.am: required file ./README not foundautomake: Makefile.am: installing ./COPYINGautomake: Makefile.am: required file ./AUTHORS not foundautomake: Makefile.am: required file ./ChangeLog not found

选项 `--add-missing' 从你的 Automake 安装处将一些样板文件拷贝到当前目录。例如 `COPYING',它包含 GNU General Public License,由于不经常改变,它们的产生就无须用户干涉。这个过程中还会安装一系列有用的脚本――产生的 `Makefile' 文件、特别是 install 目标需要使用它们。注意这时仍会缺少一些必须的文件,它们是:

`NEWS'

记录用户可见的软件包变化。它的格式并不严格,但是对最近版本的变化应该出现在文件的开头。

`README'

用户可最先查看此文件来了解软件包用途,以及特殊的安装指示。

`AUTHORS'

列举软件包编写者的名字,通常还包括他们的邮件地址。

`ChangeLog'

`ChangeLog' 是一个重要的文件,因为它记录了对软件包所做的修改。该文件的格式相当严格(see section 文档和 ChangeLogs)。

现在,我们将做足够的工作来满足 Automake 的要求:

 

$ touch NEWS README AUTHORS ChangeLog$ automake --add-missing

Automake 现在产生了一个 `Makefile.in' 文件。此时,你可能希望在自动产生文件前对这个目录有一个总体印象。

现在,目录的内容看起来比较完整,并可使你联想起安装过的 GNU 软件包的上层目录:

 

AUTHORS   INSTALL      NEWS        install-sh    mkinstalldirsCOPYING    Makefile.am  README      configure     missing ChangeLog  Makefile.in  aclocal.m4  configure.in

现在应该可以把你的目录树打包在一个 tar 文件中,并将其交付给其他用户,让他们在自己的系统上安装。由 Automake 产生的 `Makefile.in' 文件中有一个 make 目标可以简化发行包的产生过程(see section Rolling Distribution Tarballs)。用户只要解压这个 tar 文件,运行 configure(see section 如何运行 configure 和 mak),最后输入 make all

 

$ ./configurecreating cache ./config.cachechecking for a BSD compatible install... /usr/bin/install -cchecking whether build environment is sane... yeschecking whether make sets ${MAKE}... yeschecking for working aclocal... foundchecking for working autoconf... foundchecking for working automake... foundchecking for working autoheader... foundchecking for working makeinfo... foundchecking for gcc... gccchecking whether the C compiler (gcc  ) works... yeschecking whether the C compiler (gcc  ) is a cross-compiler... nochecking whether we are using GNU C... yeschecking whether gcc accepts -g... yeschecking how to run the C preprocessor... gcc -Echecking for flex... flexchecking for flex... (cached) flexchecking for yywrap in -lfl... yeschecking lex output file root... lex.yychecking whether yytext is a pointer... yeschecking for bison... bison -yupdating cache ./config.cachecreating ./config.statuscreating Makefile$ make allgcc -DPACKAGE=/"foonly/" -DVERSION=/"1.0/" -DYYTEXT_POINTER=1  -I. -I. /  -g -O2 -c main.cgcc -DPACKAGE=/"foonly/" -DVERSION=/"1.0/" -DYYTEXT_POINTER=1  -I. -I. /  -g -O2 -c foo.cflex   scanner.l && mv lex.yy.c scanner.cgcc -DPACKAGE=/"foonly/" -DVERSION=/"1.0/" -DYYTEXT_POINTER=1  -I. -I. /  -g -O2 -c scanner.cbison -y   parser.y && mv y.tab.c parser.cif test -f y.tab.h; then /  if cmp -s y.tab.h parser.h; then rm -f y.tab.h; /  else mv y.tab.h parser.h; fi; /else :; figcc -DPACKAGE=/"foonly/" -DVERSION=/"1.0/" -DYYTEXT_POINTER=1  -I. -I. /  -g -O2 -c parser.cgcc  -g -O2  -o foonly  main.o foo.o scanner.o parser.o -lfl 

 

5.3 维护输入文件

如果要在你的软件包中修改任何一个 GNU Autotools 的输入文件,为了使这些修改生效,就必须重新产生那些机器产生的文件。例如,如果你向 `Makefile.am'foonly_SOURCES 变量中增加一个新的源文件,你就有必要重新产生派生的 `Makefile.in' 文件。如果你正在构建软件包,就需要执行 configure 来重新产生 `Makefile',然后重新运行 make 来编译新的源文件并将它链接到 `foonly'

也能够通过运行所要求的工具来逐个产生这些文件。但是,正如从上文中所述,计算依赖关系是相当困难的――一个具体的变化需要运行 alocalautoconf 吗?有两种方案可以解决这个问题。

第一种方案是使用 autoreconf 命令。这个工具通过按正确顺序重新运行所有必要工具来产生所有的派生文件。这个方法虽然是生硬强制的,但是它很实用。特别是无需和其它开发者协作,或者无需日常维护时,否则这个命令会带来麻烦。

另一种方案是 Automake 的“maintainer mode”。通过在 `configure.in' 文件中使用 AM_MAINTAINER_MODE 宏,automake 会在 `configure' 文件中加入 `--enable-maintainer-mode' 选项。在Bootstrapping中详细解释了这一点。

5.4 Packaging Generated Files

The debate about what to do with generated files is one which is keenly contested on the relevant Internet mailing lists. There are two points of view and I will present both of them to you so that you can try to decide what the best policy is for your project.

One argument is that generated files should not be included with a package, but rather only the `preferred form' of the source code should be included. By this definition, `configure' is a derived file, just like an object file, and it should not be included in the package. Thus, the user should use the GNU Autotools to bootstrap themselves prior to building the package. I believe there is some merit to this purist approach, as it discourages the practice of packaging derived files.

The other argument is that the advantages of providing these files can far outweigh the violation of good software engineering practice mentioned above. By including the generated files, users have the convenience of not needing to be concerned with keeping up to date with all of the different versions of the tools in active use. This is especially true for Autoconf, as `configure' scripts are often generated by maintainers using locally modified versions of autoconf and locally installed macros. If `configure' were regenerated by the user, the result could be different to that intended. Of course, this is poor practice, but it happens to reflect reality.

I believe the answer is to include generated files in the package when the package is going to be distributed to a wide user community (ie. the general public). For in-house packages, the former argument might make more sense, since the tools may also be held under version control.

 

5.5 文档和 ChangeLogs

对于任何一个软件项目来说,在改进该项目时很重要的一点便是维护文档――该文档必须反映软件的当前状态,同时也必须精确记录过去对项目所做的改动。GNU 编码标准强制对文档的维护。事实上,当 automake 运行时,它通过确认 `ChangeLog' 文件是否存在来实现这一标准。

一系列标准化命名的文件将文档储存在 GNU 软件包中。完整的 GNU 编码标准可以提供相关信息,详情见 http://www.gnu.org/prep/standards.html

其它的工程(包括in-house工程)也可以使用这些已由实践检验的技巧。在产生的输出文件 中已经阐述了多数标准文档文件的目的,而对 `ChangeLog' 则需要多说几句。

当在 `ChangeLog' 中记录软件的变化时,每个人要编写一个完整的项目。逻辑上有关联的修改应该被记录在一起,而逻辑上不同的修改(“修改集”间)应当以一个空白行分割。以下是从 Automake 的 `ChangeLog' 中取出的一段例子:

 

1999-11-21  Tom Tromey  <tromey@cygnus.com>        * automake.in (finish_languages): Only generate suffix rule        when not doing dependency tracking.        * m4/init.m4 (AM_INIT_AUTOMAKE): Use AM_MISSING_INSTALL_SH.        * m4/missing.m4 (AM_MISSING_INSTALL_SH): New macro.        * depend2.am: Use @SOURCE@, @OBJ@, @LTOBJ@, @OBJOBJ@,        and @BASE@.  Always use -o.

另外需要指出的是:`ChangeLog' 条目都必须是简洁的。一个条目并不需要详细解释一个变化的原因,但是需要解释这个变化的内容。如果一个变化不是显而易见的,那么就由源代码解释这个变化的原因GNU 编码标准就如何写 `ChangeLog' 提供了一套完整的指导。Emacs 为编写这个文件提供了一个主模式,当然用任何一个文本编辑器都是可以修改这个文件的。

 

6. 编写 `configure.in'

写一个可移植的 `configure.in' 文件是一件需要技巧的事。既然你能将 shell 的任意编码放置在 `configure.in' 中,你的选择似乎是决定一切。初次使用 Autoconf 的用户会问很多问题:什么结构是可移植的?什么结构是不可移植的?如何决定检查什么?我不能检查什么?如何才能充分利用 Autoconf 特性?什么是不能放置在 `configure.in' 中?应该按什么顺序运行我的检查?我在什么时候应该看系统名称而不是检查具体特性?

 

6.1 What is Portability?

在我们讨论决定检查内容和方式的机制之前,我们先问自己一个简单的问题:什么是可移植性?可移植性是允许代码在不同平台上创建和运行的一种性质。对于 Autoconf 来说,可移植性通常指在类 Unix 系统上(有时也包括Windows)运行的能力。

当我刚开始使用 Autoconf 时,我也难以决定在我的 `configure.in' 中检查什么。当时,我正在维护只能在 SunOS 4 上运行的专有程序。但是我很想将它移植到 Solaris、OSF 甚至 Irix。

虽然我采用的方法达到了目的,但是它是相当费时而且很痛苦:我先写了一个最小的 `configure.in',然后简单地尝试在 Solaris 上构建我的程序。每当我遇到一个构建问题时,我就更新 `configure.in' 和我的源代码,然后重新开始。一旦它正确构建完毕,我开始测试是否存在与可移植性相关的运行时间问题。

由于我的工作不是在相对可移植的平台上开始的,且我也没意识到可以使用工具将 Autoconf 支持添加到软件包中(see section Migrating an Existing Package to GNU Autotools),该项工作的难度就比原有的高。如果可能的话,一开始就编写可移植的代码要显然好得多。

全世界有很多类 Unix 系统,其中还包括许多已经过时但仍然在使用的系统。虽然有可能将一些程序移植到所有这些系统中,但是这种尝试并没有实际用处。将程序移植到所有的系统中是很困难的,因为不可能在所有的平台上运行测试,何况每年又有新的操作系统发布,而这些系统又各有自己的错误和特性。

我们提倡一种实用的方法来处理可移植性:我们以类 Unix 系统家族中一些比较现代的成员为目标编写程序。当在我们的可移植性框架中发现缺陷时,我们就更新 `configure.in' 和我们的源代码,然后再继续我们的工作。实际上,这是种有效的处理方法。

 

6.2 可移植的 sh 概述

如果你阅读一系列的 `configure.in',你会很快注意到它们通常是用不寻常的风格写成的。例如,你会注意到你很难看见他们使用 `[';相反,你会注意到它们使用了 `test'。这里我们不会详细讲解如何写可移植的 shell 脚本,在Writing Portable Bourne Shell中将对此进行详述。

与可移植性的其他方面一样,你在 `configure.in'`Makefile.am' 中编写 shell 脚本时所采用的方法应该取决于你的目标。一些平台的 sh 实现是有缺陷的,譬如,Ultrix 的 sh 不能执行 unset。当然,GNU Autotools 是用尽可能可移植的方法写的,这样它就不会给你太多限制。

抽象地谈论可移植的 sh 编程是没有意义的。sh 本身几乎不做什么工作;大部分的实际工作都是由各个程序完成的,而每个程序都有潜在的可移植性问题。例如,一些选项无法在系统之间移植,而一些看上去很常见的程序并不存在于所有系统中。因此,你不仅需要知道哪些 sh 结构是不可移植的,还要知道你可以(或不可以)使用哪些程序,以及这些程序中的哪些选项是可移植的。

这些似乎是很令人气馁的。但是事实上,一旦你内化了这些规则,写可移植的 shell 脚本并不是十分困难的。不幸的是,这个内化过程需要很长一段时间才能实现。与此同时,一种十分可行的方法是:一边注意你在别处见到过的其他可移植代码,一边进行“尝试和观察”。同样,充分意识到你可能会喜欢哪些结构这一点也很有价值――因为这样的话,你在写可移植性很强的程序(如 emacsgcc)和写仅仅在不同版本的 Linux 上运行的程序时会有所选择。当然在 `configure.in' 中使用不可移植代码的代价是比较低的――通常情况下在不可移植结构上修改命令是比较容易的。

 

6.3 Ordering Tests

首次写 `configure.in' 的用户除了要解决如何写可移植的 sh 代码这个问题外,还需要解决的一个问题是决定各类测试的执行顺序。Autoconf 间接地(通过 autoscan 程序,在Migrating an Existing Package to GNU Autotools中进行了描述)建议了一种标准顺序,下面描述这种顺序。

标准的顺序是:

  1. 样板文件。这一部分应该包括标准的样板文件代码,例如对 AC_INIT(这个应置于首位)、AM_INIT_AUTOMAKEAC_CONFIG_HEADER,可能还有 AC_REVISION 的调用。
  2. 选项。下一部分应该包括将命令行选项添加到 `configure' 的宏,如 AC_ARG_ENABLE。通常情况下,如果支持该选项的代码很短的话,将代码也放置在这一部分中,如果下面来自 libgcj 的例子:  
    AC_ARG_ENABLE(getenv-properties,[  --disable-getenv-properties                          don't set system properties from GCJ_PROPERTIES])dnl Whether GCJ_PROPERTIES is used depends on the target.if test -n "$enable_getenv_properties"; then   enable_getenv_properties=${enable_getenv_properties_default-yes}fiif test "$enable_getenv_properties" = no; then   AC_DEFINE(DISABLE_GETENV_PROPERTIES)fi
  3. 程序。接着,通常是检查配置过程、构建过程或正在构建中的程序所需要的程序。这一步常常包括对宏,如 AC_CHECK_PROGAC_PATH_TOOL的调用。
  4. 库。检查库应该在检查其它对 C(C++ 或其他语言)可见的目标文件之前。因为一些其它的检查是通过尝试链接或运行一个程序来实现的;而先检查库能保证结果程序能被链接上。
  5. 头文件。接着检查头文件是否存在。
  6. 类型定义和结构体。在检查完头文件后再检查类型定义的原因很简单:类型定义出现在头文件中,而且我们需要在查看头文件内容前知道哪些头文件是可以使用的。
  7. 函数。最后,我们检查函数。这一项检查放在最后是因为函数依赖于前几项:当查询函数时,我们需要库来正确链接,需要头文件来寻找原型(这一点对 C++ 尤为重要,因为它有着比 C 语言更为严格的原型规则),而对于那些没有设置使用和返回类型的函数我们还需要类型定义。
  8. 输出。这是通过使用 AC_OUTPUT 来完成的。

上述顺序只是一个大概的指导,而不是一系列严格的规则。有时候,为了便于维护 `configure.in' 或者是于测试本身的确实需要,我们有必要按照不同于上述顺序排列这些测试。例如:如果你的项目同时使用了 C 和 C++ 语言,为了更容易读 `configure.in',你可能会选择在执行完所有的 C 测试后再执行所有的 C++ 测试。

 

6.4 要检查什么

决定测试内容才是编写 `configure.in' 的核心部分。一旦你阅读完 Autoconf 的使用手册,你会知道如何写一个特定的测试,但是你还是不了解什么时候写这个测试――而且检查多少东西的难度其实是一样简单的。

各个类 Unix 系统之间一个较显著的区别就是:同样的程序并不存在于所有的系统中,即使它们存在于所有的系统中,它们也不一定以同样的方式运行。如果可能的话,我们建议使用 GNU 编码标准来处理这类问题:在数量相对有限的程序集中使用最常用的选项。如果无法做到这一点的话,尝试一直使用由 POSIX 规定的程序和选项,还可以通过测试你所关注的平台中所出现的问题来扩充这一方法。

通常 `configure' 脚本中只有很小的一个部分是用来检测工具及它们之间的不同,而它的很大一部分是用来检测函数、库等等。

除了极少的核心库文件(如 `libc'`libm')和不视为系统库文件的库文件(如 `libX11')之外,各个 Unix 系统对库文件的名字或者内容并没有统一的规定。尽管如此,库文件还是很容易处理的,因为关于库文件的决定往往只影响各种 `Makefile'。也就是说,检查另一个库文件通常不要求对源代码有大的改动(有时甚至不需要任何改动)。因为添加一个新的库文件测试对开发周期的影响很小――只需重新运行 `configure',然后进行重新链接,所以你可使用粗略的方法来处理库文件。例如,你可以在你现在所关注的系统上完成你的工作,然后再按照需要来处理库文件的变化。

如果到最后一步你遇到链接问题,那么你该如何处理呢?首先,你应该使用 nm 在系统库文件中查看没有找到的函数是否存在。如果该函数存在,且位于你可以使用的库文件中,那么解决方法十分简单――只要添加另外一个 AC_CHECK_LIB。请注意:仅仅在库文件中找到这个函数是不够的,因为在一些系统中某些“标准”库文件是不合要求的。其中 `libucb' 便是你应该避开的库文件之一。

如果你在系统库文件中无法找到这个函数,那么你遇到一个比较困难的问题:即不可移植的函数。基本上有三种方法处理缺失函数。下面我们将讨论函数的问题,但事实上这三种方法或多或少都能适用于类型定义、结构和全局变量。

第一种方法是:编写一个替代的函数,然后使用条件编译的方法编译它,或者将这个函数放在一个恰当命名的文件中并使用 AC_REPLACE_FUNCS。例如,Tcl 使用 AC_REPLACE_FUNCS(strstr) 来处理没有 strstr 函数的系统。

第二种方法用于功能类似但名字不同的情况。首先查看所有的替代函数,然后修改你的源文件来使用任何一个可能存在的这种函数。习惯的做法是:在 AC_CHECK_FUNCS 的第二个参数中使用 break。这样既能跳过不必要的测试,又能向读者指明这些检查是相连的。下面便是 libgcj 如何查看 inet_atoninet_addr(它只使用找到的第一个函数)。

 

AC_CHECK_FUNCS(inet_aton inet_addr, break)

使用以上检测的代码如下:

 

#if HAVE_INET_ATON  ... use inet_aton here#else#if HAVE_INET_ADDR  ... use inet_addr here#else#error Function missing!#endif#endif

注意:当函数不存在时,我们是如何使这个情况变为一个编译时错误的。通常情况下,在创建过程中最好使错误尽可能早地出现。

处理不可移植函数的第三种方法是:编写代码使得这些函数仅被选择性地使用。例如,如果你正写编辑器时,你也许会使用 mmap 将一个文件存储在编辑器中。但是,由于 mmap 是不可移植的,你还要写一个函数来使用更具有移植性的 read

尽管如此,处理已知的不可移植函数仅仅是问题的一部分。虽然实用的方法可以解决问题,但是当你的开发工作主要在一个相当现代的系统──如 GNU/Linux 等──完成时,由于这些系统中无法找到的函数极少,这种方法的效率却不怎么高。如遇这种情况,问题就在于你可能直到源代码快要完成时才能发现其中的不可移植部分。

不幸的是,没有什么捷径可以快速解决这个问题。最后,你需要了解关于各种现存的 Unix 系统的实践知识。了解各类标准的知识(如 POSIX 和XPG)也是很有用的――如果该标准不是 POSIX,你也至少应该考虑用 POSIX 查看。但是,标准并不是万能的――因为不是所有的系统都遵循 POSIX,而且系统函数中也有错误,而你必须想办法应付它们。

最后一类你可能遇到的问题是:也很容易检测过多东西。这种情况可糟糕,因为它给你的程序增加了不必要的维护负担。例如,有时候你会看到检查 <sys/types.h> 的代码。但是,这样做并没有实际意义――使用这个头文件在多数情况下是可移植的。同时,只有实践经验才能发现这种问题,检查一下你的目标系统就好了。

 

6.5 使用配置名

虽然特性测试无疑是最好的方法,但是 `configure' 脚本也许偶尔需要根据配置名作出决定。有些信息是无法使用 Autoconf 的特性测试来检测的,当某些代码依赖于这样的信息时,就需使用到配置名。例如,软件包 expect 需要查找关于系统 `tty' 实现的信息;不检查具体的配置名,在交叉编译时是无法得到可靠的信息的。

通常情况下,测试具体的特性比测试具体的系统类型要好得多。这是因为当 Unix 和其他的操作系统在发展的过程中,系统与系统之间都相互借鉴了他人的特性。

当没有什么方法能够测试 `configure' 脚本的配置名时,最好定义一个描述特性的宏,而不是定义一个描述具体系统的宏。这样的话,同一个宏就能用于具有同样特性的其它系统中(see section Writing New Macros for Autoconf)。

Testing for a particular system is normally done using a case statement in the autoconf `configure.in' file. The case statement might look something like the following, assuming that `host' is a shell variable holding a canonical configuration system—which will be the case if `configure.in' uses the `AC_CANONICAL_HOST' or `AC_CANONICAL_SYSTEM' macros.

通常是通过 `configure.in' 文件中的 case 语句来完成对具体系统的检测。这个 case 语句看起来像下面所举的例子,假设 `host' 是包含一个规范配置系统的 shell 变量――如 `configure.in' 中使用宏 `AC_CANONICAL_HOST'`AC_CANONICAL_SYSTEM' 时。

 

case "${host}" ini[[3456]]86-*-linux-gnu*) do something ;;sparc*-sun-solaris2.[[56789]]*) do something ;;sparc*-sun-solaris*) do something ;;mips*-*-elf*) do something ;;esac

注意这段代码使用了双重方括号,这是为了解决 autoconf 中一个差劲的实现细节──它使用 M4。如果只使用单重方括号而不是双重的话,case 语句中方括号就会被 M4 吞掉,且不会出现在最终的 `configure' 脚本中。在 M4 中详细讨论了这个细节。

非常重要的一点是:在操作系统域后使用 `*',这样才能与由 `config.guess' 产生的版本号相匹配。多数情况下,你必须很仔细地匹配一系列的处理器类型。对于大多数的处理器家族来说,一个尾随的 `*' 就足够了,如例中的 `mips*')。对于 i386 家族,就现在而言,类似 `i[34567]86' 之类的东西也足够了。对于 m68k 家族,你需要类似 `m68*' 之类的东西。当然,如果你不需要对处理器进行匹配,就只要一个 `*' 用来代替整个域,如例中的 `*-*-irix*'

原创粉丝点击