【问题标题】:GNU Make: Batching multiple targets in single command invocationGNU Make:在单个命令调用中批处理多个目标
【发布时间】:2020-12-04 12:38:19
【问题描述】:

考虑以下设置:

$ touch 1.src 2.src 3.src
$ cat Makefile
%.dst: %.src
    @convert -o "$@" "$<" 

我们可以通过运行make 1.dst 2.dst 3.dst.src 文件编译成.dst 文件,这会调用convert(只是一个占位符)工具三次。

如果调用convert 的开销很小,则此设置很好。但是,在我的情况下,每次调用都会有几秒钟的启动惩罚。幸运的是,该工具可以在一次调用中转换多个文件,同时只需支付一次启动费用,即convert -o '{}.dst' 1.src 2.src 3.src

在 GNU make 中是否有办法指定多个 src 文件应批处理到对 convert 的一次调用中?
编辑:更准确地说,我在寻找什么功能for:假设1.dst 已经比1.src 更新,所以不需要重新编译。如果我运行make 1.dst 2.dst 3.dst,我希望GNU make 执行convert -o '{}.dst' 2.src 3.src

一种快速而肮脏的方法是创建一个 .PHONY 规则,它只是将所有 src 文件转换为 dst 文件,但这样我每次都会转换每个 src 文件。此外,在其他规则中将 dst 文件指定为先决条件也不再可能。

提前致谢!

【问题讨论】:

  • 我不会这样做。当单个文件从另一个单个文件中出来时,使用make -j可以有效地并行运行。一步从多个文件中创建多个目标很笨重(正如您从建议的答案中看到的那样),Makefile 变得更加复杂且难以维护,并且您可能会遇到不同的极端情况,它会以不同的方式成为瓶颈。我会考虑减少启动开销是否值得未来维护。也许一个简单的并行 make 就足够了?
  • 我实际上在尽可能使用make -jconvert 工具的问题是启动需要大约 3 秒,编译源代码不到一秒。即使我并行运行它,它也会一遍又一遍地在同一个启动过程上花费大量时间。我不确定是否可以减少启动开销。该工具是用 python 编写的,可能会在解释器中花费启动时间。

标签: makefile build gnu


【解决方案1】:

如果你有 GNU make 4.3 或更高版本,你可以像这样使用grouped targets

DST_FILES = 1.dst 2.dst 3.dst
SRC_FILES = $(_DST_FILES:.dst=.src)

all: $(DST_FILES)

$(DST_FILES) &: $(SRC_FILES)
        convert -o '{}.dst' $?
        @touch $(DST_FILES)

如果您的 convert 仅更新部分目标,那么您需要显式的 touch 来更新其余目标。

这是一种在命令行上传递目标的方法,它可能有效;将DST_FILES 更改为:

DST_FILES := $(or $(filter %.dst,$(MAKECMDGOALS)),1.dst 2.dst 3.dst)

【讨论】:

  • 谢谢,看起来很干净!有一件事让我感到不安:如果我运行make 2.dst,它会编译所有 src 文件(比它们相应的 dst 文件更新),尽管我只要求 make 编译 2.dst。这可以修复吗?
  • 你确定你有 GNU make 4.3 吗?我无法重现这种行为。我将添加一个我所做的示例。
  • 哦,我明白你的意思了,你有一个目标。是的,你是对的,它总是会构建所有的目标(这就是 grouped targets 的意思)。如果您不想要这种行为,则不能使用此解决方案。当然,您没有在问题中将其列为要求:)
  • 没错,我没有明确说明这一点。这不是什么大问题,但您知道解决该问题的简单方法吗?
  • 嗯。也许,这取决于您的 makefile 还需要做什么。我将编辑我的答案。
【解决方案2】:

在 GNU make 中有没有办法指定多个 src 文件应该被批处理到对 convert 的单个调用中?

为生成多个目标的构建步骤编写make 规则是可能的,但很麻烦,只需运行一次配方,这样如果需要更新任何目标,配方只执行一次。但是,您澄清了这一点

[如果] 1.dst 已经比1.src 更新[,并且] 我运行make 1.dst 2.dst 3.dst,我希望GNU make 执行convert -o '{}.dst' 2.src 3.src

。这是一个稍微不同的问题。您可以在配方中使用 $? 自动变量来获取比规则目标更新的先决条件,但要达到此目的,您需要一个具有单个目标的规则。 p>

这里有一种稍微复杂的方法来使它工作:

DST_FILES = 1.dst 2.dst 3.dst
SRC_FILES = $(DST_FILES:.dst=.src)

$(DST_FILES): dst.a
        ar x $< $@

dst.a: $(SRC_FILES)
        convert -o '{}.dst' $?
        x='$?'; ar cr $@ $${x//src/dst}

dst.a归档作为一个目标,所有.src文件作为前提条件,为$?的使用提供基础。此外,它还提供了一种解决方法,即每当更新该目标时,它就会比所有当时存在的 .dst 文件更新:.dst 文件相对于存档已过期,但相对于相应的 .dst 文件则没有。 src 文件是从存档中提取的,而不是从头开始重建。

【讨论】:

  • 注意$${x//src/dst} 是一个不可移植的bash-ism:如果你想使用它,你需要将SHELL = /bin/bash 添加到你的makefile。
  • 我真的不认为需要ar 的东西。为什么不将配方从 ar x $&lt; $@ 更改为 touch $@ 并将 dst.a 更改为 .sentinel 或隐藏它?
  • 感谢您的回答!它看起来像只制作目标文件的一个子集,例如make 2.dst 它仍然会编译所有源文件,对吗?有办法解决吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 2011-01-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多