You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

简单展开与递归展开变量赋值的差异及相关技术问题

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目标中体现?

有两种可行方案:

  1. 目标特定变量:给runsim目标单独定义SUB_R,确保在目标执行时取最新的RT_LIM值:
    runsim: SUB_R := $(RT_LIM)
    runsim:
        @echo SUB_R: $(SUB_R)
        @echo SUB_L: $(SUB_L)
    
  2. 动态重新赋值:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 07:42:49