【问题标题】:Make should not rebuild deep dependenciesMake不应该重建深度依赖
【发布时间】:2018-12-21 14:34:19
【问题描述】:

我有一个由以下 Makefile 示例大致描述的构建过程:

a: b
    @echo "Build a, just using b.  Don't care about c."
    touch a

b: c
    @echo "Constructing b from c is cheap..."
    touch b
    @echo "Once accomplished, I no longer need c."

c:
    @echo "Constructing c is very expensive..."
    @echo "Work work work..."
    touch c

clean:
    $(RM) a b c

example: clean
    make a
    $(RM)   c
    make a

关键是:我需要c 来构建b,但是一旦我拥有b,我就再也不需要c。当我执行make examplemake 时,会生成cba(如预期的那样),删除c,然后,在最后一次make a 调用中,只是重新制作c(并且不会重新制作ba,即使我认为它们现在已经过时了)。但是,由于我的目标是ab 没有改变,我不想重制c。忘掉它!谁在乎! a 应该被认为是最新的。

另一件奇特的事,就是当我

make a
rm c
make a

(而不是make example),在第二次调用make 中重建所有内容(而在make example 中第二次调用只是重建c)。

make 的目标是a 并且a 的所有直接先决条件都存在并且比它们更新鲜时,我如何防止make 构建ca 与@ 相比并不陈旧) 987654350@),即使先决条件的先决条件没有?

编辑:我认为我可能想要将每个文件视为旧文件(例如--old-file除非该文件不存在

【问题讨论】:

  • 我无法重现您为make example 描述的行为:删除c 后,重新构建cba。你用什么版本的make?
  • GNU Make 3.81 (OS X)
  • 使用GNU Make 4.2.1 似乎与 3.81 不同。当我使用 4.2.1 时,make example 中的第二个 make a 会重建所有内容。
  • 非常有趣,我不知道 3.81 和更新版本之间的这种差异。
  • 虽然我没有证据表明您看到了这一点,但请注意,有报告称 MacOS/Xcode 提供的 GNU make 3.81 版本存在问题,这些问题未出现在 GNU make 的原始版本中3.81,甚至为 MacOS 编译。 IIRC 的失败非常奇怪。

标签: makefile


【解决方案1】:

您似乎希望 make 将文件 c 视为中间文件,该文件对您没有任何重要性,除了在生成另一个文件或其他文件时作为中间结果。这个概念在手册的10.4 Chains of Implicit Rules 部分进行了解释。由于您的示例没有使用任何隐式规则,您可以手动将文件c 标记为.INTERMEDIATE

此 makefile 将 c 显示为中间文件。

a: b
        @echo "Build a, just using b.  Dont care about c."
        touch a

b: c
        @echo "Constructing b from c is cheap..."
        touch b
        @echo "Once accomplished, I no longer need c."

c: d
        @echo "Constructing c is very expensive..."
        @echo "Work work work..."
        touch c

.INTERMEDIATE: c
.PRECIOUS: c

我添加了一个基于your comment 的文件d,尽管此示例不需要它来运行。

在调用make 之前,文件d 必须存在,它是链的起点。调用make 时,会发生以下情况:

$ touch d
$ make
Constructing c is very expensive...
Work work work...
touch c
Constructing b from c is cheap...
touch b
Once accomplished, I no longer need c.
Build a, just using b.  Dont care about c.
touch a

现在删除 c 不会对构建产生任何影响:

$ rm c
$ make
make: `a' is up to date.

除此之外,基于依赖的更新行为“和往常一样”。

.PRECIOUS target 是可选的。它是一个内置指令make 不要删除名为c 的中间文件。如果删除该行,您可以亲眼看到会发生什么。

【讨论】:

  • 有趣,我不知道.INTERMEDIATE。你能解释一下.INTERMEDIATE.SECONDARY之间的区别吗?
  • 我从不使用.SECONDARY,但它看起来和.INTERMEDIATE 做同样的事情,而且它不会丢弃你的中间文件。所以看起来你也可以使用它来代替.INTERMEDIATE.PRECIOUS。对我来说,后两者的名称更清楚地说明了它们的用途,但这是一个偏好问题。
  • 再想一想:不必将文件显式标记为.INTERMEDIATE 并不常见。由于 10.4 节中解释的隐式链接,通常文件是中间文件。 .PRECIOUS 指示很常见。这就是为什么我最终明确使用这两个,以及为什么我对.SECONDARY 不太熟悉。当然,如果您一开始就不想想要保留c,那么只需使用.INTERMEDIATE 就足够了,make 将自行删除c
  • 好的。所以,如果我不希望 c 被自动删除,我可以标记它(.INTERMEDIATE.PRECIOUS)或.SECONDARY,因为它们是等价的吗?如果它们是等价的,为什么要有.SECONDARY 目标?
  • .PRECIOUS 的文档确实表明与.SECONDARY 存在重叠。不同之处似乎是当 make 在 shell 执行时收到致命信号时所产生的行为,请参阅5.6 Interrupting or Killing make
【解决方案2】:

b 可能是从 c 构建的,但您不想告诉 Make b 依赖于 c — 如果 b 仅存在,那就足够了。所以你可以把b的食谱写成

b:
    $(MAKE) c
    @echo "Constructing b from c is cheap..."
    touch b
    @echo "Once accomplished, I no longer need c."

或者如果c用于制作b,您可以将制作c 的命令折叠到b 的配方中而不暴露@987654332 的存在@ 完全制作。

也许有更优雅的方式来表达这一点,而无需调用子制作。如果c 有一些先决条件,如果它们被更新会导致它被重建,我想它们也需要被列为b 的先决条件。

【讨论】:

  • 不幸的是,在我的真实示例中,我有多个可能依赖于 c 的东西,而不仅仅是 b。一旦这些东西在那里,c就可以被扔了。 c 也确实有先决条件,如果它们改变,c 就会改变(以及依赖于 c 的改变)。但是如果 c 被删除并且你已经得到了依赖它的东西,没问题。
猜你喜欢
  • 2012-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-18
  • 1970-01-01
相关资源
最近更新 更多