1
votes

I was just playing around with Makefiles, and I'm trying to understand why some variables get expanded twice, while some others get expanded only once. Suppose the following Makefile:

FILE := $(shell mktemp)
TEMP = $(shell mktemp)
CONTENTS = $(shell cat $(FILE))
test:
    echo $(TEMP)
    echo $(CONTENTS)
    echo test > $(FILE)
    echo $(CONTENTS)
    echo $(TEMP)

Running make test outputs the following:

echo /tmp/tmp.ZWFtOiCG9v
/tmp/tmp.ZWFtOiCG9v
echo

echo test > /tmp/tmp.FEubzF4Gwa
echo

echo /tmp/tmp.OIKSGVvY1l
/tmp/tmp.OIKSGVvY1l

How come TEMP gets expanded each time it's called, but CONTENTS is only expanded once?

I'm running GNU Make 3.82

1

1 Answers

0
votes

Variables for a recipe are all expanded when the recipe begins to execute:

#forcing `bash` to prevent `make` from using its builtin shell
$ strace -t -e execve -f make -d SHELL=/bin/bash test >output 2>&1
$ cat output # with irrelevant bits elided
17:20:48 execve("/usr/bin/make", ["make", "-d", "SHELL=/bin/bash", "test"], [/* 52 vars */]) = 0
GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Reading makefile 'Makefile'...
Updating makefiles....
 Considering target file 'Makefile'.
 <lots of lines omitted>
 No need to remake target 'Makefile'.
Updating goal targets....

# prepare the recipe
Considering target file 'test'.
 File 'test' does not exist.
 Finished prerequisites of target file 'test'.
Must remake target 'test'.

# expand $(DATE) the first time
strace: Process 32729 attached
[pid 32729] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "date"], [/* 52 vars */]) = 0
[pid 32729] 17:20:48 execve("/bin/date", ["date"], [/* 52 vars */]) = 0
[pid 32729] 17:20:48 +++ exited with 0 +++
17:20:48 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32729, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---

# expand $(FILE) the first time
strace: Process 32730 attached
[pid 32730] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "mktemp"], [/* 52 vars */]) = 0
[pid 32730] 17:20:48 execve("/bin/mktemp", ["mktemp"], [/* 52 vars */]) = 0
[pid 32730] 17:20:48 +++ exited with 0 +++
17:20:48 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32730, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---

# expand $(DATE) the second time
strace: Process 32731 attached
[pid 32731] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "date"], [/* 52 vars */]) = 0
[pid 32731] 17:20:48 execve("/bin/date", ["date"], [/* 52 vars */]) = 0
[pid 32731] 17:20:48 +++ exited with 0 +++
17:20:48 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32731, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---

# expand $(FILE) the second time
strace: Process 32732 attached
[pid 32732] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "mktemp"], [/* 52 vars */]) = 0
[pid 32732] 17:20:48 execve("/bin/mktemp", ["mktemp"], [/* 52 vars */]) = 0
[pid 32732] 17:20:48 +++ exited with 0 +++
17:20:48 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32732, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---

# begin executing the recipe
strace: Process 32733 attached
Putting child 0x5629c95cb0c0 (test) PID 32733 on the chain.
Live child 0x5629c95cb0c0 (test) PID 32733 
[pid 32733] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "echo Tue Jun 26 17:20:48 PDT 201"...], [/* 56 vars */]) = 0
Tue Jun 26 17:20:48 PDT 2018
[pid 32733] 17:20:48 +++ exited with 0 +++
17:20:48 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32733, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Reaping winning child 0x5629c95cb0c0 PID 32733 
strace: Process 32734 attached
Live child 0x5629c95cb0c0 (test) PID 32734 
[pid 32734] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "echo /tmp/tmp.nWxcC4KeVf"], [/* 56 vars */]) = 0
/tmp/tmp.nWxcC4KeVf
[pid 32734] 17:20:48 +++ exited with 0 +++
17:20:48 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32734, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Reaping winning child 0x5629c95cb0c0 PID 32734 

# significant time doesn't pass until here
sleep 3
strace: Process 32735 attached
Live child 0x5629c95cb0c0 (test) PID 32735 
[pid 32735] 17:20:48 execve("/bin/bash", ["/bin/bash", "-c", "sleep 3"], [/* 56 vars */]) = 0
[pid 32735] 17:20:48 execve("/bin/sleep", ["sleep", "3"], [/* 56 vars */]) = 0
[pid 32735] 17:20:51 +++ exited with 0 +++
17:20:51 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32735, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Reaping winning child 0x5629c95cb0c0 PID 32735 
strace: Process 32736 attached
Live child 0x5629c95cb0c0 (test) PID 32736 
[pid 32736] 17:20:51 execve("/bin/bash", ["/bin/bash", "-c", "echo Tue Jun 26 17:20:48 PDT 201"...], [/* 56 vars */]) = 0
Tue Jun 26 17:20:48 PDT 2018
[pid 32736] 17:20:51 +++ exited with 0 +++
17:20:51 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32736, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Reaping winning child 0x5629c95cb0c0 PID 32736 
strace: Process 32737 attached
Live child 0x5629c95cb0c0 (test) PID 32737 
[pid 32737] 17:20:51 execve("/bin/bash", ["/bin/bash", "-c", "echo /tmp/tmp.QgNem5ezr0"], [/* 56 vars */]) = 0
/tmp/tmp.QgNem5ezr0
[pid 32737] 17:20:51 +++ exited with 0 +++
17:20:51 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=32737, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Reaping winning child 0x5629c95cb0c0 PID 32737 
Removing child 0x5629c95cb0c0 PID 32737 from chain.
Successfully remade target file 'test'.
17:20:51 +++ exited with 0 +++

This makes sense, even without .ONESHELL, because variables can expand to multiple lines, which need to be executed separately.