13

Autoconf 可以产生一份 Shell 脚本。对于大部分类 Unix 系统,这份 Shell 脚本能够自动配置软件源码包的构建环境。这份 Shell 脚本就是 Linux 系统中大名鼎鼎的 configure 脚本。

在 Linux 系统中,只要你打算以编译源码的方式安装软件包,通常要借助 configure 脚本构建软件源码包的编译环境,除非是某个基于 CMake 或 SCons 构建的软件源码包。迄今为止 Autoconf 所属的 GNU Autotools 依然是类 Unix 系统中主流的项目构建工具,并且也是 Linux 系统中软件源码构建工具的事实标准。

与同类相比,GNU Autotools 最大的特点是不重新发明轮子,它完全基于 GNU M4 与 Bash Shell 语言(实际上还有 Perl)开发而成。此外,基于 GNU Autotools 发布的软件,它的构建环境配置以及构建过程不再依赖 GNU Autotools,这一点是 CMake 与 SCons 们无法做到的。

要知道 Autoconf 如何生成 Shell 脚本,你至少要具备一丁点 Shell 脚本与 M4 的知识。在此,Shell 脚本指的是 Bash Shell 脚本,有关它的一些知识可以阅读『用几分钟学习 Bash』;M4 指的是 GNU M4,它的一些知识可以阅读『让这世界再多一份 GNU m4 教程』。

现在假设你已经具备了这些基础知识。下面是我写的一段很简单的 Shell 代码,它可以检测系统中是否安装了 foo 程序:

if [ $(which foo) ]
then
    echo "checking foo ... yes!"
else
    echo "You should install foo!"
    exit -1
fi

现在,我用 M4 给上述 Bash 代码制作一个『界面』:

define(`检测系统中是否已安装 foo', `
if [ $(which foo) ]
then
    echo "checking foo ... yes!"
else
    echo "You should install foo!"
    exit -1
fi
')

所谓的『界面』,就是宏 检测系统中是否已安装 foo。当 GNU m4 读到这个宏时,它就会自动将其展开为它所封装的 Bash 代码。为了说明这一点,请将上述 M4 代码放到一份名为 check-foo.m4 的文件中,然后在一份名为 configure.ac 的文件中写出以下代码:

include(check-foo.m4)

检测系统中是否已安装 foo

然后用 GNU m4 读入这个 configure.ac 文件,并将展开结果写入到一份名为 configure 的 Bash 脚本中:

$ m4 configure.ac > configure

结果就在 configure 文件中获得了 检测系统中是否已安装 foo 这个宏的展开结果:

if [ $(which foo) ]
then
    echo "checking foo ... yes!"
else
    echo "You should install foo!"
    exit -1
fi

如果你得不到上述结果,是因为我欺骗了你。虽然 m4 允许使用中文宏名,但是它不认为这是真的宏名。你可以将 检测系统中是否已安装 foo 改为 check-foo,这样 m4 就认为它是真正的宏了。也可以使用 GNU M4 提供的间接宏调用功能,这样就可以迂回的使得 m4 支持中文宏名了,即:

indir(`检测系统中是否已安装 foo')

现在,我认为我已经回答了『autoconf 是如何生成 Shell 脚本的』这个问题,你只需要将上述的 m4 命令视为 autoconf 即可。也就是说,autoconf 本质上就是 m4——穿了外套的 m4。

当 autoconf 将 configure.ac 文件中的宏展开为 Bash 代码并将其存储于 configure 脚本之后,以后执行 configure 脚本时,就与 autoconf 无关了。而且,我将 configure 脚本传给他人使用,他们也不需要 autoconf。

这就是 autoconf 运作的基本原理。然而很多人被这个基本原理吓走了,因为他们看见 M4 与 Shell 语言就头大!

如果看到这里你依然不觉得害怕,那么我就可以放心的将上文中的那个 Bash 代码片段做成一个真正的 Autoconf 宏了。所谓的 Autoconf 宏,它本质上就是 M4 宏——穿了外套的 M4 宏。

下面,我要创建一个目录,叫 m4,在这个目录中放置 check-foo.m4 文件,然后将 check-foo.m4 文件的内容修改为:

AC_DEFUN([CHECK_FOO], 
[if [[ $(which foo) ]]
then
    echo "checking foo ... yes!"
else
    echo "You should install foo!"
    exit -1
fi])

这里的 CHECK_FOO 是一个 Autoconf 宏,它与前文中的那个 M4 宏 检测系统中是否已安装 foo 本质上是一样的。二者的定义有区别的地方就在于:

  • AC_DEFINE 取代了 define
  • [ 取代了 M4 的左引号,] 取代了 M4 的右引号;
  • CHECK_FOO 取代了 检测系统中是否已安装 foo
  • [[ $(which foo) ]] 取代了 [ $(which foo) ],这一点需要了解 M4 的工作原理。

这些『取代』,是 M4 所允许的,也就是说,这一切只用 M4 就能够做到。

接下来,再将 configure.ac 文件修改为:

AC_INIT
CHECK_FOO

然后,在 configure.ac 文件所在的目录执行以下命令:

$ aclocal -I m4
$ autoconf
$ ./configure

如果你的系统中没有 foo 程序,就可以得到这样的结果:

which: no foo in (/bin:/usr/bin:/usr/local/bin)
You should install foo!

上述过程,只有两点需要略做说明。首先,configure.ac 文件中出现的 AC_INIT 宏会被 autoconf 展开为很长的一段 Bash 代码,用于初始化软件源码构建环境;其次,aclocal 命令负责收集 M4 文件的路径信息并将其存储于 aclocal.m4 文件中。

在执行 autoconf 命令时,它会自动读取 configure.ac 文件,然后根据 aclocal.m4 文件中记录的 M4 文件,去寻找 configure.ac 中出现的宏的定义然后进行展开,展开结果就是 configure 脚本。执行 configure 脚本,除了会执行 AC_INIT 所展开的 Bash 代码,还执行了 CHECK_FOO 所展开的:

if [ $(which foo) ]
then
    echo "checking foo ... yes!"
else
    echo "You should install foo!"
    exit -1
fi

garfileo
6k 声望1.9k 粉丝

这里可能不会再更新了。


引用和评论

0 条评论