Makefile之更新存档
上一篇:练习10 Makefile之隐式规则,目录|首页
温故知新
- 练习1 理解编译的基本过程
- 练习2 创建基本的Makefile
- 练习3 Makefile之简单介绍
- 练习4 Makefile之显式规则
- 练习5 Makefile之配方规则
- 练习6 Makefile之使用变量
- 练习7 Makefile之条件部分
- 练习8 Makefile之使用函数
- 练习9 Makefile之运行参数
- 练习10 Makefile之隐式规则
上一篇:练习10 Makefile之隐式规则,目录|首页
重新制作
有时候,Makefile 可能会从其他文件(如 RCS 或 SCCS 文件)中重新生成。如果可以从其他文件重新生成一个 Makefile,你可能希望 make 获取一个最新的 Makefile 版本来读取。
为此,在读取所有 Makefile 后,make 会将每个 Makefile 视为一个目标,并按照处理的顺序尝试更新它们。如果启用了并行构建,那么 Makefile 也会同时重新构建。
如果一个 Makefile 有一条规则告诉它如何更新(无论是在当前 Makefile 中找到还是在另一个 Makefile 中找到),或者如果适用隐式规则,它将会在必要时被更新。在检查了所有 Makefile 后,如果有任何一个实际上被更改了,make 会从干净的状态开始,重新读取所有 Makefile。(它也会尝试重新更新它们,但通常这不会再次更改它们,因为它们已经是最新的。)每次重新启动都会导致特殊变量 MAKE_RESTARTS 被更新。
如果你知道你的一个或多个 Makefile 不能重新生成,并且你希望阻止 make 对它们执行隐式规则搜索,也许是出于效率的考虑,你可以使用任何阻止隐式规则查找的常规方法来实现。例如,你可以编写一个以 Makefile 为目标的显式规则,并留下一个空的配方。
如果 Makefile 指定了一个双冒号规则,用于用配方但没有先决条件来重新生成一个文件,那么该文件将始终被重新生成。在 Makefile 的情况下,具有具有配方但没有先决条件的双冒号规则的 Makefile 将在每次运行 make 时重新生成,然后在 make 重新开始并再次读取 Makefile 后再次重新生成。这会导致一个无限循环:make 会不断地重新生成 Makefile 并重新启动,永远不会做其他事情。因此,为了避免这种情况,make 将不会尝试重新生成作为双冒号规则的目标的 Makefile,它具有一个配方但没有先决条件。
伪目标具有相同的效果:它们永远不会被认为是最新的,因此标记为伪目标的包含文件将导致 make 不断地重新启动。为了避免这种情况,make 将不会尝试重新生成被标记为伪目标的 Makefile。
你可以利用这一点来优化启动时间:如果你知道你不需要重新生成 Makefile,你可以通过添加以下之一来阻止 make 尝试重新生成它
归档文件是包含命名子文件(称为成员)的文件;它们通过程序 ar
进行维护,其主要用途是作为链接的子程序库。
上一篇:练习10 Makefile之隐式规则,目录|首页
将档案成员作为目标
归档文件中的单个成员可以在 make 中用作目标或先决条件。您可以使用以下格式指定名为 member
的成员,其中 archive
是归档文件的名称:
archive(member)
这个结构只能在目标和先决条件中使用,而不能在配方中使用!您在配方中使用的大多数程序不支持此语法,并且无法直接处理归档成员。只有 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)’。
上一篇:练习10 Makefile之隐式规则,目录|首页
存档成员目标的隐式规则
回顾一下,类似于 a(m)
的目标代表归档文件 a
中名为 m
的成员。
当 make 寻找这样一个目标的隐式规则时,作为一种特殊特性,它会考虑匹配 (m) 的隐式规则,以及匹配实际目标 a(m)
的规则。
这导致了一个特殊规则,其目标是 (%)
,会匹配。该规则通过将文件 m
复制到归档中来更新目标 a(m)
。例如,它将通过将文件 bar.o
复制到归档 foo.a
中,作为名为 bar.o
的成员,来更新归档成员目标 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
可能会很有用。
上一篇:练习10 Makefile之隐式规则,目录|首页
更新存档符号目录
作为库使用的归档文件通常包含一个名为 __.SYMDEF
的特殊成员,其中包含所有其他成员定义的外部符号名称的目录。在更新任何其他成员之后,您需要更新 __.SYMDEF
,以便它能够正确总结其他成员。这可以通过运行 ranlib
程序来完成:
ranlib 归档文件
通常,您会将这个命令放在归档文件的规则中,并将归档文件的所有成员都作为该规则的先决条件。例如,
libfoo.a: libfoo.a(x.o y.o …)
ranlib libfoo.a
这样做的效果是首先更新归档成员 x.o
、y.o
等,然后通过运行 ranlib
更新符号目录成员 __.SYMDEF
。这里未显示更新成员的规则;很可能您可以省略它们并使用前面部分中描述的将文件复制到归档的隐式规则。
在使用 GNU 的 ar
程序时,这是不必要的,因为它会自动更新 __.SYMDEF
成员。
上一篇:练习10 Makefile之隐式规则,目录|首页
使用档案时的危险
内置规则用于更新归档文件与并行构建不兼容。这些规则(符合 POSIX 标准)在编译时将每个目标文件添加到归档中。启用并行构建时,这允许多个 ar 命令同时更新同一个归档,这是不受支持的。
如果要在归档中使用并行构建,可以通过在您的 Makefile 中添加以下行来覆盖默认规则:
(%) : % ;
%.a : ; $(AR) $(ARFLAGS) $@ $?
第一行将更新归档中的单个对象的规则更改为不执行任何操作,第二行将构建归档的默认规则更改为在一个命令中更新所有过时的对象($?)。
当然,您仍然需要使用归档语法声明库的先决条件:
libfoo.a: libfoo.a(x.o y.o …)
如果您更喜欢编写显式规则,可以使用:
libfoo.a: libfoo.a(x.o y.o …)
$(AR) $(ARFLAGS) $@ $?
上一篇:练习10 Makefile之隐式规则,目录|首页
归档文件的后缀规则
要处理存档文件,您可以使用一种特殊类型的后缀规则。存档后缀规则在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’。
上一篇:练习10 Makefile之隐式规则,目录|首页