仅限订单的先决条件在GNU make中无法正常工作?

Geo*_*dré 21 makefile gnu-make

我有一个仅限订单的先决条件的问题.这些根本不会首先执行.我是否误解了仅限订单的先决条件的工作方式?

以下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
Run Code Online (Sandbox Code Playgroud)

...打印出以下内容:

a
b
c
normaltarget2 done
I'm first too!
normaltarget done
I'm first!
helloworld done
go done
Run Code Online (Sandbox Code Playgroud)

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

I'm first!
I'm first too!
a
b
c
normaltarget2 done
normaltarget done
helloworld done
go done
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?

Rei*_*eek 42

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

是的,这就是它的样子.

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

例如,请参阅用例的先决条件类型部分,其中应在创建该目录中的对象之前创建目录.

举个例子makefile:

a: b
    touch a

b: c
    touch b

c:
    touch c

x: | y 
    touch x

y: | z 
    touch y

z:
    touch z
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,b并且c是正常的先决条件ab,而yz有秩序,只有先决条件xy.从干净的石板开始,它们看起来是一样的:

:~$ 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.
Run Code Online (Sandbox Code Playgroud)

但是,如果我们现在手动"更新"链末尾的先决条件(cz),我们会看到差异:

:~$ touch c
:~$ make a
touch b
touch a
:~$ touch z
:~$ make x
make: `x' is up to date.
Run Code Online (Sandbox Code Playgroud)

这显示了存在的仅限订单的先决条件如何不会使任何目标无效,而与其时间戳无关.删除仅订单目标会导致重建(但只重建该丢失的文件):

:~$ rm c
:~$ make a
touch c
touch b
touch a
:~$ rm z
:~$ make x
touch z
Run Code Online (Sandbox Code Playgroud)

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

a: mefirst
    @echo a
Run Code Online (Sandbox Code Playgroud)

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


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

normaltarget: mefirst2 normaltarget2
    @echo "normaltarget done"
Run Code Online (Sandbox Code Playgroud)

helloworld: mefirst normaltarget
    @echo "helloworld done"
Run Code Online (Sandbox Code Playgroud)

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

  • @Reinier OOPR和普通PR之间的_crucial_差异是这样的:如果make决定重建目标的PR(无论是OO还是普通的),它将运行该PR的配方(像往常一样).现在,对于普通PR,此更新意味着目标现在已过时,_make_将必须运行目标的配方.对于OOPR OTOH,_make_不会将目标标记为需要更新.(目标的配方可能仍会运行,但这将是由于其他PR,而不是这个特定的OOPR.) (4认同)
  • 对不起,但这个答案在许多重要方面都是错误的**.正如@George指出的那样,构建顺序是_not_从左到右.如果你的makefile依赖于它,那么它就会被破坏.上面提到的修复是错误的.此外,`|`之后的先决条件是以完全正常的方式构建的.如果它们不是最新的,那么它们的配方将会运行.关键的区别在于,在这样的更新之后,_make_将不会继续运行原始目标的配方.这与普通的预先要求形成鲜明对比 - 如果这些需要更新则意味着目标已经过时. (3认同)
  • @bobbogo好 - 抱歉顽固,谢谢你的额外解释.我更多地使用了`makefile`这个例子,也使用了你建议的`-Rrd`.它完全按照你的描述工作,我站得更正.我会更新我的答案并感谢您的教育. (3认同)
  • 感谢您的澄清 Reinier。我首先尝试过,但是在使用具有大量并行性的 -j 标志时它不起作用。“myfirst”先决条件将与以下同时执行,因为它都是并行的,并且 make 尝试使所有先决条件。 (2认同)
  • 在这种情况下,如果您确实需要在 y 之前(甚至在并行情况下重新)制作 x,那么您必须使 xa 成为 y 本身的先决条件,而不是将它们放在同一行上。这正是先决条件列表的用途:指示哪个目标取决于哪个(因此需要首先(重新)制作哪个目标)。但是,您的问题不够详细,无法为您的案例提供确切的解决方案,因为尚不清楚不同目标的依赖关系究竟应该是什么。有很多方法可以实现您的输出。 (2认同)