简单展开与递归展开变量赋值的差异及相关技术问题
GNU Make简单展开与递归展开变量赋值问题解答
附示例Makefile代码:
RT_LIM ?= 08:00:00 SUB_R := $(RT_LIM) SUB_L = $(RT_LIM) echo_% : ; @echo $* = $($*) runsim: @echo SUB_R: $(SUB_R) @echo SUB_L: $(SUB_L) # reassign to different value afterwards, such as in a different included file RT_LIM = 09:00:00
问题1:为何执行make echo_RT_LIM能显示RT_LIM的更新值,但SUB_R却不行?
- 简单展开变量(
:=)是立即求值:SUB_R := $(RT_LIM)在定义时就把当时RT_LIM的取值(08:00:00)直接存入SUB_R,后续RT_LIM的修改不会影响已经固化的SUB_R值。 - 递归展开变量(
=)是延迟求值:RT_LIM最后一次赋值是09:00:00,当执行make echo_RT_LIM时,才会展开RT_LIM的最终值,所以能显示更新后的结果。
问题2:若SUB_R使用简单展开变量赋值:=,是否有办法让用户重新赋值RT_LIM后,其更新值能在早期定义的runsim目标中体现?
有两种可行方案:
- 目标特定变量:给runsim目标单独定义SUB_R,确保在目标执行时取最新的RT_LIM值:
runsim: SUB_R := $(RT_LIM) runsim: @echo SUB_R: $(SUB_R) @echo SUB_L: $(SUB_L) - 动态重新赋值:在RT_LIM更新后,用
$(eval)重新计算SUB_R,比如在包含的外部文件末尾添加:$(eval SUB_R := $(RT_LIM))
问题3:递归展开变量赋值在文本转换函数场景下会导致make运行变慢,能否结合简单示例详细说明?
递归变量每次被引用时,都会重新展开其中所有的函数和变量,而简单变量只在定义时计算一次。举个实际场景:
# 递归变量:每次引用都会重新执行subst函数 REC_VAR = $(subst .c,.o,$(wildcard src/*.c)) # 简单变量:仅在定义时执行一次wildcard和subst SIM_VAR := $(subst .c,.o,$(wildcard src/*.c)) # 多次引用变量 all: @echo $(REC_VAR) @echo $(REC_VAR) @echo $(SIM_VAR) @echo $(SIM_VAR)
如果src/目录下有上百个.c文件,每次引用REC_VAR都会重新遍历目录、执行字符串替换;而SIM_VAR只做一次计算,后续引用直接复用结果。当变量被频繁引用或处理大量文件时,递归变量的重复计算会明显拖慢make的运行速度。
额外问题:改用递归展开变量赋值,在此场景下会有副作用吗?是否仍允许用户后续覆盖RT_LIM的值?
- 无明显副作用:将
SUB_R := $(RT_LIM)改为SUB_R = $(RT_LIM)后,SUB_R会随RT_LIM的更新自动同步,完全符合需求。唯一需要注意的是,如果SUB_R后续被用于复杂函数场景(如问题3中的文本转换),多次引用会触发重复计算,但当前场景仅为简单变量引用,性能影响可以忽略。 - 允许后续覆盖RT_LIM:用户在后续代码(或包含的外部文件)中重新赋值
RT_LIM完全有效,递归变量SUB_R每次被引用时都会取RT_LIM的最新值,实现动态配置的效果。
内容的提问来源于stack exchange,提问作者Tom Ma
相关产品推荐
相关产品推荐

