语言: 日本語 | Español | Français | Português | 中文 | English
上一章 | 下一章 | 目录 | 英文原版(gnu.org)

11 使用 make 更新归档文件

归档文件是把称为成员的有名子文件汇集存放在一起的文件。归档文件由 ar 这个程序进行管理,主要用作链接时的子程序库。

11.1 把归档成员作为目标

归档文件中的各个成员可以用作 make 的目标或前置条件 (prerequisite)。要指定归档文件 archive 中名为 member 的成员,写法如下:

archive(member)

这种写法只能用在目标和前置条件中,不能用在命令 (recipe) 里!因为你在命令里会用到的大多数程序都不支持这种语法,无法直接对归档成员进行操作。能够做到这一点的,只有 ar 以及其他专门为操作归档而设计的程序。因此,用来更新归档成员目标的有效命令多半要使用 ar。例如,下面这条规则的含义是:复制文件 hack.o,在归档 foolib 中创建一个成员 hack.o:

foolib(hack.o) : hack.o
        ar cr foolib hack.o

实际上,几乎所有的归档成员目标都正是以这种方式更新的,所以已经准备好了一条能自动完成此事的隐含规则。[请注意] 如果归档文件还不存在,就必须给 ar 加上 ‘c’ 标志。

要指定同一个归档中的多个成员,可以把所有成员名一起写在括号里。例如,像下面这样写:

foolib(hack.o kludge.o)

这与下面这样写的含义相同:

foolib(hack.o) foolib(kludge.o)

在引用归档成员时,也可以使用 shell 风格的通配符。请参阅在文件名中使用通配符。例如 ‘foolib(*.o)’ 会展开为 foolib 归档中所有名字以 ‘.o’ 结尾的现有成员,比如 ‘foolib(hack.o) foolib(kludge.o)’。

11.2 归档成员目标的隐含规则

请回想一下,形如 a(m) 的目标表示归档文件 a 中名为 m 的成员。

make 为这样的目标查找隐含规则时,作为一种特殊机制,它不仅会考虑匹配实际目标 a(m) 的规则,也会把匹配 (m) 的隐含规则纳入对象。

正因如此,有一条目标为 (%) 的特殊规则会匹配上。这条规则通过把文件 m 复制到归档中来更新目标 a(m)。例如,它会把文件 bar.o 作为名为 bar.o成员复制到归档 foo.a 中,从而更新归档成员目标 foo.a(bar.o)

当这条规则与其他规则连锁时,效果会非常强大。因此,只要存在一个名为 bar.c 的文件,即使完全没有 makefile,只需键入 ‘make "foo.a(bar.o)"’(引号是必需的,用来防止 ‘(’ 和 ‘)’ 被 shell 解释为特殊含义),就会执行下面的命令:

cc -c bar.c -o bar.o
ar r foo.a bar.o
rm -f bar.o

这里 make 把文件 bar.o 设想为一个中间文件。请参阅隐含规则的连锁

像这样的隐含规则是使用自动变量 ‘$%’ 来编写的。请参阅自动变量

归档中的归档成员名不能包含目录名,但在 makefile 中假装它能包含目录名有时会很方便。如果你写出归档成员目标 foo.a(dir/file.o),make 会用下面的命令进行自动更新:

ar r foo.a dir/file.o

这样做的效果是把文件 dir/file.o 复制为一个名为 file.o 的成员。与这种用法相关联,自动变量 %D%F 有时会很有用。

11.2.1 更新归档的符号目录

用作库的归档文件通常包含一个名为 __.SYMDEF 的特殊成员。它保存着由其他所有成员定义的外部符号名的目录。在你更新了其他成员之后,需要同时更新 __.SYMDEF,以便它能正确地概括其他成员。这是通过运行 ranlib 程序来完成的:

ranlib archivefile

通常,你会把这条命令写在归档文件的规则中,并把归档文件的所有成员作为该规则的前置条件。例如,像下面这样写:

libfoo.a: libfoo.a(x.o y.o …)
        ranlib libfoo.a

这样做的效果是更新归档成员 x.oy.o 等,接着通过运行 ranlib 更新符号目录成员 __.SYMDEF。这里没有给出更新这些成员的规则;你多半可以把它们省略掉,交给上一节所介绍的、把文件复制到归档中的隐含规则来处理。

另外,使用 GNU 的 ar 程序时,这是不必要的,因为 GNU 的 ar 会自动更新 __.SYMDEF 成员。

11.3 使用归档时的风险

用于更新归档的内置规则与并行构建相处得并不好。这些规则(它们是 POSIX 标准所要求的)会在每个目标文件(object)被编译时就把它添加到归档中。当启用并行构建时,这会导致多个 ar 命令试图同时更新同一个归档,而这是不受支持的。

如果你想对归档使用并行构建,可以通过在 makefile 中添加下面这些行来覆盖 (override) 默认规则:

(%) : % ;
%.a : ; $(AR) $(ARFLAGS) $@ $?

第一行把更新归档内各个 object 的规则改为什么都不做。第二行把构建归档的默认规则改为用一条命令更新所有过时的 object($?)。

当然,你仍然需要使用归档语法来声明库的前置条件:

libfoo.a: libfoo.a(x.o y.o …)

如果你更愿意编写显式规则,可以这样写:

libfoo.a: libfoo.a(x.o y.o …)
        $(AR) $(ARFLAGS) $@ $?

11.4 归档文件的后缀规则

你也可以编写一种特殊的后缀规则来处理归档文件。关于后缀规则的完整说明,请参阅老式的后缀规则。归档的后缀规则在 GNU make 中已经过时,因为面向归档的模式规则是更为通用的机制(请参阅归档成员目标的隐含规则)。不过,为了与其他 make 兼容,它们被保留了下来。

要编写归档的后缀规则,只需以 ‘.a’(归档文件常用的后缀)作为目标后缀来编写一条后缀规则即可。例如,下面是从 C 源文件更新库归档的老式后缀规则:

.c.a:
        $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
        $(AR) r $@ $*.o
        $(RM) $*.o

它的行为与编写下面这条模式规则完全相同:

(%.o): %.c
        $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $*.o
        $(AR) r $@ $*.o
        $(RM) $*.o

实际上,当 make 看到一条以 ‘.a’ 作为目标后缀的后缀规则时,所做的正是这件事。任何形如 ‘.x.a’ 的双后缀规则都会被转换为目标模式为 ‘(%.o)’、前置条件模式为 ‘%.x’ 的模式规则。

由于你可能想把 ‘.a’ 用作其他某种文件的后缀,make 也会按通常方式把归档的后缀规则转换为模式规则(请参阅老式的后缀规则)。因此,双后缀规则 ‘.x.a’ 会生成两条模式规则:‘(%.o): %.x’ 和 ‘%.a: %.x’。


上一章 | 下一章 | 目录 | 英文原版(gnu.org)