GNU Make:在单个命令调用中批量处理多个目标

GNU Make: Batching multiple targets in single command invocation

考虑以下设置:

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

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

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

GNU make 中有没有一种方法可以指定多个 src 文件应该被批处理到对 convert 的单个调用中?
编辑:更准确地说,我正在寻找什么功能:假设 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 文件指定为其他规则中的先决条件。

提前致谢!

Is there a way in GNU make to specify that multiple src files should be batched into a single call to convert?

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

[if] 1.dst is already newer than 1.src [, and] I run make 1.dst 2.dst 3.dst, I would like GNU make to execute convert -o '{}.dst' 2.src 3.src.

。这是一个稍微不同的问题。您可以在配方中使用 $? 自动变量来获取比规则目标更新的先决条件,但为了达到目的,您需要一个带有 single[=31= 的规则] 目标。

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

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 文件是从存档中提取的,而不是从头开始重建的。

如果你有 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)