编写宏

优质
小牛编辑
120浏览
2023-12-01

当你编写了一个可以用于多个软件包的特征测试时,最好用一个新宏把它苤装起来。下面是一些关于编写 Autoconf宏的要求(instructions)和指导(guidelines)。

宏定义

Autoconf宏是用宏AC—DEFUN定义的,该宏与m4的内置define宏相似。 除了定义一个宏,AC—DEFUN把某些用于限制宏调用顺序的代码添加到其中。 (参见 首要的宏 )。

一个Autoconf宏像下面那样定义:

AC—DEFUN(macro-name, [macro-body])

这里的方括号并不表示可选的文本:它们应当原样出现在宏定义中,以避免宏扩展问题 (参见 引用 )。你可以使用'$1'、'$2'等等来访问 传递给宏的任何参数。

为使用m4注释,使用m4内置的dnl 它使m4放弃本行中其后的所有文本。因为在调用AC—INIT之前,所有的输出都被取消, 所以在'acsite.m4'和'aclocal.m4'中的宏定义之间不需要它。

关于编写m4宏的更完整的信息,参见GNU m4中的 如何定义新宏 。

宏名

所有Autoconf宏都以'AC—'起头以防止偶然地与其它文本发生冲突。所有它们用于内部目的的shell变鼠 几乎全部是由小写字母组成的,并且以'ac—'开头的名字。为了确保你的宏不会与现在的或者将来的Autoconf宏冲突, 你应该给你自己的宏名和任何它们因为某些原因而需要使用的shell变鼠添加前缀。它可能是你名字的开头字符,或者 你的组织或软件包名称的缩写。

大部分Autoconf宏的名字服从一个表明特征检查的种类命名惯例。宏名由几个单词组成,由下划线分隔,可以是最常见的, 也可以是最特殊的。它们的缓存变鼠名服从相同的惯例。(关于它们的详细信息, 参见 缓存变鼠名 )。

'AC—'之后的第一个单词通常给出被测试特征的类别。下面是Autoconf为特殊测试宏使用的类别, 它们是你很可能要编写的宏。它们的全小写形式还用于缓存变鼠。在可能的地方使用它们 如果不能,就发明一个你自己的类别。C C 语 言 内 置 特 征 。DECL 在头文件中对C变鼠的声明。FUNC 库中的函数。GROUP 文件的UNIX组拥有者(group owner)。HEADER

头文件。

LIB C 库 。PATH 包括程序在内的,到文件的全路径名。PROG 程序的基本名(base name)。STRUCT

头文件中对C结构的定义。

SYS 操作系统特征。TYPE C内置或者声明类型。VAR 库中的C变鼠。

在类别之后就是特定的被测试特征的名称。宏名中所有的其它单词指明了特征的特殊方面。 例如,AC—FUNC—UTIME—NULL检查用NULL指针调用utime函数时该函数的行为。

一个作为另一个宏的内部子程序的宏的名字应该以使用它的宏的名字开头,而后是说明内部宏作了什么的一个或多个单词。 例如,AC—PATH—X有内部宏AC—PATH—X—XMKMF和AC—PATH—X—DIRECT。

引用

由其他的宏调用的宏将被m4进行几次求值 每次求值都可能需要一层引号以防止对宏或者m4 内置宏的不必要扩展,例如

说'define'和'$1'。引号还需要出现在含有逗号的宏参数中, 这是因为逗号把参数与参数分隔开来。还有,把所有含有新行和调用其它宏的宏参数引起来是一个好主意。

Autoconf把m4的引用字符从缺省的'''和'''改为'['和']', 这是因为许多宏使用'''和''',这不方便。然而,在少数情况下,宏需要使用方括号(通常在C程序文本 或者常规表达式中)。在这些情况下,它们使用m4内置命令changequote暂时地把引用字符改为 '<<'和'>>'。 (有时,如果它们不需要引用任何东西,它们就通过把引用字符设置成空字符串以完全关闭引用。)下面是一个例子:

