【问题标题】:how to force Make to take the "shortest path" from sources to targets?如何强制 Make 采取从源到目标的“最短路径”?
【发布时间】:2013-10-22 21:46:08
【问题描述】:

我有这个Makefile(只是一个例子,没有实际意义)

all: foobar.intermediate foobar.a

define RECIPE_THROUGH_INTERMEDIATE
    cp $< $@.intermediate
    mv $@.intermediate $@
endef 

define RECIPE_DIRECT
    cp $< $@
endef

%.a: %.c
    $(RECIPE_THROUGH_INTERMEDIATE)

%.a: %.intermediate
    $(RECIPE_DIRECT)

%.intermediate: %.c
    $(RECIPE_DIRECT)

我希望 Make 尽可能“快”并像这样更新:

>make foobar.a

foobar.cfoobar.a 执行配方到中间层

但是

>make

直接从foobar.c 执行配方到foobar.intermediate

直接从foobar.intermediate 执行配方到foobar.a

换句话说,这是一种常见的情况,从源 (foobar.c) 生成目标 (foobar.a) 需要在内部通过中间体,这比从源到中间体需要更多时间,或者从中间到目标,单独,但是当您明确(不是内部)通过中间时,它花费的时间比这两者加起来要少。

当然,Make 不知道配方需要多长时间。

所以我想提示 Make what the correct path to take:

  • 当我只想要目标时,将直接配方从源带到目标
  • 当我想要目标和中间体时,先制作中间体,然后再从中获得目标。

我有 GNU Make 至少 3.82,但我无法控制模式词干的长度 - 这必须适用于任何长度的词干,包括,就像在这个例子中一样,所有词干的长度都相同。

有没有办法改变上面的 Makefile 来实现所需的行为?

【问题讨论】:

  • 我可以看到人们不理解这个问题。所以我说得更清楚了,希望如此。
  • 听起来/看起来您的 makefile 正在执行您所描述的操作——如果您执行make foobar.a,它会执行一步操作,如果您执行make all(默认目标)它同时产生foobar.intermediatefoobar.a(由此而来)。如果您希望 make all 不构建 foobar.intermediate,请将其从 all 的先决条件中删除...
  • @ChrisDodd 不,它没有,这就是重点,如上所述,make all 效率低下。我之所以具备all的先决条件,就是为了准确的说明问题。
  • 有趣的问题。如果在调用make 之前存在foobar.intermediate(并且是最新的),您希望make 的行为如何?

标签: makefile gnu-make


【解决方案1】:

您所期望的不满足 Makefile 规则, 如果您仔细查看代码,则可以观察到以下内容

all: foobar.b foobar.a

%.a: %.c                  // .a depends on .c
       from .c to .a

%.a: %.b                 // .a depends on .b
       from .b to .a

%.b: %.c                 // .b depends on .c
       from .c to .b

所以这里的 .a 取决于 .b 和 .c .. 因为 .a 和 .b 都取决于 .c 而 .a 取决于 .b 第一条规则 from .c to .a 由 make 优化。你得到from .c to .b 然后from .b to .a 没有办法让makefile找到最短路径,因为所有makefile想要做的就是确保满足所有依赖条件。

【讨论】:

  • 不,模式规则说:“我们知道如何从 .c 生成 .a:这就是方法”。他们没有断言.a 依赖于.c。当 make 搜索模式规则的空间以推断先决条件时,动态引入了依赖关系。可以对其进行优化以采用可用的快捷方式是合理的。
  • 这类似于编译器中的跳转线程优化。如果你有一个“goto x”,其中 x 是“goto y”的标签,那么“goto x”可以重写为“goto y”。
  • 是的,我知道 Sagar,你在说什么我知道。我的问题写得很仔细——我花了时间写它,所以它很精确。它说“将上面的 Makefile 更改为...”。
  • @Kaz。 go to example 是可以理解的,但是 makefile 在运行之前会做任何优化吗?因为有一条规则要“从 x 到 y”执行。如果没有规则,那么我确信 make 文件会进行优化。
  • 我想说,马克看到的问题与其说是缺乏优化,不如说是明确的“悲观化”。没有理由不以自然找到最短路径的广度优先方式追逐图。我关于 goto 跳转线程的陈述只是一种沉思。我想起了它。
【解决方案2】:

我无法使用 GNU Make 3.81 重现该行为。

显然,这不是真正的 Makefile,因为它调用了一个不存在的命令 from。我通过使用# 注释掉命令来更改它。 在我的目录中,我只有 makefile 和 foobar.c

$ make foobar.a
#from .c to .a

$ make foobar.b
#from .c to .b

只有当我注释掉 %.a : %.c 模式规则时,我才会得到这个:

$ make foobar.a
#from .c to .b
#from .b to .a

所以还有其他事情发生。也许这是 3.82 中的回归,或者您的实际 makefile 触发了一些错误,或者在我的情况下可能是侥幸选择了短路径。

很明显,当您请求生成foobar.a 时,Make 应该搜索其模式规则以推断可以生成foobar.a 的先决条件。这应该是广度优先搜索:它应该在模式规则中遇到%.a: %.c,看看,啊哈,我们有一个foobar.c 文件,我们走吧!只有当第一轮失败时,图闭包才应该被扩展以包括先决条件一个步骤被删除等等。这甚至是一个巧妙优化的问题;只是图遍历顺序的问题!

【讨论】:

  • 您可以通过放置 echo 来检查而不是评论吗? echo "从 .c 到 .a" 。如果您发表评论,则规则字段将为空,并且 make 将进行优化
  • @SagarSakre 我最初尝试使用更完整的生成文件来实际创建目标对象。还是一样的。
  • @SagarSakre 规则正文中的注释不为空,因为井号位于制表符之后!所以它被视为一个shell命令(你可以看到在规则评估时打印了注释,表明主体已被激活。)我认为这与执行回显没有太大区别。
【解决方案3】:

有两种不同的方式来构建相同的目标is explicitly not allowed in Gnu Make

所有规则中提到的所有先决条件都合并为一个 目标的先决条件列表。如果目标比任何目标更老 任何规则的先决条件,都会执行配方。

一个文件只能执行一个配方。如果超过 一条规则为同一个文件提供一个配方,make 使用最后一个 给出并打印错误消息。

不太可能支持基于依赖图上的一些任意度量来选择多个配方的概念。因此,为了回答您提出的问题,首先需要更改 Make 以支持多种替代方案。

如果您想提供一个“提示”,说明应该首选两种策略中的哪一种,您可能必须使用ifeq ... else ... endif 之类的条件来定义您想要的行为(如果提供了提示)以及想要的行为如果不。到构建依赖图时,目标只有一个配方。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-11
    • 1970-01-01
    相关资源
    最近更新 更多