946

Can anybody give a clear explanation of how variable assignment really works in Makefiles.

What is the difference between :

 VARIABLE = value
 VARIABLE ?= value
 VARIABLE := value
 VARIABLE += value

I have read the section in GNU Make's manual, but it still doesn't make sense to me.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
mmoris
  • 9,970
  • 3
  • 18
  • 12

6 Answers6

1223

Lazy Set

VARIABLE = value

Normal setting of a variable, but any other variables mentioned with the value field are recursively expanded with their value at the point at which the variable is used, not the one it had when it was declared

Immediate Set

VARIABLE := value

Setting of a variable with simple expansion of the values inside - values within it are expanded at declaration time.

Lazy Set If Absent

VARIABLE ?= value

Setting of a variable only if it doesn't have a value. value is always evaluated when VARIABLE is accessed. It is equivalent to

ifeq ($(origin VARIABLE), undefined)
  VARIABLE = value
endif

See the documentation for more details.

Append

VARIABLE += value

Appending the supplied value to the existing value (or setting to that value if the variable didn't exist)

ex1led
  • 427
  • 5
  • 21
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 30
    Does A += B expand B? That is if if I do A += B, and then B += C, would A evaluate to concatenation of ${B} and ${C}? – Anton Daneyko Feb 01 '13 at 12:46
  • 25
    As the linked section of the manual says. += operates according to whatever simple or recursive semantics the original assignment had. So yes, it will expand the RHS but whether it does that immediately or in a deferred manner depends on the type of the variable on the LHS. – Etan Reisner Mar 03 '13 at 21:02
  • 7
    What do you mean when you say variable value is expanded? – Sashko Lykhenko Feb 20 '15 at 21:45
  • 4
    @СашкоЛихенко have a look here to get meaning of expansion http://www.gnu.org/software/make/manual/make.html#Flavors – Umair R Feb 25 '15 at 09:12
  • 7
    is "set if absent" lazy or immediate? can i "lazy set if absent" and "immediate set if abset"? – Woodrow Barlow Apr 01 '16 at 14:55
  • 2
    So... In more simple terms `=` is referenced and `:=` is an actual copy by value? – basickarl Jun 16 '17 at 09:25
  • using `=` will mutate the reference variable, if any mutation happen. using `:=` will is clone the the reference, so if any mutation happen, it wont affect the reference variable https://riptutorial.com/makefile/example/21468/recursively-expanded-variables – JXLai Feb 26 '19 at 06:42
  • 1
    The `Lazy set` title is misleading, and `when the variable is used, not when it's declared` is unclear on one important matter: `=`-style variable is expanded EACH time it's evaluated. For example, `AAA=$(shell date)` can have different values in the same run. In make manual, is not called "lazy", it's called "recursively expanded". – Victor Sergienko May 25 '20 at 06:31
  • 2
    @VictorSergienko I do refer to "recursively expanded" in the explanation, and "lazy evaluation" is a well known term. – Alnitak May 27 '20 at 20:29
  • 2
    Right, and "Lazy evaluation" is NOT what `make` does here. Lazy evaluation works once, when a value is first accessed: https://en.wikipedia.org/wiki/Lazy_evaluation. `make` re-evaluates a recursively expanded variable each time. – Victor Sergienko May 27 '20 at 20:41
  • This reads way better than the GNU manual equivalent. – SoreDakeNoKoto Jan 01 '21 at 01:04
  • 2
    Is it worth mentioning the override directive here? If a the user set VARIABLE with a command-line argument (or a previous override) then none of these assignments should change VARIABLE unless preceded with the override directive. Example: override VARIABLE += value – LivingDust Jul 29 '21 at 19:57
  • 1
    @Etan Reisner: What if there is no original assignment (LHS) for an append (+=)? The first append creates the value, right? Will it be simple or recursive? I'm guessing recursive. – steve Oct 03 '21 at 17:17
  • I think the "Lazy Set" in this answer should be re-labeled as "Dynamic value" because it causes maximum possible re-computations for the value but allows it to dynamically change as a result of other things changing. It's closer to setting a reference to a closure that re-computes value on the fly every time. – Mikko Rantalainen Aug 16 '22 at 15:56
333

Using = causes the variable to be assigned a value. If the variable already had a value, it is replaced. This value will be expanded when it is used. For example:

HELLO = world
HELLO_WORLD = $(HELLO) world!

# This echoes "world world!"
echo $(HELLO_WORLD)

HELLO = hello

# This echoes "hello world!"
echo $(HELLO_WORLD)

Using := is similar to using =. However, instead of the value being expanded when it is used, it is expanded during the assignment. For example:

HELLO = world
HELLO_WORLD := $(HELLO) world!

# This echoes "world world!"
echo $(HELLO_WORLD)

HELLO = hello

# Still echoes "world world!"
echo $(HELLO_WORLD)

HELLO_WORLD := $(HELLO) world!

# This echoes "hello world!"
echo $(HELLO_WORLD)

Using ?= assigns the variable a value iff the variable was not previously assigned. If the variable was previously assigned a blank value (VAR=), it is still considered set I think. Otherwise, functions exactly like =.

Using += is like using =, but instead of replacing the value, the value is appended to the current one, with a space in between. If the variable was previously set with :=, it is expanded I think. The resulting value is expanded when it is used I think. For example:

HELLO_WORLD = hello
HELLO_WORLD += world!

# This echoes "hello world!"
echo $(HELLO_WORLD)

If something like HELLO_WORLD = $(HELLO_WORLD) world! were used, recursion would result, which would most likely end the execution of your Makefile. If A := $(A) $(B) were used, the result would not be the exact same as using += because B is expanded with := whereas += would not cause B to be expanded.

strager
  • 88,763
  • 26
  • 134
  • 176
  • 7
    a consequence of that is therefore `VARIABLE = literal` and `VARIABLE := literal` are always equivalent. Did I get that right? – aiao May 03 '14 at 21:43
  • 4
    @aiao, yes as literals are invariant to their uses – Sebastian May 15 '14 at 18:01
  • 1
    A subtle difference is:- ?: can improve performance in recursive called makefiles. For e.g if $? = $(shell some_command_that_runs_long_time). In recursive calls this will be evaluated only once. causing gains in build performance. := will be slower since the command is needlessly running multiple times – KeshV Sep 17 '17 at 02:33
79

I suggest you do some experiments using "make". Here is a simple demo, showing the difference between = and :=.

/* Filename: Makefile*/
x := foo
y := $(x) bar
x := later

a = foo
b = $(a) bar
a = later

test:
    @echo x - $(x)
    @echo y - $(y)
    @echo a - $(a)
    @echo b - $(b)

make test prints:

x - later
y - foo bar
a - later
b - later bar

Check more elaborate explanation here

akhy
  • 5,760
  • 6
  • 39
  • 60
nickand
  • 907
  • 6
  • 2
39

When you use VARIABLE = value, if value is actually a reference to another variable, then the value is only determined when VARIABLE is used. This is best illustrated with an example:

VAL = foo
VARIABLE = $(VAL)
VAL = bar

# VARIABLE and VAL will both evaluate to "bar"

When you use VARIABLE := value, you get the value of value as it is now. For example:

VAL = foo
VARIABLE := $(VAL)
VAL = bar

# VAL will evaluate to "bar", but VARIABLE will evaluate to "foo"

Using VARIABLE ?= val means that you only set the value of VARIABLE if VARIABLE is not set already. If it's not set already, the setting of the value is deferred until VARIABLE is used (as in example 1).

VARIABLE += value just appends value to VARIABLE. The actual value of value is determined as it was when it was initially set, using either = or :=.

mipadi
  • 398,885
  • 90
  • 523
  • 479
10

In the above answers, it is important to understand what is meant by "values are expanded at declaration/use time". Giving a value like *.c does not entail any expansion. It is only when this string is used by a command that it will maybe trigger some globbing. Similarly, a value like $(wildcard *.c) or $(shell ls *.c) does not entail any expansion and is completely evaluated at definition time even if we used := in the variable definition.

Try the following Makefile in directory where you have some C files:

VAR1 = *.c
VAR2 := *.c
VAR3 = $(wildcard *.c)
VAR4 := $(wildcard *.c)
VAR5 = $(shell ls *.c)
VAR6 := $(shell ls *.c)

all :
    touch foo.c
    @echo "now VAR1 = \"$(VAR1)\"" ; ls $(VAR1)
    @echo "now VAR2 = \"$(VAR2)\"" ; ls $(VAR2)
    @echo "now VAR3 = \"$(VAR3)\"" ; ls $(VAR3)
    @echo "now VAR4 = \"$(VAR4)\"" ; ls $(VAR4)
    @echo "now VAR5 = \"$(VAR5)\"" ; ls $(VAR5)
    @echo "now VAR6 = \"$(VAR6)\"" ; ls $(VAR6)
    rm -v foo.c

Running make will trigger a rule that creates an extra (empty) C file, called foo.c but none of the 6 variables has foo.c in its value.

phs
  • 541
  • 3
  • 18
  • This is a great call and has plenty of examples for expansion at declaration time, it'd be useful to extend the answer with an example and some words for expansion at use time – Robert Monfera Aug 05 '19 at 06:44
9

The most upvoted answer can be improved.

Let me refer to GNU Make manual "Setting variables" and "Flavors", and add some comments.

Recursively expanded variables

The value you specify is installed verbatim; if it contains references to other variables, these references are expanded whenever this variable is substituted (in the course of expanding some other string). When this happens, it is called recursive expansion.

foo = $(bar)

The catch: foo will be expanded to the value of $(bar) each time foo is evaluated, possibly resulting in different values. Surely you cannot call it "lazy"! This can surprise you if executed on midnight:

# This variable is haunted!
WHEN = $(shell date -I)

something:
    touch $(WHEN).flag

# If this is executed on 00:00:00:000, $(WHEN) will have a different value!
something-else-later: something
    test -f $(WHEN).flag || echo "Boo!"

Simply expanded variable

VARIABLE := value
VARIABLE ::= value

Variables defined with ‘:=’ or ‘::=’ are simply expanded variables.

Simply expanded variables are defined by lines using ‘:=’ or ‘::=’ [...]. Both forms are equivalent in GNU make; however only the ‘::=’ form is described by the POSIX standard [...] 2012.

The value of a simply expanded variable is scanned once and for all, expanding any references to other variables and functions, when the variable is defined.

Not much to add. It's evaluated immediately, including recursive expansion of, well, recursively expanded variables.

The catch: If VARIABLE refers to ANOTHER_VARIABLE:

VARIABLE := $(ANOTHER_VARIABLE)-yohoho

and ANOTHER_VARIABLE is not defined before this assignment, ANOTHER_VARIABLE will expand to an empty value.

Assign if not set

FOO ?= bar

is equivalent to

ifeq ($(origin FOO), undefined)
FOO = bar
endif

where $(origin FOO) equals to undefined only if the variable was not set at all.

The catch: if FOO was set to an empty string, either in makefiles, shell environment, or command line overrides, it will not be assigned bar.

Appending

VAR += bar

Appending:

When the variable in question has not been defined before, ‘+=’ acts just like normal ‘=’: it defines a recursively-expanded variable. However, when there is a previous definition, exactly what ‘+=’ does depends on what flavor of variable you defined originally.

So, this will print foo bar:

VAR = foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))

