Autoconf 的基本原理

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

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

与同类相比,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 程序:code

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 脚本中:get

$ 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 支持中文宏名了,即:it

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
相关文章
相关标签/搜索