我有两个文件列表作为先决条件
input_i.xx
config_j.yy
我需要运行它们的所有组合。单个的如下所示:
input1_config3.output: input1.xx config3.yy
run_script $^
同样在现实中,它们的名称没有编号,但我已经在INPUTS
和CONFIGS
中定义了它们的茎。这样,我就可以生成所有的目标
TARGETS:=$(foreach input,$(INPUTS),$(foreach config,$(CONFIGS),$(input)_$(config).output))
但是我对先决条件有困难。看来我需要
- 得到basename
- 在
_
上分割 添加扩展名
.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页