为何通配符在静态模式规则的先决条件中无法生效?
目录结构
src/whatever/foo Makefile
Makefile示例
out/bar : src/*/foo @echo $^ # ----> 正常执行,输出: src/whatever/foo out/foo : out/% : src/*/% @echo $^ # ----> 错误: No rule to make target 'src/*/foo'
执行make out/foo为何无法正常工作?通配符在静态模式规则中的表现为何与普通规则不同?
有相关讨论提到“通配符在静态模式规则中不受支持”,但GNU Make手册中未找到明确说明。
已知可以通过$$(wildcard...)结合.SECONDEXPANSION让示例正常运行,但为何必须这么做?
编辑1
这可能和文件名/模式的区别有关。以下写法同样无法生效:
out2/foo : out2/% : $(wildcard src/*/%) @echo $^ ### ----> 错误,无输出
尽管按手册说明,wildcard函数应能展开参数中的星号(手册称其为“文件名模式”)。
1. 普通规则与静态模式规则的通配符处理逻辑差异
普通规则的依赖列表(比如src/*/foo)会在读取Makefile阶段自动触发通配符展开——make会直接扫描文件系统,把*替换成匹配的目录/文件名,所以src/*/foo能正确展开为src/whatever/foo。
但静态模式规则的结构是目标列表: 模式: 依赖模式,这里的src/*/%属于依赖模式,make会把它当作一个带模式占位符%的模板,而不是普通的文件名通配符。在解析静态模式规则时,make不会自动展开这里的*,而是把src/*/%整体当作一个包含%的模式字符串,试图寻找匹配src/*/%格式的文件——但实际文件是src/whatever/foo,*在这里不是通配符,而是字面量,自然找不到对应的文件,所以报错。
2. 直接用$(wildcard src/*/%)无效的原因
$(wildcard ...)是在读取Makefile阶段执行的,此时%还没有被替换成具体的stem(这里是foo),src/*/%中的%会被当作字面量字符处理。而你的文件系统里没有src/*/%这种格式的文件,所以wildcard返回空,依赖列表为空,执行时自然没有输出。
3. 为何需要.SECONDEXPANSION和$$(wildcard...)
.SECONDEXPANSION会让make对规则的依赖进行二次展开:第一次展开在读取Makefile阶段,第二次展开在匹配到具体目标、确定stem之后。
使用$$(wildcard...)时,第一次展开会把$$变成$,得到$(wildcard src/*/foo);等到二次展开阶段,%已经被替换成了实际的stemfoo,此时wildcard函数才会正确扫描文件系统,展开src/*/foo为真实存在的文件路径,依赖才能正确匹配。
内容的提问来源于stack exchange,提问作者L. Bruce

