[图片] What is Portability? 在进行分析什么值得去检测, 如何检测之前, 抛出一个问题: 到底什么是可移植性? 可移植性是一种代码的质量, 可以使得编译和运行在各种各样的平台上。 在 Autoconf 的背景下, 可移植性通常指的是可以运行在类 Unix 的系统上--有时候包含 Windows. ..

"configure.in" 的书写

What is Portability?

在进行分析什么值得去检测, 如何检测之前, 抛出一个问题: 到底什么是可移植性? 可移植性是一种代码的质量, 可以使得编译和运行在各种各样的平台上。 在 Autoconf 的背景下, 可移植性通常指的是可以运行在类 Unix 的系统上--有时候包含 Windows.
当我第一次使用 Autoconf, 很难决定到底去检测什么在"configure.in".当时在做的一些项目是在 SunOS 4 上。 但是很是有兴趣移植到其他的 Unix 平台上(比如 Solaris).
我采用的方法虽然可行但是相对耗时且痛苦: 我编写了一个最小的'configure.in', 然后继续简单地尝试在 Solaris 上构建我的程序。 每次遇到构建问题时, 我都会更新'configure.in'和我的源代码并重新启动。 正确构建后, 我开始测试是否存在与可移植性相关的运行时问题。
因为我没有从一个相对可移植的基础开始, 也因为我不知道可用于帮助添加 Autoconf 支持的工具。 如果可能的话, 最好先编写可移植代码。
世界上有许多类 Unix 系统, 这些系统虽然仍在运行, 但只能被认为是过时的。 虽然将某些程序移植到所有这样的系统存在可能性,但通常尝试也没什么实际的用处。 移植就是一个困难的过程, 特别是考虑到通常无法在所有平台上进行测试, 并且这些平台每年都会发布具有自己的 bug 和特性的新操作系统。
我们提倡一种实用的可移植性方法: 我们编写的程序针对的是一个相当大的, 但也相当现代的类 Unix 系统。 由于在我们的可移植性框架中发现了缺陷, 我们更新'configure.in'和我们的源代码, 然后继续。 在实践中, 这才是一种有效的方法。

Brief introduction to portable sh

如果你读过好多的"configure.in"文件, 你会发现这个文件的书写使用的是一种非比寻常的风格。 比如说, 你很少见一个程序是"["样书写的; 这片我们不会深入太多细节关于这个脚本怎么去写, 这个会在后面详细的说道的。
与可移植性的其他方面一样, 在'configure.in'和'Makefile.am'中编写 shell 脚本的方法应该取决于设定的的目标。 一些平台已经臭名昭着地打破了 sh 实现。 例如, Ultrix sh 没有实现 unset. 当然, GNU Autotools 是以最可移植的方式编写的, 避免对可能性的定制扩展进行了限制。
另外, sh 脚本本身做得很少, 大多数实际工作是由单独的程序完成的, 每个程序都有自己潜在的可移植性问题。 例如, 某些选项在系统之间不可移植, 并且并不是每个系统上都会存在可见的公共程序 - 因此不仅要知道哪些 sh 结构不可移植, 而且还必须知道哪些程序可以(或不能)使用, 以及这些程序的哪些选项是可移植的。
这个看起来很难以实现, 但其实写一个可移植脚本程序不是那么难 - 只要对规则了如指掌了。 但是要想达到这个境界, 这个过程还是需要耗费点时间的。
了解你可能会关心哪些架构是值得的 - 如果你正在编写一个可移植度高的程序, 比如 Emacs 或 gcc, 那么你会做出不同的选择, 而不是你正在编写的东西只能运行在各种 Linux 版本上。 此外, 在"configure.in"中使用不可移植代码的成本相对较低 - 通常, 在找到不可移植的结构时, 按需重写片段代码相当容易。

Ordering Tests

除了编写可移植的 sh 代码的问题之外, 第一次编写"configure.in"面临的另一个问题是确定运行各种测试的顺序。 Autoconf 通过 Autoscan(以后会详细讲述到)间接的建议了标准顺序:

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}
         fi 

	 if test "$enable_getenv_properties" = no; then
		AC_DEFINE(DISABLE_GETENV_PROPERTIES) 
	 fi

What to check for

