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?

1 Answer 1

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.

6
  • ... 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. Commented May 7, 2022 at 0: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. Commented May 7, 2022 at 0: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. Commented May 7, 2022 at 0:45
  • How do I invoke shell operations to call mktemp rather than make functions like shell? Commented May 7, 2022 at 0:50
  • 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. Commented May 7, 2022 at 13:32

Not the answer you're looking for? Browse other questions tagged or ask your own question.