【问题标题】:Getting gmake to complete a recipe early让 gmake 尽早完成食谱
【发布时间】:2018-11-16 16:11:24
【问题描述】:

我知道以前有人问过这个问题,但我发现没有一个解决方案对我有用,因为它们是抗 DRY 的。

我有许多目标,这些目标依赖于无法轻易添加时间戳的事物,例如从另一个系统复制的文件。我希望能够做的是列出变量中的依赖项,例如nobuild=this,that,并假定这些目标是最新的。因为我有很多这样的,我不想想在每一个周围都ifdef;在伪代码上更可取的是像

ignorable-target: dependencies
        $(call ifnobuild,$@)
        .. rest of normal build steps ..

如果ignorable-targetnobuild 变量中被提及,则ifnobuild 宏扩展为某种exit-from-this-recipe-with-success gmake 指令。

我也想要进入多行连续的shell命令,以便将条件推迟到配方本身;我希望能够告诉 make“假设这些目标是最新的并且不要尝试构建它们”,因此我可以使用已经从有问题的配方中获得的本地副本测试其他方面。

gmake没有任何类型的 exit-recipe-with-success 机制,是吗?

[编辑希望能让情况更清楚。]

这是一个例子。目标remote1remote2 都涉及使用ssh 在远程系统上执行一些耗时的操作,然后将结果复制到本地。目标local1 是在本地构建的,不是时间汇。 target-under-work 取决于以上所有三个。

local1: local1.c Makefile

remote1: local1
        scp local1 remote-host:/tmp/
        ssh remote-host /tmp/local1 some-args # takes a long time
        scp remote-host:/tmp/local1.out remote1

remote2: local1
        scp local1 other-host:/tmp/
        ssh other-host /tmp/local1 other-args # takes a long time
        scp other-host:/tmp/local1.out remote2

target-under-work: local1 remote1 remote2
        do-something-with remote1,remote2

现在,当我只运行 make target-under-work 时,它将运行 remote1remote2 的配方。但是,这些文件的本地副本对于我的测试来说“足够好”,所以我希望它们每次都运行。一旦事情投入生产,它们每次都运行,但是当我开发target-under-work时,我只想使用已经构建的副本,我可以每天(或其他)重建它们以获得必要的测试粒度。

以上内容过于简单化了;有多个步骤和目标取决于remote1 和/或remote2。我知道如何通过使它们成为仅订购的先决条件来获得我想要的效果——但这意味着更改将它们作为先决条件的每个目标的依赖关系列表,而不是将 single 更改为remote1remote2 所以我可以使用命令行中的一些变量来告诉他们的食谱“假设它已经构建,如果已经有副本就不要实际构建它。”

我希望这能让我的问题更清楚。

【问题讨论】:

  • 如果一个目标永远不会被重建,为什么要为它制定一个规则?这些目标中的任何一个都是其他任何东西的先决条件吗?
  • 你知道order-only prerequisites吗?我可能错了,但我怀疑它们可能就是您要寻找的东西。
  • 并不是说它们永远不会被重建;他们确实得到重建。但是它们需要很长时间,当我在做依赖于它们的东西时,我想使用我上次构建的东西,而不是再次构建它。没有办法给它们加上时间戳,因为没有办法(不做所有的远程工作)来判断是否有任何变化。我希望能够有选择地说“这不需要重建”;完成后,它们都会正常构建。

标签: makefile build gnu-make exit recipe


【解决方案1】:

不,这个提前退出制作功能不存在。

请注意,您的问题可能未充分说明,因为当 slow 目标尚不存在时,您没有解释所需的行为。

让我们假设nobuild 中列出的慢速目标当且仅当它们不存在时才会被重建。您可以使用 make 函数来“隐藏”他们的先决条件列表,而不是使用 make 函数来提前退出他们的配方。这样,如果它们已经存在,即使它们已经过时,它们也不会被重建。唯一的微妙之处是您需要第二次扩展才能在先决条件列表中使用$@ 自动变量。在以下示例中,slow(您的remoteX)依赖于fast1(您的local1)。 fast2(你的target-under-work)依赖于fast1slow

host> cat Makefile
# Expands as empty string if $(1) exists and
# listed in $(nobuild). Else expands as $(2).
# $(1): target
# $(2): prerequisites
define HIDE_IF_NOBUILD
$(if $(wildcard $(1)),$(if $(filter $(1),$(nobuild)),,$(2)),$(2))
endef

nobuild :=

fast1:
    @echo 'build $@'
    @touch $@

fast2: fast1 slow
    @echo 'build $@'
    @touch $@

.SECONDEXPANSION:

slow: $$(call HIDE_IF_NOBUILD,$$@,fast1)
    @echo 'build $@'
    @touch $@

# Case 1: slow target not listed in nobuild and not existing
host> rm -f slow; touch fast1; make fast2
build slow
build fast2

# Case 2: slow target not listed in nobuild and existing and outdated
host> touch slow; sleep 2; touch fast1; make fast2
build slow
build fast2

# Case 3: slow target listed in nobuild and not existing
host> rm -f slow; touch fast1; make nobuild="slow" fast2
build slow
build fast2

# Case 4: slow target listed in nobuild and existing and outdated
host> touch slow; sleep 2; touch fast1; make nobuild="slow" fast2
build fast2

【讨论】:

  • 这是有道理的,尽管它比我希望的要复杂得多,而且它需要修改所有 目标 的先决条件,而不仅仅是先决条件本身的配方。我会玩这个。谢谢!
  • @RoUS 一定有什么我不明白的地方:我以为你想要的是修改 ignorable-targets 的配方。我的建议是修改他们的先决条件列表而不是配方。而且,在最后一个版本中,其方式似乎并不比您的 $(call ifnobuild,$@) 初始提案复杂。但是在您的评论中,您提到了 prereq 本身的配方。我在这里错过了什么?
  • 如果五个不同的目标依赖于长期运行的 prereq,则它们的每个配方都需要更新。如果您的示例是depend1depend2,我想在nobuild 中列出,因为这些依赖关系是时间密集型且难以时间戳的。有更好的吗?
  • @RoUS:那么,如果depend1depend2 列在nobuild 中,您想避免重新构建target?很简单,但未指定:如果只列出 depend1 而不是 depend2 怎么办?
  • 不,如果depend1nobuild 中,我希望它被视为已构建,而target 则像depend1 一样继续拥有已建成。这越来越令人困惑;让我修改原始问题文本。
猜你喜欢
  • 2019-02-15
  • 1970-01-01
  • 2014-11-06
  • 1970-01-01
  • 2015-12-13
  • 2019-01-29
  • 2021-11-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多