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 before a-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.

Comments

Post a Comment

Popular posts from this blog

Checkmate on Your Terms: A Personal Journey with Correspondence Chess

Firefox profiles: Quickly replicate your settings to any machine