Makefile:如何为两个文件列表设置先决条件



我有两个文件列表作为先决条件

  • input_i.xx
  • config_j.yy

我需要运行它们的所有组合。单个的如下所示:

input1_config3.output: input1.xx config3.yy
run_script $^

同样在现实中,它们的名称没有编号,但我已经在INPUTSCONFIGS中定义了它们的茎。这样,我就可以生成所有的目标

TARGETS:=$(foreach input,$(INPUTS),$(foreach config,$(CONFIGS),$(input)_$(config).output))

但是我对先决条件有困难。看来我需要

  1. 得到basename
  2. _上分割
  3. 添加扩展名.xx.yy
.SECONDEXPANSION
$(TARGETS): $(basename $@)
run_script $^
谁能告诉我怎么做?不确定这是不是正确的方法,也许自下而上的方法更简单?

make实际上不适合跟踪结果的M x N矩阵。最根本的问题是你不能在一个规则中有两个词干,所以你不能说像

这样的东西。
# BROKEN
input%{X}_config%{Y}.output: input%{X}.xx config%{Y}.yy

作为一个粗略的近似,您可以使用递归make规则来设置几个参数,并从那里开始,但这相当笨拙。

.PHONY: all
all:
$(MAKE) -$(MAKEFLAGS) X=1 Y=6 input1_config6.output
$(MAKE) -$(MAKEFLAGS) X=1 Y=7 input1_config7.output
$(MAKE) -$(MAKEFLAGS) X=2 Y=6 input2_config6.output
:

input$X_config$Y.output: input$X.xx config$Y.yy
run_script $^

如果您提供一个完整的示例示例,其中包含完整的目标集和先决条件以及您想要发生的事情,则会容易得多。

使用.SECONDEXPANSION可能工作,但你没有正确使用它;请重新阅读文档。.SECONDEXPANSION的关键之处在于,您必须转义您希望在第二次执行之前避免展开的变量。在您的示例中,您没有转义任何内容,因此.SECONDEXPANSION实际上在这里根本没有做任何事情。然而,正如@tripleee指出的那样,在单个目标中使用多个变量值并不容易。

为了更容易地做到这一点,您可能需要使用eval。像这样:

define DECLARE
$1_$2.output: $1.xx $2.yy
TARGETS += $1_$2.output
endef
TARGETS :=
$(foreach input,$(INPUTS),$(foreach config,$(CONFIGS),$(eval $(call DECLARE,$(input),$(config)))))
$(TARGETS):
run_script $^

我有另一个解决方案使用include和bashfor循环。

include trees.mk
trees.mk:
@for input in $(INPUTS); do 
for config in $(CONFIGS); do 
echo $${input}_$$config.output : $${input}.xx $$config.yy; 
echo -e 't run_scipt $$^ ';
done 
done > $@

开始时,trees.mk不存在。双for循环使用文件重定向>$@将规则写入目标。

我从Robert Mecklenburg的《用GNU Make管理项目,第三版》中得到这个想法56页