假设我们有以下规则:
a: b c d e
b
、c
、d
和 e
是彼此独立的。
制作 b
、c
、d
和 e
的顺序有定义吗?通常情况下,它们会按照 b
、c
、d
、e
的顺序制作,但有时候可能会出现不同的顺序。
假设我们有以下规则:
a: b c d e
b
、c
、d
和 e
是彼此独立的。
制作 b
、c
、d
和 e
的顺序有定义吗?通常情况下,它们会按照 b
、c
、d
、e
的顺序制作,但有时候可能会出现不同的顺序。
不,顺序并没有被定义。使用声明式依赖导向编程的整个点就在于此:计算机可以选择最优的评估顺序,甚至可以同时评估它们。
GNU make会按照前置条件的类型进行排序。
具体顺序根据GNU Make手册第4.2节而定:
There are actually two different types of prerequisites understood by GNU make: normal prerequisites such as described in the previous section, and order-only prerequisites. A normal prerequisite makes two statements: first, it imposes an order in which recipes will be invoked: the recipes for all prerequisites of a target will be completed before the recipe for the target is run. Second, it imposes a dependency relationship: if any prerequisite is newer than the target, then the target is considered out-of-date and must be rebuilt.
Normally, this is exactly what you want: if a target’s prerequisite is updated, then the target should also be updated.
Occasionally, however, you have a situation where you want to impose a specific ordering on the rules to be invoked without forcing the target to be updated if one of those rules is executed. In that case, you want to define order-only prerequisites. Order-only prerequisites can be specified by placing a pipe symbol (|) in the prerequisites list: any prerequisites to the left of the pipe symbol are normal; any prerequisites to the right are order-only:
targets: normal-prerequisites | order-only-prerequisites
The normal prerequisites section may of course be empty. Also, you may still declare multiple lines of prerequisites for the same target: they are appended appropriately (normal prerequisites are appended to the list of normal prerequisites; order-only prerequisites are appended to the list of order-only prerequisites). Note that if you declare the same file to be both a normal and an order-only prerequisite, the normal prerequisite takes precedence (since they have a strict superset of the behavior of an order-only prerequisite).
Consider an example where your targets are to be placed in a separate directory, and that directory might not exist before
make
is run. In this situation, you want the directory to be created before any targets are placed into it but, because the timestamps on directories change whenever a file is added, removed, or renamed, we certainly don’t want to rebuild all the targets whenever the directory’s timestamp changes. One way to manage this is with order-only prerequisites: make the directory an order-only prerequisite on all the targets:
OBJDIR := objdir OBJS := $(addprefix $(OBJDIR)/,foo.o bar.o baz.o) $(OBJDIR)/%.o : %.c $(COMPILE.c) $(OUTPUT_OPTION) $< all: $(OBJS) $(OBJS): | $(OBJDIR) $(OBJDIR): mkdir $(OBJDIR)
Now the rule to create the ‘objdir’ directory will be run, if needed, before any ‘.o’ is built, but no ‘.o’ will be built because the ‘objdir’ directory timestamp changed.
然而,有时您会遇到这样的情况,即希望对目标的规则调用施加特定的顺序,而又不强制在执行其中一个规则时更新目标。
这似乎没有很好地表达出仅限顺序先决条件所做的事情。据我的理解,仅限顺序先决条件只是一种前提条件,如果更改了该前提条件,不会强制 make
重新构建目标。 - clickMemake -j
以查找是否按照您预期的顺序执行依赖项。 - dubbaluga如果我使用make -j a
,它们可能会同时构建(取决于b
、c
、d
或e
是否依次具有其他/相互关联的依赖关系)。
b
、c
、d
、e
进行制作,但有时可能会按不同的顺序进行制作?”我很确定我提供了一个反例。我想你的看法可能会有所不同。 - Carl Norum为了以后参考,我想补充一点。虽然GNU Make在处理前提条件时可能没有定义特定的顺序,但POSIX make要求按照它们被指定的顺序进行处理,即从左到右。大多数实现都遵循这个规则。POSIX甚至给出了一个例子,一个不遵循这个规则的make实现可能会破坏程序的构建过程:
foo: y.tab.o lex.o main.o
$(CC) $(CFLAGS) -o $@ t.tab.o lex.o main.o
问题出在lex.o使用了错误的y.tab.h文件。虽然可以重写代码以适用于GNU Make,但我想分享一下有关先决条件排序的提示。
如果顺序很重要,您可以使用递归 make来有选择地强制执行它。例如,假设您不关心 b 和 c 的制作顺序,只要它们在 d 之前都制作完成,而 d 又在 e 之前制作完成。那么您可以将规则编写为:
a: b c
$(MAKE) d
$(MAKE) e
# Additional steps to make a
请注意,根据 d 和 e 的复杂程度不同,这种方法可能会对您的构建时间造成不良影响:请参阅递归式 Makefile 构建方式的危害(PDF)以了解反对使用此方法的论点。