Makefile目标特定变量条件赋值未生效问题及方案咨询
Makefile目标特定变量的条件赋值问题与解决方案
问题重现
给定如下Makefile代码:
.PHONY: plus cond set plus: export FOO += plus plus: @echo plus is $(FOO) cond: export FOO ?= cond cond: plus @echo cond is $(FOO) set: export FOO := set set: cond @echo set is $(FOO)
执行make set时,预期输出:
plus is set plus cond is set set is set
但实际输出为:
plus is cond plus cond is cond set is set
从plus is cond plus能看出目标特定变量会传递给前置目标,但为什么cond目标里的条件赋值FOO ?= cond没识别到set目标已设置FOO?这是Bug吗?
原因分析
这不是Bug,是Makefile目标特定变量的赋值时机与作用域规则导致的:
- 目标特定的
export变量是在进入目标执行阶段才生效,而条件赋值?=属于Makefile解析阶段的操作。 - 当Make解析
cond: export FOO ?= cond时,全局或上层目标的FOO还未被设置(set的FOO := set是目标特定变量,仅在set目标的上下文生效),所以?=会直接给FOO赋值cond。 - 而目标特定的
+=是在执行目标时才追加,所以plus执行时会继承cond的FOO=cond,再追加plus得到cond plus。
无.SECONDEXPANSION的解决方案
要实现make cond输出cond is cond、make set输出cond is set的预期行为,以下两种方案可行:
方案1:Shell层面判断赋值
把条件判断放到shell执行阶段,此时目标传递的变量已生效:
.PHONY: plus cond set plus: export FOO += plus plus: @echo plus is $(FOO) cond: @if [ -z "$(FOO)" ]; then export FOO=cond; fi; echo cond is $$FOO cond: plus set: export FOO := set set: cond @echo set is $(FOO)
- 执行
make cond时,FOO为空,shell会赋值cond后输出; - 执行
make set时,FOO=set已存在,直接跳过赋值输出set。
方案2:利用目标特定变量的覆盖优先级
给cond设置默认目标特定变量,set用:=覆盖它:
.PHONY: plus cond set plus: export FOO += plus plus: @echo plus is $(FOO) cond: FOO = cond cond: export FOO cond: plus @echo cond is $(FOO) set: FOO := set set: export FOO set: cond @echo set is $(FOO)
make cond时,cond的FOO=cond生效,传递给plus后追加plus,输出符合预期;make set时,set的FOO:=set优先级高于cond的默认值,cond和plus都会使用FOO=set,完全匹配预期输出。
内容的提问来源于stack exchange,提问作者Old Pro
相关产品推荐
相关产品推荐