决定要检查的内容实际上是编写"configure.in"的重中之重。 一旦参考了 Autoconf 手册, 那么编写特定测试的"方法"应该不是什么复杂的事儿。 但是"when"可能仍然存在一定的不确定性。
各种类 Unix 系统之间存在着一个显着的分歧, 即相同的程序并不会出现在所有的系统上, 即使它们存在, 它们并不总是以相同的方式工作。 对于这些问题, 建议在可能的情况下遵循 GNU 编码标准的建议: 使用相对有限的一组程序中最常见的选项。 如果做不到这一点, 尝试使用 POSIX 指定的程序和选项, 或者通过检查关心的平台上的已知问题来扩充这种方法。
对工具的和差异性的检查通常只是一小部分。 更多的是对函数和库的检查。
除了像"libc"这样的核心库, 像"libm"和像"libX11"这样的库(不被认为是系统库), 对于不同 Unix 系统之间的库名或内容几乎没有保持一致的。 尽管如此, 库也是很容易处理的。 因为关于库的决定几乎总是只影响各种"Makefile". 这意味着检查另一个库通常不需要对源代码进行重大更改。 此外, 因为添加新的库进行测试对开发周期的影响很小 - 实际上只是重新运行"configure"然后重新链接。 可以采用不严格的库方法来提高效率。 比如说, 针对于想要部署的平台上进行库的测试就行了, 然后基于这些 base 进行定制化更改。
假设遇到链接问题。 怎么处理它? 首先要做的是使用 nm 查看系统库来查看是否存在缺失的函数。 如果是, 并且它在你知道的库中, 只需添加另一个"AC_ CHECK_LIB". 注意, 仅仅在库中查找函数是不够的, 因为在某些系统上, 某些"标准"库是不合需要的。 比如说"libucb"是你应该避免使用的最常见的库。
如果在系统库中找不到该函数, 那么就遇到了一个比较棘手的问题: 非可移植的函数。 针对于缺失函数基本上有三种方法。 下面我们讨论函数, 但实际上这些方法或多或少适用于 typedef, 结构体和全局变量。

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

注意, 如果函数不存在, 我们如何使它成为编译时错误。 通常, 最好在构建过程中尽早发生错误。

Using Configuration Names

虽然功能测试绝对是最好的方法, 但是"configure"脚本有时可能不得不根据 configuration name 做出决定。 如果某些代码必须基于无法使用标准"Autoconf"功能测试进行测试的内容进行不同的编译, 那么 configuration name 则是必要的。 例如, "expect"包需要找到有关系统的"tty"实现的信息; 如果不检查特定的 configuration name 就无法可靠地完成交叉编译。
通常最好测试特定功能, 而不是测试特定系统类型。 这是因为随着 Unix 和其他操作系统的发展, 不同的系统会相互复制功能。
如果没有其他方法可以在"configure"脚本中测试 configuration name, 则最好定义一个描述功能的宏, 而不是定义一个描述特定系统的宏。 这允许在具有相同功能的其他系统上使用相同的宏。
通常使用"autoconf"的"configure.in"文件中的 case 语句对特定系统进行测试。 case 语句可能类似于以下内容

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

假设"host"是一个包含规范配置系统的 shell 变量-如果"configure.in"使用"AC_CANONICAL_HOST"或"AC_CANONICAL_SYSTEM"宏, 则情况将也是如此。
请注意这段代码中的双方括号。 这些用于解决"autoconf"丑陋的实现细节-它在后台使用 M4. 没有这些额外的括号, case 语句中的方括号将被 M4 吞噬, 并且不会出现在结果的"configure"中。 这部分恶心细节会在之后长篇概述。
在操作系统字段后使用""尤为重要, 以匹配将由"config.guess"生成的版本号。 在大多数情况下, 必须小心匹配一系列处理器类型。 对于大多数处理器系列, 如上面的"mips "所示, 尾随的""就足够了。 对于 i386 家族而言, 目前满足"i[34567] 86"的要求就足够了。 对于 m68k 系列, 将需要"m68 "之类的东西。 当然如果不需要匹配处理器, 简单的使用'', 在'--irix'中即可。

OK, 到这里初步的针对于"configure.in"的介绍就结束了, 其中的好多好多的细节, 只后会介绍到。

回帖
请输入回帖内容...