GNU make会按照什么顺序进行先决条件的制定?

43

假设我们有以下规则:

a: b c d e

bcde 是彼此独立的。

制作 bcde 的顺序有定义吗?通常情况下,它们会按照 bcde 的顺序制作,但有时候可能会出现不同的顺序。

7个回答

38

不,顺序并没有被定义。使用声明式依赖导向编程的整个就在于此:计算机可以选择最优的评估顺序,甚至可以同时评估它们。


2016年,_GNU make_的维护者将其顺序描述为至少是“已知的”/确定性的(尽管显然只适用于某些情况)。详细信息和一些阐述请参考:https://stackoverflow.com/a/77176584/1562596 - undefined

15

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.


10
这并没有真正意义上的强制顺序,至少不比一般的先决条件更强。它只是表示对于一个仅需顺序的先决条件,gmake 应该忽略时间戳。它作为一个先决条件,在当前规则运行之前必须存在,但由于它是一个仅需顺序的先决条件,它的时间戳/状态不会触发当前规则。(这并不排除其他依赖项导致仅需顺序的依赖项执行其命令的可能性。) - simpleuser
7
根据上述 GNU make 文档:然而,有时您会遇到这样的情况,即希望对目标的规则调用施加特定的顺序,而又不强制在执行其中一个规则时更新目标。 这似乎没有很好地表达出仅限顺序先决条件所做的事情。据我的理解,仅限顺序先决条件只是一种前提条件,如果更改了该前提条件,不会强制 make 重新构建目标。 - clickMe
我认为这不是OP正在寻找的东西。尝试调用make -j以查找是否按照您预期的顺序执行依赖项。 - dubbaluga

7
根据您提供的规则,按正确的顺序排列。对于您的特定示例,这可能意味着多种不同的顺序(从记忆中得知4!= 24)。
只要满足依赖关系,所有“make”程序都可以自由选择所需的顺序。如果您的示例中有其他规则(例如“c:b”),则“c”将在“b”之前制作(但正如您所指出的那样,这并不是该情况)。
如果您需要依赖特定的顺序,则需要更多规则来强制执行它。否则,“make”可以按照自己的意愿进行操作。GNU Make的文档仅说明了如何处理规则,而不是处理规则内依赖关系的顺序。最合理的顺序(至少对我来说)应该是它们列出的顺序,但这并不能保证。

4

如果我使用make -j a,它们可能会同时构建(取决于bcde是否依次具有其他/相互关联的依赖关系)。


10
这并没有真正回答问题,所以我很惊讶它被接受了。 - user181548
3
我读到的问题是:“通常它们会按顺序 bcde 进行制作,但有时可能会按不同的顺序进行制作?”我很确定我提供了一个反例。我想你的看法可能会有所不同。 - Carl Norum
“order-only-prerequisites”是按照特定顺序构建的。虽然我不确定发帖者是否知道除了“normal-prerequisites”之外还有其他前提条件。 - jww
3
@jww 不,仅用于先决条件的目标并没有按特定顺序构建,它们只是不会“[..]强制更新目标,如果执行其中一个规则[..]”。 - juan.facorro
@user181548,不要惊讶,这是非常典型的SO行为。 - SO_fix_the_vote_sorting_bug

4

当没有依赖关系时,您不能指望顺序。

  • make需要执行拓扑排序,因为依赖关系可能有额外和多个关系。make所做的排序可能非常复杂,因为图中的节点可能在不同级别上多次相关联。
  • 一般而言,排序算法甚至对于简单的基于关键字的排序也不是自然地稳定的。

4

为了以后参考,我想补充一点。虽然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,但我想分享一下有关先决条件排序的提示。


来源:https://pubs.opengroup.org/onlinepubs/009695399/utilities/make.html#tag_04_84_13_03 - Perplexabot

2

如果顺序很重要,您可以使用递归 make来有选择地强制执行它。例如,假设您不关心 b 和 c 的制作顺序,只要它们在 d 之前都制作完成,而 d 又在 e 之前制作完成。那么您可以将规则编写为:

a: b c
    $(MAKE) d
    $(MAKE) e
    # Additional steps to make a

请注意,根据 d 和 e 的复杂程度不同,这种方法可能会对您的构建时间造成不良影响:请参阅递归式 Makefile 构建方式的危害(PDF)以了解反对使用此方法的论点。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接