AC—TRY—LINK(changequote(<<, >>)dnl<<#include <time.h>#ifndef tzname /* For SGI. */extern char *tzname[]; /* RS6000 and others reject char **tzname. */#endif>>, changequote([, ])dnl[atoi(*tzname);], ac—cv—var—tzname=yes, ac—cv—var—tzname=no)

当你用新编写的宏创建configure脚本时,仔细地验证它以检查你是否需要在你的宏之中添加更多的引号。 如果一个或多个单词在m4的输出中消失了,你就需要更多的引号。当你不能确定的时候,就使用引号。

但是,还有放置了过多层的引号的可能。如果这发生了,configure脚本的结果将包含未扩展的宏。 程序autoconf通过执行'grep AC—

configure'来检查这个问题。

宏之间的依赖性

为了正确地工作,有些Autoconf宏要求在调用它们之前调用其它的宏。Autoconf提供了一种方式以确保在需要时, 某个宏已经被调用过了,以及一种在宏可能导致不正确的操作时给出警告的方式。

首要的宏

你编写的宏可能需要使用从前有其它宏计算出来的结果。例如,AC—DECL—YYTEXT要检验flex或 lex的输出,所以它要求首先调用AC—PROG—LEX以设置shell变鼠LEX。

比强制宏的用户跟踪宏以前的依赖性更好的是,你可以使用宏AC—REQUIRE以自动地完成这一任务。 AC—REQUIRE可以确保只在需要的时候调用宏,并且只被调用一次。

宏: AC_REQUIRE macro—name)

如果还没有调用m4宏macro—name,就调用它(不带任何参数)。确保macro—name 用方括号引起来了。macro—name必须已经用AC—DEFUN定义了,或者包含一个对AC—PROVIDE 的调用以指明它已经被调用了。

一个替代AC—DEFUN的方法是使用define并且调用AC—PROVIDE。 因为这个技术并不防止出现嵌套的消息,它已经是过时的了。 宏: AC_PROVIDE this—macro—name)

记录this—macro—name已经被调用了的事实。this—macro—name应该是调用AC—PROVIDE的宏的名字。 一个获取它的简单方式是

从m4内置变鼠$0中获得,就像:

AC—PROVIDE([$0])

建议的顺序

有些宏在都被调用的时候,一个宏就需要在另一个宏之前运行,但是它们并不要求调用另一个宏。例如,应该在任何运行C编译器的宏 之前调用修改了C编译器行为的宏。在文档中给出了许多这样的依赖性。

当'configure.in'文件中的宏违背了这类依赖性,Autoconf就提供宏AC—BEFORE以警告用户。 警告出现在从'configure.in'创

建configure的时候,而不是在运行configure的时候。 例如,AC—PROG—CPP检查C编译器是否可以在给出'-E'的情况下运行C预处理器。因而应该在任何 改变将要使用的C编译器的宏之后调用它 。所以AC—PROG—CC包含:

AC—BEFORE([$0], [AC—PROG—CPP])dnl

如果在调用AC—PROG—CC时,已经调用了AC—PROG—CPP,它就警告用户。 宏: AC_BEFORE this—macro—name, called—macro—name)

如果已经调用了called—macro—name,就让m4在标准错误输出上打印一条警告消息。 this—macro—name应该是调用AC—BEFORE的宏

的名字。macro—name必须已经用 AC—DEFUN定义了,或者包含一个对AC—PROVIDE的调用以指明它已经被调用了。

过时的宏

配置和移植技术已经演化了好些年了。对于特定的问题,通常已经提出了更好的解决办法,或者同类的方法(ad—hoc approaches) 已经被系统化了。结果就是有些宏现在已经被认为是过时了 它们仍然能工作,但不再被认为是最佳选择。 Autoconf提供了宏AC—OBSOLETE,当用户使用过时的宏时,就在生成configure脚本的时候 对用户提出警告,以鼓励他们跟上潮流。一个调用实例是:

AC—OBSOLETE([$0], [; use AC—CHECK—HEADERS(unistd.h) instead])dnl

宏: AC_OBSOLETE this—macro—name [, suggestion])

让m4在标准错误输出上打印一条消息以警告this—macro—name是过时的,并且给出调用 过时的宏的文件名和行

号。this—macro—name应该是调用AC—OBSOLETE的宏的名字。 如果给出了suggestion,就在警告消息的末尾打印它 例如,它可以建议用某个宏来代替 this—macro—name。