258

在我的 GNUmakefile 中,我想要一个使用临时目录的规则。例如:

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

如所写,上述规则在解析规则时创建临时目录。这意味着,即使我不是一直使用 out.tar,也会创建许多临时目录。我想避免我的 /tmp 被未使用的临时目录弄得乱七八糟。

有没有办法使变量仅在触发规则时才被定义,而不是在定义时才被定义?

我的主要想法是将 mktemp 和 tar 转储到 shell 脚本中,但这似乎有点难看。

4

4 回答 4

399

在您的示例中,每当评估规则时,TMP都会设置变量(并创建临时目录) 。为了仅在实际触发时创建目录,您需要将目录创建移至以下步骤:out.tarout.tar

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

eval函数评估一个字符串,就好像它是手动输入到 makefile 中一样。在这种情况下,它将TMP变量设置为shell函数调用的结果。

编辑(回应评论):

要创建唯一变量,您可以执行以下操作:

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

这会将目标的名称(在本例中为 out.tar)添加到变量中,从而生成名称为 的变量out.tar_TMP。希望这足以防止冲突。

于 2009-12-15T18:23:45.540 回答
91

一种相对简单的方法是将整个序列编写为 shell 脚本。

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

我在这里合并了一些相关提示:https ://stackoverflow.com/a/29085684/86967

于 2015-03-16T19:53:29.817 回答
39

另一种可能性是在触发规则时使用单独的行来设置 Make 变量。

例如,这是一个包含两个规则的生成文件。如果触发规则,它会创建一个临时目录并将 TMP 设置为临时目录名称。

PHONY = ruleA ruleB display

all: ruleA

ruleA: TMP = $(shell mktemp -d testruleA_XXXX)
ruleA: display

ruleB: TMP = $(shell mktemp -d testruleB_XXXX)
ruleB: display

display:
    echo ${TMP}

运行代码会产生预期的结果:

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow
于 2013-12-21T01:32:21.323 回答
6

我不喜欢“不要”的答案,但是……不要。

make的变量是全局的,应该在 makefile 的“解析”阶段进行评估,而不是在执行阶段。

在这种情况下,只要变量局部于单个目标,请按照@nobar 的答案并将其设为 shell 变量。

其他 make 实现也认为特定于目标的变量是有害的:katiMozilla pymake。正因为如此,一个目标可以不同地构建,具体取决于它是独立构建的,还是作为具有目标特定变量的父目标的依赖项。而且您不会知道它是哪种方式,因为您不知道已经构建了什么。

于 2019-10-11T00:52:23.243 回答