Quantcast
Channel: CodeSection,代码区,Linux操作系统:Ubuntu_Centos_Debian - CodeSec
Viewing all articles
Browse latest Browse all 11063

autotools 教程:从 m4 说起

$
0
0
m4 是什么?

Autoconf 的基础是一个叫做 m4 的东西。那么 m4 是什么呢?说白了,它类似于 C 语言的预处理器,用来处理宏的。

要看看这玩意是怎么工作的?

kai@debian:~$ m4
define(`OS', `operating system')
OS
operating system

注意,我输入的是 `' (第一个字符不是半角单引号,而是 ~ 键不按 Shift)而不是 ''。

很简单吧?它将 OS 直接展开成了 operating system,而 C 语言 #define 就差不多:

#define OS operating system
OS /* 展开成 operating system */ m4 对我们有什么用?

要玩转 autoconf,我们不得不了解一下 m4。虽然 configure.ac 看上去很简单,但是里面一些高端玩法都是和 m4 有关的,要进行深度定制也离不开 m4。但是我们有必要学得很深入么?当然没有必要啦,学学跟我们可能有关的就行了。

m4 玩法举例 引用

前引用符号 ` 和后引用符号 ' 扩起来的,就是一个单纯的引用(不知道怎么解释比较好,文档里称 Quotation,注意不是 Reference)。

m4 在遇到 quotation 时不会进行宏展开,这类似于 Lisp,Lisp 遇到 quote 时不会对变量求值。如果一个关键字可以被宏展开,那么 m4 会反复将其展开到不能再展开为止。

注意,因为 m4 有前缀和后缀,所以一般来说,你在其他编程语言中有关 " " 的经验是失效的。比如 ``abc'' 是嵌套引用,应用上述展开规则,首先展开为 `abc',再展开为`abc'。

定义变量 define(`variable', `字符串')
define(`varInt', 100)
define(`DO', `define(`varInt', incr(varInt))Hello $1 varInt')

从鸭子编程(我生造的词)的角度来看,这确实是定义变量,虽然官方声称它是宏。

调用宏 define(`COUNT', 0)
define(`DO', `define(`COUNT', incr(COUNT))Hello $1 varInt')

你看得懂 DO 吗?展开后很易懂,每次调用 DO 会将 COUNT 重新定义为 COUNT + 1,并输出一段字符串 Hello $1 varInt(这里是varInt的值),$1 代表 DO 的第一个参数,如:

DO(hi)
Hello hi 1
DO(mike)
Hello mike 2

有多个参数,用逗号分隔,如果一个宏没有参数,那么依然要加上括号:

macro(arg1, agr2, ..., argn)
macro()

根据你自己的尝试和上面的一些例子,你能否猜出下面的代码是什么意思呢?

define(`foo', `one')
define(foo, `two')

嗯,其实第二句就相当于 define(`one', `two'),如果你会 Tcl,应该很好理解:

set foo one
set $foo two 注释 dnl 这是注释
dnl 这还是注释
dnl 一共三行注释
dnl 上面是骗你的 一些常见的控制结构

测试是否定义宏:

ifdef(`foo', ``foo' is defined', ``foo' is not defined')
`foo' is not defined
define(`foo', `bar')
ifdef(`foo', ``foo' is defined', ``foo' is not defined')
`foo' is defined

通用的 if 结构:

ifelse(string-1, string-2, equal-1, ..., [not-equal])
[ ] 表示可选参数

什么意思呢?看这个例子:

ifelse(foo, `bar', `$`foo' == bar', `$`foo' != bar')
$foo != bar
define(`foo', `bar')
ifelse(foo, `bar', `$`foo' == bar', `$`foo' != bar')
$foo == bar

也就是 string-1 和 string-2 相等时,该宏展开成 equal-1,以此类推,如果所有判断都是假,就展开成 not-equal。

循环?m4 是没有的。记得 m4 的求值规则吗?我们完全可以使用递归!这里我们利用 shift 函数(把一个列表的起始项移走)。

shift(1,2,3)
2,3
shift(2,3)
3
shift(3)
shift()
define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'',
`reverse(shift($@)),`$1'')')
reverse(1,2,3)
3,2,1

这个例子是从文档里面抄出来的,很恶心吧?那就最好别用。

包含一个文件 include(`xxx.m4') 宏的参数 $1 第一个参数 $2 第二个参数 $3 第三个参数 …… $n 第n个参数 $@ 所有参数

注意,GNU m4 实现支持任意个参数,但这与 POSIX 不兼容,因此不建议那样使用。

我们再看一个有关引用的小问题 define(`active', `ACT, IVE')
define(`show', `$1 $1')
show(active)
ACT ACT
show(`active')
ACT, IVE ACT, IVE
show(``active'')
active active

你能解释上述结果吗?

写在 autoconf 边上 m4 提供了一个叫 changequote 的功能,可以修改 quotation 的前缀后缀,autoconf 就用了这个功能,即前缀后缀分别修改成了 [ ],显得更加清晰简洁。

我们在本章中介绍的 m4 其实已经涵盖了 m4 的核心功能,这也是我们接下来使用 autoconf 的一个坚实基础。如果你对 m4 还有兴趣,可以参考 info m4 阅读官方文档。

其他碎碎念

m4 其实完全可以当作一个编程语言来用,但是其奇妙的用法实在让人难以接受,所以官方将其定位为 macro preprocessor 其实是非常准确的。从编程语言角度来看,相比 Tcl,m4 是一个更为贯彻「一切皆为字符串」信条的,比如上面那个引用小问题……Tcl 是不可能有这种问题的。


Viewing all articles
Browse latest Browse all 11063

Trending Articles