Variables in GNU Make: Simple and Recursive
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.
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.
💡
It's worth noting that 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 say 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-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