在 Makefile 中使用 `subst` 函数匹配依赖项

Matching a dependency using `subst` function in Makefile

我有一个目标规则

data/processed/21.12.2021/experiment6/written_piv21122021.005.exp6.mp4

在我的 makefile 中使其具有依赖性

data/raw/21.12.2021/experiment6/piv21122021.005.exp6.mov

使用 subst 函数,我正在尝试通过模式匹配创建依赖关系

%/written_*.mp4: \
$(subst processed,raw, $$*)/*.mov \
    <do something>

但是,上述规则无法找到*.mov 依赖项。我已经尝试了很多版本的 $(subst processed,raw, $$*)/*.mov 来匹配依赖但没有用。

如何做到这一点?正确的语法是什么?

首先,你不能这样做:

%/written_*.mp4:

您不能将模式 % 与通配符 * 结合使用。您必须意识到 make 的工作分为两个非常独立的步骤:首先,解析所有 makefile 并将所有目标和先决条件的内部表示构建到一个图中。然后,遍历该图,弄清楚需要构建什么以及如何构建它,以及 运行 食谱。

在解析 makefile 时(在第一步中)扩展目标和先决条件中的 Make 变量、函数和通配符。像 $* 这样的自动变量在调用配方(第二步)之前不会被设置,而像 % 这样的模式在 make 尝试决定如何构建某些东西之前不会 matched/expanded (再次在第二步)。

所以,规则如下:

%/written_*.mp4: $(subst processed,raw, $$*)/*.mov

无法工作,因为通配符将扩展为与文字文件名 %/written_*.mp4 相匹配的文件,但显然没有匹配项,因为您没有名为 % 的目录。在任何情况下,你都不能在 targets 中使用通配符,因为当 make 解析 makefile 时,这些目标将不存在(因为这是你想要构建的目标)所以通配符不会匹配任何东西。此外,$$* 是文字字符串 $* 并且其中没有 processed 字符串,因此 subst 函数将不执行任何操作。而且,即使 /*.mov 确实匹配了某些东西,它也会将所有匹配该通配符的文件作为 每个 目标的先决条件,这样只要任何一个改变,它们就会全部重建。

最后,你绝对不应该在你的先决条件之后使用反斜杠:这只会把你的食谱变成先决条件。

您的问题很难解决,因为您的目标和先决条件在多个不同的地方不同,并且 make 不支持多个 % 匹配。您可以通过以下方式完成大部分工作:

data/processed/%.mp4: data/raw/%.mov
        <do something>

然而这并不完全正确,因为目标中的 %written_... 而在先决条件中它只是 ... 而这在 make 中是不可能表示的。

如果您可以修改文件名,以便您可以使用 piv21122021.005.exp6.written.mp4 而不是 written_piv21122021.005.exp6.mp4(或者更好的是您根本不需要 written_ 前缀),那么您可以轻松做到这一点。如果不是,你需要非常喜欢才能完成这项工作。

这是一个不完美的拼凑。

将工作委托给另一个 makefile,我称之为 adjunct.mk。在主生成文件中:

data/processed/%.mp4:
    @$(MAKE) -f adjunct.mk $@

并且在 adjunct.mk 这个丑陋的转变中:

.SECONDEXPANSION:
$(MAKECMDGOALS): $$(patsubst data/processed/%,data/raw/%,$$(subst written_,,$$(patsubst %.mp4,%.mov,$$@)))
    ...do whatever with $< and $@...

这会招致递归 Make 的通常成本:它使 higher-level Make 对较低层跟踪的依赖关系视而不见。因此,如果您的 Make 必须构建那个 mov 文件,或者您在 mov 不存在时尝试构建 mp4,那么此解决方案将需要更仔细的管道安装才能正常工作.