Makefile 在规则执行时定义make变量

Makefile 在规则执行时定义make变量,makefile,gnu-make,Makefile,Gnu Make,在我的GNUmakefile中,我希望有一个使用临时目录的规则。例如: out.tar: TMP := $(shell mktemp -d) echo hi $(TMP)/hi.txt tar -C $(TMP) cf $@ . rm -rf $(TMP) 如上所述,上述规则在解析规则时创建临时目录。这意味着,即使我没有一直使用make out.tar,也会创建许多临时目录。我希望避免我的/tmp被未使用的临时目录弄乱 是否有办法使变量仅在触发规

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

out.tar: TMP := $(shell mktemp -d)
        echo hi $(TMP)/hi.txt
        tar -C $(TMP) cf $@ .
        rm -rf $(TMP)
如上所述,上述规则在解析规则时创建临时目录。这意味着,即使我没有一直使用make out.tar,也会创建许多临时目录。我希望避免我的/tmp被未使用的临时目录弄乱

是否有办法使变量仅在触发规则时定义,而不是在定义规则时定义


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

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

out.tar:
$(评估TMP:=$(壳牌MKTEM-d))
@echo hi$(TMP)/hi.txt
tar-C$(TMP)cf$@。
rm-rf$(TMP)
该函数对字符串进行求值,就好像它是手动键入到makefile中一样。在这种情况下,它将
TMP
变量设置为
shell
函数调用的结果

编辑(回应评论):

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

out.tar:
$(eval$@\u TMP:=$(shell MKTEM-d))
@echo hi$($@_TMP)/hi.txt
tar-C$($@\u TMP)cf$@。
rm-rf$($@\u TMP)

这将在变量前面加上目标的名称(在本例中为out.tar),生成一个名为
out.tar\u TMP
的变量。希望这足以防止冲突。

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

例如,这里是一个具有两个规则的makefile。如果触发规则,它将创建一个临时目录并将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

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

out.tar:
   set -e ;\
   TMP=$$(mktemp -d) ;\
   echo hi $$TMP/hi.txt ;\
   tar -C $$TMP cf $@ . ;\
   rm -rf $$TMP ;\
我在这里总结了一些相关提示:

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

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

在这种情况下,只要该变量是局部变量,就要遵循并使其成为shell变量


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

冷静一些澄清。。。这不会将TMP范围扩大到这个目标,是吗?因此,如果有其他规则具有自己的$(TMP)用法(可能与-j并行),那么可能存在冲突?还有,@echo有必要吗?似乎你可以省去它。似乎做到了(虽然对一般非制造大师来说有点不透明:-)谢谢!小心这个解决方案<代码>$(eval$@\u TMP:=$(shell mktemp-d))将在第一次对Makefile求值时发生,而不是按照规则过程的顺序求值。换句话说,
$(eval…
发生得比你想象的要快。对于本例来说,这可能没问题,但这种方法会给某些顺序操作带来问题。@JamesThomasMoon1979您知道如何克服这些限制吗,例如,评估一些变量,这些变量应该是规则前面执行的步骤的结果?回答我自己的问题:我的解决方法是在第一个规则执行期间创建文件,并尝试在第二个规则的第一个步骤中评估它们。作为提醒,GNUMake有一个语法,它可能是补充这种方法所必需的<代码>规则a:TMP=$(shell mktemp-d testruleA_XXXX)和
规则b:TMP=$(shell mktemp-d testruleB_XXXX)
将在首次评估生成文件时发生。换句话说,
ruleA:TMP=$(shell…
发生的时间比您想象的要早。虽然这种方法可能适用于这种特殊情况,但这种方法会导致某些顺序操作出现问题。这不是我的经验-规则局部变量仅在请求特定规则时才展开(显式或作为依赖项)如果您执行
规则a:private TMP=…
,这应该仍然有效。请参阅。这绝对是最简单的,因此也是最好的答案(避免
@
eval
并执行相同的工作)。请注意,在您的输出中可以看到
$TMP
(例如
tar-C$TMP…
)虽然值被正确地传递给命令。这是通常的做法;我希望人们看到这个答案,因为在实践中,没有人经过接受的命令的所有努力。如果您使用的是特定于shell的命令呢?在这种情况下,shell命令应该与调用make的命令使用相同的shell语言,对吗?我见过一些带有shebang的makefile。@p选项:您可能还希望在makefile中启用BASH特定的功能。局部变量将非常有用,即使它们是真正的局部变量,也就是说,它们在我们执行的规则下是不可见的。@Attilandvai在写答案时,我不知道。这肯定会使它们更安全,b但我仍然不鼓励使用它们。