Make:是否可以从模式匹配中构建依赖关系?

Make: Is it possible to have dependencies constructed from a pattern match?

这里的目标是动态适应和处理 results-0, results-1, ..., results-n 作为 results-* 出现在 makefile 的目录中,我调用 make有一个 cron 作业。

我正在使用的 make gnu-make vers. 4.3 通过 brew 安装在 mac 上。


我编写了以下 MVP,它代表了我的实际问题,并且有效。请注意,在我的实际问题中,没有完整的通配符链,基本问题由被注释掉和未注释掉的区域表示:

sources:=result-0/foo.txt result-0/bar.txt result-1/foo.txt result-1/bar.txt
results:=result-0.txt result-1.txt

result-%/:
  @-mkdir $@

result-0/%.txt: result-0
  @touch $@

result-1/%.txt: result-1
  @touch $@


#.SECONDEXPANSION:
#result-%.txt: $$(filter $$(basename $$@)/%,$$(sources))
# @echo $^ args of: $@
# @touch $@

.SECONDEXPANSION:
result-0.txt: $$(filter $$(basename $$@)/%,$$(sources))
  @echo $^ args of: $@
  @touch $@

.SECONDEXPANSION:
result-1.txt: $$(filter $$(basename $$@)/%,$$(sources))
  @echo $^ args of: $@
  @touch $@

all: $(results)

我用命令构建它:

mkdir result-0
touch result-0/foo.txt
touch result-0/bar.txt
echo result-0/foo.txt result-0/bar.txt args of: result-0.txt
touch result-0.txt
mkdir result-1
touch result-1/foo.txt
touch result-1/bar.txt
echo result-1/foo.txt result-1/bar.txt args of: result-1.txt
touch result-1.txt
rm result-1

这表示首次构建成功。


但是,当我对 result-[0...n] 中的抽象进行以下更改时,系统中断:

sources:=result-0/foo.txt result-0/bar.txt result-1/foo.txt result-1/bar.txt
results:=result-0.txt result-1.txt

result-%/:
  @-mkdir $@

result-0/%.txt: result-0
  @touch $@

result-1/%.txt: result-1
  @touch $@

.SECONDEXPANSION:
result-%.txt: $$(filter $$(basename $$@)/%,$$(sources))
  @echo $^ args of: $@
  @touch $@

all: $(results)

系统故障:

$ make all -Bn
mkdir result-0.txt
mkdir result-1.txt

注意到我可以简单地进行以下更改:

# instead of 
result-%.txt: $$(filter $$(basename $$@)/%,$$(sources))

# I could write
$(results): $$(filter $$(basename $$@)/%,$$(sources))

这是我所知道的,而且我可以轻松地完成这项工作(我有工作代码)。这不是我要问的问题。我不是“我该如何调试”。

问:“为什么下面的规则表达式没有填充第二个扩展中的先决条件?”

.SECONDEXPANSION:
results-%:  $$(filter $$(basename $$@)/%,$$(sources))

围绕这个问题的一切都不是努力激发这个问题,而是 证明 问题的努力。如果有另一种方法可以“使这个示例工作”,那超出了范围。


make 可以吗?这是您在 Mac 上看到的奇怪问题之一吗?

而且,如果现在的问题似乎是 result-0/%.txt 在链上进一步获取模式匹配,那实际上并不是我要处理的问题。我开发了一个没有文件的复制粘贴演示,如果我无意中添加了此模式扩展可能失败的另一个原因,那么我的问题演示就搞砸了。所以如果这是问题所在,我可以重新解决这个问题。

不确定你到底想达到什么目的,所以我只是根据你认为成功的构建来猜测。我将使用简单的 static pattern rule 创建源文件,并且,正如您自己尝试的那样,使用第二个扩展来列出结果的依赖关系。以下应该与您想要的相差不远:

sources:=result-0/foo.txt result-0/bar.txt result-1/foo.txt result-1/bar.txt
results:=result-0.txt result-1.txt

.PHONY: all
all: $(results)

$(sources): %.txt:
    @mkdir -p $(@D)
    @touch $@

.SECONDEXPANSION:
$(results): $$(filter $$(basename $$@)/%,$(sources))
    @echo $^ args of: $@
    @touch $@

I am asking: "why isn't the following rule expression populating the pre-requisites in the second expansion?"

.SECONDEXPANSION:
results-%:  $$(filter $$(basename $$@)/%,$$(sources))

奇怪的是,您的麻烦 makefile 工作正常,我认为您对 GNU make 4.0 版的预期是这样的:

> make --version
GNU Make 4.0
Built for x86_64-unknown-linux-gnu
Copyright (C) 1988-2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
>
> make -Bn
echo  args of: result-0.txt
touch result-0.txt
echo  args of: result-1.txt
touch result-1.txt

我检查了版本 4.1、4.2 和 4.3 的发行说明,但没有看到任何我认为描述此更改的内容。不过,我想我可以解释一下 4.3 版中发生了什么。

第一次展开产生这条规则:

results-%:  $(filter $(basename $@)/%,$(sources))

您希望 make 将其与 results-0 等目标相匹配,在该赋值范围内展开 $@,然后计算 filter 函数。但是函数参数中的 % 呢?

您依赖先决条件列表中的 % 按字面中继到 filter,但是当 make 评估模式规则时,它会替换目标的匹配词干在先决条件列表中出现 %。在这种情况下,看起来 make 在评估函数调用之前就这样做了,因此生成的先决条件列表与现有或可构建的目标不匹配。结果,该规则被拒绝。

这基本上是执行顺序的差异。如果在执行词干替换之前扩展函数调用,那么将获得您想要的结果,但如果首先执行词干替换,那么您的规则将无法按预期工作。我没有发现这个具体的细节被明确地记录下来,但是我对模式规则的一般行为的阅读让我期望首先执行词干替换(所以我判断 GNU make 4.0 在这方面是错误的)。

无论如何,这似乎与第二次资料片没有直接关系。我希望类似的执行顺序效果适用于任何试图在先决条件列表中使用 % 作为文字字符的模式规则。