views:

143

answers:

1

In my GNUmakefile, I would like to have a rule that uses a temporary directory. For example:

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)

As written, the above rule creates the temporary directory at the time that the rule is parsed. This means that, even I don't make out.tar all the time, many temporary directories get created. I would like to avoid my /tmp being littered with unused temporary directories.

Is there a way to cause the variable to only be defined when the rule is fired, as opposed to whenever it is defined?

My main thought is to dump the mktemp and tar into a shell script but that seems somewhat unsightly.

+1  A: 

In your example, the TMP variable is set (and the temporary directory created) whenever the rules for out.tar are evaluated. In order to create the directory only when out.tar is actually fired, you need to move the directory creation down into the steps:

out.tar : 
    $(eval TMP := $(shell mktemp -d))
    @echo hi $(TMP)/hi.txt
    tar -C $(TMP) cf $@ .
    rm -rf $(TMP)

The eval function evaluates a string as if it had been typed into the makefile manually. In this case, it sets the TMP variable to the result of the shell function call.

edit (in response to comments):

To create a unique variable, you could do the following:

out.tar : 
    $(eval $@_TMP := $(shell mktemp -d))
    @echo hi $($@_TMP)/hi.txt
    tar -C $($@_TMP) cf $@ .
    rm -rf $($@_TMP)

This would prepend the name of the target (out.tar, in this case) to the variable, producing a variable with the name out.tar_TMP. Hopefully, that is enough to prevent conflicts.

e.James
Cool a few clarifications... this won't scope TMP to this target, will it? So if there are other rules that have their own $(TMP) usage (possibly in parallel with -j), there can be conflicts? Also, is the @echo even necessary? Seems you could just leave it out.
Emil
I didn't think you could do it without the `@echo`, but I tested it out, and it works. Good catch! I'll change it in my answer.
e.James
As far as scoping goes, the TMP variable would not be specific to that particular rule. It would exist in the global namesapce and could conflict with other variables that had the same name. Is that the desired behaviour? There are probably ways to work around it if need be.
e.James
It'd be helpful; this is part of a larger project where we definitely want to avoid clashing and use parallel builds. Perhaps you can declare a target specific variable which you then write to inside the rule... though that doesn't seem to work for me. Hm.
Emil
I've posted a possible solution in my answer. Hopefully that helps. Good luck!
e.James
Seems to do the trick (though a bit opaque to the average non-Make guru :-) Thanks!
Emil