LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 1647|回复: 4

The Goat Book 中文版 (第6章)写`configure.in'

[复制链接]
发表于 2003-12-30 21:07:02 | 显示全部楼层 |阅读模式
//*************************************************************

译序

初学linux系统,感觉linux底下的编译,链接程序真的很麻烦。
因此想到了用Autoconf等工具。找到了Autobook这本好书。
也不知道有没有人翻译过这本书。
该书由女友Ellen翻译,在此对她表示感谢。
该书原文请参考 http://sources.redhat.com/autobo ... obook_29.html#SEC29
如果翻译时有什么错误请
Ellengut2002@yahoo.com或者jasongut2002@126.com

转载请保留译序,3x.
//***************************************************************

6写`configure.in'

 写一个可移植的`configure.in'文件是一件需要技巧的事。既然你能将shell的任意编码放置在`configure.in'中,你的选择似乎是决定一切。初次使用Autoconf的用户会问很多问题:什么结构是可移植的?什么结构是不可移植的?如何决定检查什么?我不能检查什么?如何才能充分利用Autoconf特性?什么是不能放置在configure.in'中?应该按什么顺序运行我的检查?我在什么时候应该看系统名称而不是检查具体特性?
6.1什么是可移植性
 在我们讨论决定检查内容和方式的机制之前,我们先问自己一个简单的问题:什么是可移植性?可移植性是允许代码在不同平台上创建和运行的一种性质。在Autoconf的上下文,可移植性通常指在类Unix系统上(有时也包括Windows)运行的能力。
  当我刚开始使用Autoconf时,我也难以决定在我的`configure.in'中检查什么。当时,我正在维护只能在SunOS 4上运行的专用程序。但是我很想将它移植到Solaris, OSF/1,有可能的话,甚至将它移植到Irix。
  虽然我采用的方法达到了目的,但是它是相当费时而且很痛苦:我先写了一个最小的`configure.in',然后简单地尝试在Solaris上创建我的程序。每当我遇到一个创建问题时,我就更新configure.in'和我的源代码,然后重新开始。一旦它正确创建完毕,我开始测试是否存在与可移植性相关的运行时间问题。
  既然我并不在相对可移植的平台上开始我的工作,而且我也没意识到可以使用工具将Autoconf支持添加到软件包中(参见24. Migrating an Existing Package to GNU Autotools),该项工作的难度就比原本的难度更高。如果可能的话,一开始就写可移植的代码要显然好得多。
  全世界有很多类Unix系统,其中还包括许多已经过时但仍然在使用的系统。虽然有可能将一些程序移植到所有这些系统中,但是这种尝试并没有实际用处。将程序移植到所有的系统中是很困难,因为不可能在所有的平台上运行测试,何况每年又有新的操作系统(这些系统又各有自己的错误和特性)和发布。
  我们提倡一种实用的方法来处理可移植性:我们以类Unix系统家族中一些比较现代的成员为目标编写程序。当在我们的可移植性框架中发现缺陷时,我们就更新`configure.in'和我们的源代码,然后再继续我们的工作。实际上,这是种有效的处理方法。
单选投票, 共有 2 人参与投票
50.00% (1)
50.00% (1)
0.00% (0)
0.00% (0)
您所在的用户组没有投票权限
 楼主| 发表于 2003-12-30 21:18:27 | 显示全部楼层

The Goat Book 中文版 (6.2)简要介绍可移植的sh

6.2简要介绍可移植的sh
//**********************
该书原文请参考 http://sources.redhat.com/autobo ... obook_31.html#SEC31
如果翻译时有什么错误请
Ellengut2002@yahoo.com或者jasongut2002@126.com或就在本版回贴。
希望得到你的意见。
转载请保留译序,3x.
//**********************

  如果你阅读一系列的configure.in's,你会很快注意到它们通常是用不寻常的方式写成的。例如,你会注意到你很难看见`['程序的使用;相反,你会注意到`test'的激活。这里我们不会详细讲解如何写可移植的shell脚本(22. Writing Portable Bourne Shell将具体讲解该部分内容)。

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

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

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

使用道具 举报

 楼主| 发表于 2003-12-30 21:27:57 | 显示全部楼层

> The Goat Book 中文版 (6.3)测试顺序

6.3测试顺序
//**********************
该书原文请参考
http://sources.redhat.com/autobo ... 32.html#SEC32如果
翻译时有什么错误请
Ellengut2002@yahoo.com或者jasongut2002@126.com或就在本版回贴。
希望得到你的意见。
转载请保留译序,3x.
//**********************
   首次写`configure.in'的用户除了解决如何写可移植的sh代码这个问题外,还需要解决的一个问题是决定各类测试的运行顺序。Autoconf间接地(通过autosca程序,参见24. Migrating an Existing Package to GNU Autotools)建议了一种标准顺序。下面描述这种顺序。

1.样板文件。这一部分应该包括标准的样板文件代码,例如对AC_INIT(这个应置于首位),  
  AM_INIT_AUTOMAKE, AC_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}
fi
if test "$enable_getenv_properties" = no; then
   AC_DEFINE(DISABLE_GETENV_PROPERTIES)
fi
//+++++++++++

3.程序。接着,通常是检查configure进程、build进程或正在创建中的程序所需要的程序。这一步常常包括对宏(如AC_CHECK_PROG和AC_PATH_TOOL)的调用。

4.库。检查库应该在检查其他对C(C++或其他语言)可见的目标文件之前。因为一些其他的检查是通过尝试链接或运行一个程序来实现的;而先检查库又能保证结果程序能被链接上。

5.头文件。接着检查头文件是否存在。

6.类型定义和结构体。在检查完头文件后再检查类型定义的原因很简单:类型定义出现在头文件中,而且我们需要在查看头文件内容前知道哪些头文件是可以使用的。

7. 函数。最后,我们检查函数。这一项检查放在最后是因为函数依赖于前几项:当查询函数时,我们需要库来正确链接,需要头文件来寻找原形(这一点对C++尤为重要,因为它有着比C语言更为严格的原形规则),而对于那些没有设置使用和回归类型的函数我们还需要类型定义。

8.输出。这是通过激活AC_OUTPUT来完成的。

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

使用道具 举报

 楼主| 发表于 2003-12-30 21:31:24 | 显示全部楼层

The Goat Book 中文版 (6.4) 测试内容

6.4测试内容    :cool


//**********************
该书原文请参考

:askhttp://sources.redhat.com/autobo ... obook_34.html#SEC32
如果翻译时有什么错误请
Ellengut2002@yahoo.com或者jasongut2002@126.com或就在本版回贴。
希望得到你的意见。
转载请保留译序,3x.
//**********************

    决定测试内容才是写configure.in的核心部分。一旦你阅读完Autoconf的使用手册,你会知道如何写一个特定的测试。但是你还是不了解什么时候写这个测试——而且检查极少的东西与检查很多的东西一样容易.

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

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

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

    假如,到最后一步你遇到链接问题,那么你该如何处理呢?首先,你应该使用nm在系统库文件中查看没有找到的函数是否存在。如果该函数存在,且位于你可以使用的库文件中,那么解决方法十分简单——只要添加另外一个AC_CHECK_LIB。请注意:仅仅在库文件中找到这个函数是不够的,因为在一些系统中某些“标准”库文件是不需要的。其中libucb'便是你应该避开的库文件之一。
    如果你在系统库文件中无法找到这个函数,那么你遇到一个比较困难的问题:即不可移植的函数。基本上有三种方法处理丢失函数。下面我们将讨论函数(但事实上这三种方法或多或少都能适用于类型定义、结构和全局变量)。

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

    当有功能类似但名字不同的函数存在时,则使用第二种方法。首先查看所有的替代函数,然后修改你的源文件来使用任何一个可能存在的这种函数。习惯的做法是:在AC_CHECK_FUNCS的第二个参数中使用break。这样既能跳过不必要的测试,又能向读者指明这些检查是相连的。下面便是libgcj如何查看inet_aton和inet_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>的代码。但是,这样做并没有实际意义——使用这个头文件的大多是可移植。同时,只要使用实践知识就能解决这个问题(检查一下你的目标系统便达到目的)。
回复

使用道具 举报

 楼主| 发表于 2003-12-30 21:43:41 | 显示全部楼层

The Goat Book 中文版 (6.5) 使用配置名

6.5 使用配置名

//**********************
该书原文请参考

:askhttp://sources.redhat.com/autobo ... obook_33.html#SEC32
如果翻译时有什么错误请
Ellengut2002@yahoo.com或者jasongut2002@126.com或就在本版回贴。
希望得到你的意见。
转载请保留译序,3x.
//**********************

   虽然特性测试无疑是最好的方法,但是configure脚本也许偶尔需要就配置名作出决定。这一点在代码必须基于无法使用标准Autoconf进行特性测试的因素进行编译时尤为需要。例如,软件包expect需要查找关于系统`tty'的执行信息;只是交互编译而不检查具体的配置名是不能很可靠地实现上述目的。

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

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

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

//++++++++++
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
//+++++++++++

     注意这段代码使用了“[[ ]]”。使用它们是为了避开autoconf的一个差劲的细节运行——即autoconf在这种情形下使用M4。如果用“[ ]”代替“[[ ]]”的话,case语句中“[ ]”就会被M4吞掉,由此最终的`configure'中就不会出现“[ ]”。21. M4将详细讨论这个细节。

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

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表