GNU Make's Two Personalities: Understanding Simple (:=) vs. Recursive (=) Variables
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
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.
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
a-recursive-var = ...
target1 :
echo $(a-recursive-var)
echo $(a-recursive-var)
Ironcially, despite its name, a recursive variable cannot reference itself!
Recursive Variable Gotcha
The value of a recursive variable is computed every time it is expanded.
Show Me The Code!
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-functions
Output
💡 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-var
is computed beforea-recursive-var
is computed and any target is made. a-simple-var
is computed only once.-
Recursive variable
timestamp
produces different results on lines #37 and #38 in the Makefile. -
a-recursive-var
is 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, 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