GNU Make’s Two Personalities: Understanding Simple (:=) vs. Recursive (=) Variables
Updated: 2025-10 Published: 2025-08
There are two major flavours of variables in GNU Make: “simple” and “recursive”.
While simple variables are quite simple and easy to understand, they can be limiting at times. On the other hand, recursive variables are powerful yet tricky.
Is your Make variable a known value, or is it in a state of quantum superposition until expanded!?
Basics
Let’s review the definition of the two flavours.
Simple variable
The value of a simple variable is computed exactly once no matter how many times it is expanded. More importantly, the value is computed when the variable is defined and not when it is used.
For example, in the snippet below, the value of a-simple-var is computed only on line #1 and is simply reused on lines #4 and #5.
a-simple-var := ...
target1 :
echo $(a-simple-var)
echo $(a-simple-var)Recursive variable
The value of a recursive variable is computed every time it is expanded. Unlike a simple variable, the value is not computed when the variable is defined.
For example, in the snippet below, line #1 is just an instruction (recipe?) for Make as to how to compute the value of a-recursive-var. Then on lines #4 and #5 that instruction is used to compute the value.
a-recursive-var = ...
target1 :
echo $(a-recursive-var)
echo $(a-recursive-var)As you can see, a recursive variable is more like a function in a programming language. The body of the recursive variable (function) is executed every time it is referenced.
Ironically, despite its name, a recursive variable cannot reference itself!
Recursive Variable Gotcha
Recursive variables are quite powerful as they introduce a pinch of imperative programming into the otherwise totally declarative nature of a Makefile.
However, that different paradigm (imperative) may cause hard to debug behaviour; in other words failing to remember that recursive variables are more like functions can be a recipe for trouble.
Avoiding those surprises is easy. There is exactly one rule to recall when using recursive variables as outlined earlier.
The value of a recursive variable is computed every time it is expanded.
Show Me The Code!
“A Makefile is worth a thousand words”, as the ancient saying goes!
Makefile
#################################################################
# Makefile
#################################################################
SHELL := /usr/bin/env -S bash -o pipefail
.DEFAULT_GOAL := all
#################################################################
define a-recursive-var =
$(info a-recursive-var is computed.)$(strip a-recursive-var)
endef
define a-simple-var :=
$(info a-simple-var is computed.)$(strip a-simple-var)
endef
.PHONY : demonstrating-text-functions
demonstrating-text-functions :
@echo \
; echo $(@) \
; echo ' $(a-recursive-var)' \
; echo ' $(a-recursive-var)' \
; echo ' $(a-recursive-var)' \
; echo ' $(a-simple-var)' \
; echo ' $(a-simple-var)' \
; echo ' $(a-simple-var)' \
; echo
#################################################################
timestamp = $(shell perl -MTime::HiRes=time -E'say time()')
.PHONY : demonstrating-shell-function
demonstrating-shell-function :
@echo \
; echo $(@) \
; export ts1='$(timestamp)' \
; export ts2='$(timestamp)' \
; [[ "$$ts1" == "$$ts2" ]] \
&& echo " ts1 = ts2" \
|| echo " ts1 ≠ ts2" \
; echo
#################################################################
.PHONY : all
all : demonstrating-shell-function
all : demonstrating-text-functionsOutput
a-simple-var is computed.
demonstrating-shell-function
ts1 ≠ ts2
a-recursive-var is computed.
a-recursive-var is computed.
a-recursive-var is computed.
demonstrating-text-functions
a-recursive-var
a-recursive-var
a-recursive-var
a-simple-var
a-simple-var
a-simple-var
Takeaways
a-simple-varis computed beforea-recursive-varis computed and any target is made.a-simple-varis computed only once.- Recursive variable
timestampproduces different results on lines #37 and #38 in the Makefile. a-recursive-varis computed as many times as it is expanded and only when it is expanded.
Don’t forget to join the Makefile chatter on Matrix or visit the !makefile@lemmy.ml community.
As always, the GNU Make manual is your friend. Read the section The Two Flavors of Variables if you need more information.
Thanks a lot, it helps with your example.
ReplyDeleteGreat blog poost
ReplyDelete