如何在GNU Make中结合SECONDEXPANSION实现规则通配?
问题根源
当结合.SECONDEXPANSION使用$(PROJECT).%模式规则时,若$(PROJECT)是延迟展开变量(用=定义)或在规则解析阶段未完成赋值,第一次展开目标模式时$(PROJECT)不会被替换,导致Make无法识别%作为后缀匹配符,规则失效。即使$(PROJECT)是立即赋值变量,若依赖中需要引用后缀对应的变量(如$(foo_DEPS)),也需确保二次展开时能正确捕获%对应的后缀。
正确替换方法
1. 若$(PROJECT)为立即赋值变量(静态值)
直接使用$(PROJECT).%作为模式,结合二次展开引用后缀变量:
# 立即赋值PROJECT,确保第一次展开时能替换 PROJECT := myapp # 定义对应后缀的依赖变量 foo_DEPS := src/foo.c src/common.c bar_DEPS := src/bar.c src/common.c .SECONDEXPANSION: # 模式规则匹配myapp.foo、myapp.bar等目标 $(PROJECT).%: $$($$*_DEPS) @echo 编译目标: $@ gcc -o $@ $^
关键逻辑:
PROJECT用:=立即赋值,第一次展开目标模式时替换为myapp.%,%正确匹配后缀(foo/bar)- 依赖中的
$$($$*_DEPS)经过二次展开:第一次展开为$($*_DEPS),第二次展开时$*被替换为当前后缀(foo/bar),最终得到$(foo_DEPS)或$(bar_DEPS)
2. 若$(PROJECT)为延迟展开变量(动态值)
使用$(eval)动态生成模式规则,确保PROJECT赋值后再解析规则:
# 延迟赋值PROJECT(后续可能修改) PROJECT = myapp foo_DEPS := src/foo.c src/common.c bar_DEPS := src/bar.c src/common.c # 定义规则模板 define PROJECT_TEMPLATE $(PROJECT).%: $$($$*_DEPS) @echo 编译目标: $@ gcc -o $@ $^ endef .SECONDEXPANSION: # 动态生成规则,此时PROJECT已完成赋值 $(eval $(PROJECT_TEMPLATE))
$(eval)会在Makefile解析到该行时执行,此时PROJECT的最新值会被替换到模板中,生成正确的myapp.%模式规则。
3. 若$(PROJECT)是多项目列表
用$(foreach)遍历项目,生成每个项目的模式规则:
PROJECTS := app1 app2 foo_DEPS := src/foo.c src/common.c bar_DEPS := src/bar.c src/common.c .SECONDEXPANSION: # 为每个项目生成对应的模式规则 $(foreach proj,$(PROJECTS),$(proj).%): $$($$*_DEPS) @echo 编译目标: $@ gcc -o $@ $^
$(foreach)会展开为app1.% app2.%,每个都是独立的模式规则,分别匹配app1.foo、app2.bar等目标。
验证方法
执行make myapp.foo或make myapp.bar,若能正确加载对应依赖并执行编译命令,说明规则生效。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