but this will print foo:

VAR := foo
# ... a mile of code
VAR += $(BAR)
BAR = bar
$(info $(VAR))

The catch is that += behaves differently depending on what type of variable VAR was assigned before.

Multiline values

The syntax to assign multiline value to a variable is:

define VAR_NAME :=
line
line
endef

or

define VAR_NAME =
line
line
endef

Assignment operator can be omitted, then it creates a recursively-expanded variable.

define VAR_NAME
line
line
endef

The last newline before endef is removed.

Bonus: the shell assignment operator ‘!=’

 HASH != printf '\043'

is the same as

HASH := $(shell printf '\043')

Don't use it. $(shell) call is more readable, and the usage of both in a makefiles is highly discouraged. At least, $(shell) follows Joel's advice and makes wrong code look obviously wrong.

Victor Sergienko
  • 13,115
  • 3
  • 57
  • 91
  • Are `?=` and `=` equivalent when defining macros that are intended to be overridden by environment variables, like `CFLAGS`/`CPPFLAGS`/`LDFLAGS`? – shadowtalker Aug 17 '21 at 13:55
  • 1
    @shadowtalker, no. Make variables default to environment variables, but `=` overrides any existing value. Also note that command-line *override* variables (that go *after* `make` on command line) override every single assignment in `Makefile`, except for [overrides](https://www.gnu.org/software/make/manual/html_node/Override-Directive.html). – Victor Sergienko Aug 17 '21 at 18:26
  • Does this sound correct? `=` and `:=` override environment variables, environment variables override `=?`, command-line "override variables" override both, and the `override` directive overrides all of the above. This interaction with environment variables and command-line override might be a very helpful clarification in your already-very-thorough answer. – shadowtalker Aug 17 '21 at 18:35
  • 1
    This is accurate. Thank you. I thought that this is a bit outside of the scope of the question. On the other hand, there is no question "How do Makefile variables get their values?". Maybe it's worth asking the question. – Victor Sergienko Aug 17 '21 at 18:54
  • I took your suggestion: https://stackoverflow.com/a/68825174/2954547 – shadowtalker Aug 17 '21 at 23:51