在规则执行时定义make变量

Emi*_*Sit 186 makefile gnu-make

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

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

如上所述,上述规则在解析规则时创建临时目录.这意味着,即使我没有直接创建.tar,也会创建许多临时目录.我想避免我的/ tmp被未使用的临时目录弄乱.

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

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

e.J*_*mes 293

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

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

EVAL,如果它已经手动键入到makefile文件函数计算的字符串.在这种情况下,它将TMP变量设置为shell函数调用的结果.

编辑(回应评论):

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

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

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

  • 小心这个解决方案!`$(eval $ @ _ TMP:= $(shell mktemp -d))`将按照规则程序的顺序首次评估Makefile*而不是*.换句话说,`$(eval ...)`发生的时间比你想象的要快.虽然这可能适用于此示例,但此方法将导致某些顺序操作出现问题. (29认同)
  • 似乎做的伎俩(虽然有点不透明的普通非制作大师:-)谢谢! (3认同)
  • 冷静一些澄清......这不会将TMP的范围扩大到这个目标,是吗?因此,如果有其他规则具有自己的$(TMP)用法(可能与-j并行),可能会有冲突?另外,@ echo甚至是必要的吗?似乎你可以把它留下来. (2认同)
  • @JamesThomasMoon1979 你知道如何克服这些限制,例如评估一些应该是规则中较早执行的步骤的结果的变量吗? (2认同)
  • 回答我自己的问题:我的解决方法是在执行第一条规则期间创建文件,并尝试在第二条规则的第一步中评估它们。 (2认同)
  • @JamesThomasMoon1979,如果您在此 Makefile 中运行 `make`,则在首次评估 Makefile 时它似乎不会执行。换句话说,该文件夹不是使用“make”创建的,而是按照预期使用“make TEST”创建的。https://gist.github.com/jsign/c7313f359daa99c02bafb222becacad3 (2认同)

nob*_*bar 57

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

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

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

  • 这绝对是最简单的,也是最好的答案(避免`@`和`eval`并做同样的工作).请注意,在您的输出中,您会看到`$ TMP`(例如``tar -C $ TMP ...`),尽管该值已正确传递给命令. (3认同)

小智 30

另一种可能性是在规则触发时使用单独的行来设置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}
Run Code Online (Sandbox Code Playgroud)

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

$ ls
Makefile
$ make ruleB
echo testruleB_Y4Ow
testruleB_Y4Ow
$ ls
Makefile  testruleB_Y4Ow
Run Code Online (Sandbox Code Playgroud)

  • 小心这个解决方案!首次评估Makefile时,将发生`ruleA:TMP = $(shell mktemp -d testruleA_XXXX)`和`ruleB:TMP = $(shell mktemp -d testruleB_XXXX)`.换句话说,`ruleA:TMP = $(shell ...`发生的时间比你想象的要快.虽然这可能适用于这种特殊情况,但这种方法会导致某些顺序操作出现问题. (10认同)

Vic*_*nko 5

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

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

在这种情况下,只要变量是单个目标的局部变量,就按照@nobar 的回答并使其成为 shell 变量。

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