【问题标题】:Order-only prerequisites not working correctly in GNU make?仅订购的先决条件在 GNU make 中无法正常工作?
【发布时间】:2014-07-18 09:28:09
【问题描述】:

我对仅限订单的先决条件有疑问。这些根本不执行。我是否误解了仅订购先决条件的工作方式?

以下make脚本:

.PHONY: mefirst mefirst2

mefirst:
    @echo "I'm first!"

mefirst2:
    @echo "I'm first too!"

normaltarget: normaltarget2 | mefirst2
    @echo "normaltarget done"

normaltarget2: a b c 
    @echo "normaltarget2 done"

helloworld: normaltarget | mefirst
    @echo "helloworld done"

.DEFAULT_GOAL := go
go: helloworld
    @echo "go done"

a:
    @echo a
b:
    @echo b
c:
    @echo c

...打印出以下内容:

a
b
c
normaltarget2 done
I'm first too!
normaltarget done
I'm first!
helloworld done
go done

...而不是我所期望的:

I'm first!
I'm first too!
a
b
c
normaltarget2 done
normaltarget done
helloworld done
go done

我做错了什么?

【问题讨论】:

  • “order-only”并不意味着“不同的顺序”。顺便说一句,示例的大小可能会减少一半或更多而不会丢失任何东西。

标签: makefile gnu-make


【解决方案1】:

我是否误解了仅订购先决条件的工作方式?

是的,就是这个样子。

“仅订购”这个名称有点令人困惑。 | 背后的先决条件被称为“仅订单先决条件”,不是因为它们改变了单个目标的先决条件列表中配方执行的顺序,而是因为它们的唯一目的是让某些目标在其他目标之前创建,例如引导程序。正如下面用户 bobbogo 准确解释的那样( -- 感谢您的纠正):如果 make 决定重建目标的先决条件,它将运行该先决条件的配方。现在,对于一个普通的先决条件,此更新意味着目标现在已过时,make 将不得不运行目标的配方。另一方面,对于仅限订单的先决条件,make 不会将目标标记为需要更新。

例如,请参阅Types of Prerequisites 部分,了解应在创建目录中的对象之前创建目录的用例。

以makefile为例:

a: b
    touch a

b: c
    touch b

c:
    touch c

x: | y 
    touch x

y: | z 
    touch y

z:
    touch z

如您所见,b 和c 是a 和b 的正常先决条件,而y 和z 是x 和y 的仅订单先决条件。从零开始,它们看起来都一样:

:~$ make a
touch c
touch b
touch a
:~$ make x
touch z
touch y
touch x
:~$ make a
make: `a' is up to date.
:~$ make x
make: `x' is up to date.

但是,如果我们现在手动“更新”链末尾的先决条件(c 和 z),我们会看到不同之处:

:~$ touch c
:~$ make a
touch b
touch a
:~$ touch z
:~$ make x
make: `x' is up to date.

这显示了存在的仅订单先决条件如何不使任何目标无效,而与它们的时间戳无关。删除 order-only 目标确实会导致重建(但只会重建丢失的文件):

:~$ rm c
:~$ make a
touch c
touch b
touch a
:~$ rm z
:~$ make x
touch z

话虽如此,更改配方运行顺序的正确方法是更正目标之间的依赖关系。例如,如果您希望在a 之前构建mefirst,那么您需要使mefirst 成为a 的先决条件,如

a: mefirst
    @echo a

由于您没有详细描述您希望配方运行的顺序,因此无法为您的问题提供完整的解决方案。


您的答案有一条捷径,虽然不是解决方案,但仍然很有趣。虽然没有记录,但似乎单个目标的先决条件是按照它们出现的顺序处理的。 | 符号不会改变这一点。在您的简单情况下,您可以利用它来实现您正在寻找的输出:

normaltarget: mefirst2 normaltarget2
    @echo "normaltarget done"

和

helloworld: mefirst normaltarget
    @echo "helloworld done"

但是,正如您自己指出的那样,一旦使用 -j 标志并行运行配方,这个“解决方案”就会中断。此外,正如用户 bobbogo 所指出的,依赖这种排序机制是不好的做法。引入新的依赖项可能会干扰排序。所以不要这样做:-)

【讨论】:

  • 感谢 Reinier 的澄清。我首先尝试过,但是在使用具有高度并行性的 -j 标志时它不起作用。 “myfirst”先决条件将与以下同时执行,因为它都是并行的,并且 make 会尝试创建所有先决条件。
  • 在这种情况下,如果你真的需要在 y 之前(重新)制作 x 甚至在并行情况下,那么你必须让 x 成为 y 本身的先决条件,而不是把它们都放在同一行。这正是先决条件列表的用途:指示哪个目标取决于哪个(因此需要首先(重新)制作哪个目标)。但是,您的问题不够详细,无法为您的案例提供确切的解决方案,因为不清楚不同目标的依赖关系究竟应该是什么。有很多方法可以实现您的输出。
  • 抱歉,这个答案在许多重要方面都是错误。正如@George 所指出的,构建顺序是not 从左到右。如果您的 makefile 曾经依赖于此,则它已损坏。上面建议的修复是错误的。此外,| 之后的先决条件是以完全正常的方式构建的。如果它们不是最新的,那么它们的配方将被运行。关键的区别在于,在这样的更新之后,make 将不会继续运行原始目标的配方。这与普通的 pre-req 形成鲜明对比——如果这些需要更新,则意味着目标已过时。
  • @Reinier OOPR 和普通 PR 之间的关键区别在于:如果 make 决定重建目标的 PR(无论是 OO 还是普通),它将运行该 PR 的配方(像往常一样)。现在,对于一个普通的 PR,这个更新意味着目标现在已经过时了,make 将不得不运行目标的配方。对于 OOPR OTOH,make 不会将目标标记为需要更新。 (目标的配方可能仍会运行,但这将是由于其他 PR,而不是这个特定的 OOPR。)
  • @bobbogo 好的——很抱歉很固执,感谢您的额外解释。我还使用了您建议的-Rrd 更多地使用了示例makefile。它确实如你所描述的那样工作,我是正确的。我会更新我的答案并感谢您的教育。
猜你喜欢
  • 2016-01-03
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多