0

This is a Makefile piece of code of how someone may use mktemp in a Makefile

TEST=$(shell mktemp -d)
mktemp:
    echo $(TEST)
    touch $(TEST)/test.txt
    ls $(TEST)
    cat $(TEST)/test.txt
    rm -rf $(TEST)

This is an example output

❯ make mktemp 
echo /var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.tQI5EeyW
/var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.tQI5EeyW
touch /var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.lVL3N8Rp/test.txt
ls /var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.sBW9FzgD
cat /var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.Ti53SWSw/test.txt
cat: /var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.Ti53SWSw/test.txt: No such file or directory
make: *** [mktemp] Error 1

The expectation is that cat /var/folders/62/wkysd_4n0w57ljl9ycfsd9cc0000gn/T/tmp.Ti53SWSw/test.txt would not error.

How can mktemp be used in this case?

Nimantha
  • 6,405
  • 6
  • 28
  • 69

1 Answers1

3

This line:

TEST=$(shell mktemp -d)

sets the value of the TEST variable to the string $(shell mktemp -d). It doesn't expand that string (doesn't run the shell command), it just keeps the string as-is.

Now, every time you use that value later in the makefile, it's expanded which means mktemp is run again, and you get a different value:

mktemp:
        echo $(TEST)
        touch $(TEST)/test.txt
        ls $(TEST)
        cat $(TEST)/test.txt
        rm -rf $(TEST)

You want to use immediate expansion when you assign the variable, so that it is expanded only one time when the makefile is parsed; use:

TEST := $(shell mktemp -d)

Alternatively you can just write the recipe using shell operations and not use any make functions like shell:

mktemp:
        TEST=$$(mktemp -d) && \
        echo $$TEST && \
        touch $$TEST/test.txt && \
        ls $$TEST && \
        cat $$TEST/test.txt && \
        rm -rf $$TEST

Note by default each logical line of a recipe is run in a separate shell, so in order to have all the lines run in the same shell (so they have access to the same $TEST variable) you need to use backslash to combine them into a single logical line.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • ... though the OP should be aware that the temp directory will then be created, unconditionally, when the makefile is parsed, not when the recipe for target `mktemp` is executed. That may or may not be what they want. – John Bollinger May 07 '22 at 00:12
  • The unconditionallity can be easily resolved by using target specific variable which I have edited into my question. since I can't paste formatted text in comments. This answer is awesome. Thanks everyone. – Tiger Kaovilai May 07 '22 at 00:40
  • Well, target-specific variables can have other side-effects: they are inherited by prerequisites for example. Maybe you don't care about that in this case. If what you really want is a temporary directory that exists only for the lifetime of this recipe it's probably better to use shell operations to do it rather than make functions like `shell`. When asking questions it's best to be very specific about exactly what you want. – MadScientist May 07 '22 at 00:45
  • How do I invoke shell operations to call mktemp rather than make functions like shell? – Tiger Kaovilai May 07 '22 at 00:50
  • I think you mean making calls outside make target. – Tiger Kaovilai May 07 '22 at 01:27
  • 1
    A recipe in a rule is a shell script, so any shell operations can be run directly in the recipe. I will update the answer. – MadScientist May 07 '22 at 13:32