Makefile:惰性变量中的shell函数不是惰性的

时间:2019-04-04 09:45:33

标签: makefile

考虑以下Makefile。

$(shell touch /tmp/example.txt)
FILE := /tmp/example.txt
CONTENTS = $(shell cat $(FILE); bash -c 'echo [debugging id: $$RANDOM]')

.PHONY: all
all:
    @cat $(FILE)
    @echo '$$(CONTENTS):' $(CONTENTS)
    bash -c 'echo file-contents-$$RANDOM' > $(FILE)
    @cat $(FILE)
    @echo '$$(CONTENTS):' $(CONTENTS) # This line outputs the old contents. Why?

它将打印文件的内容,用新内容覆盖并再次打印内容。显示为(make的第二张照片之后):

file-contents-1543
$(CONTENTS): file-contents-1543 [debugging id: 15172]
bash -c 'echo file-contents-$RANDOM' > /tmp/example.txt
file-contents-22441
$(CONTENTS): file-contents-1543 [debugging id: 151]

旧内容为file-contents-1543,新内容为file-contents-22441(数字是随机的),但是最后一行echo $(CONTENTS)不打印新内容。 我认为该命令实际上是调试ID所显示的两次,但是在将新内容写入文件之前,似乎执行了lazy变量中的shell函数。

我希望每次引用Makefile中的惰性变量时都会对其求值,echo $(CONTENTS)命令始终会打印最新的文件内容。我怎么了?

顺便说一句,我发现使用CONTENTS = $$(cat $(FILE))可以达到预期效果。我将用它代替shell函数,但是可以吗?

1 个答案:

答案 0 :(得分:1)

  

我希望每次引用Makefile中的惰性变量时都会对其求值,echo $(CONTENTS)命令总是打印最新的文件内容。我怎么了?

首先,在make的语中,这些变量称为递归,而不是惰性的。而且,是的,每次使用$(CONTENTS)进行引用时,它们都会扩展(即递归替换)。考虑到$(eval...)$(shell...)(几乎像$(...)一样)也经历了相同的(递归)扩展过程(尽管有一些“副作用”),每次扩展这样的变量也会导致某种“评估”或“执行”。

接下来,make中的扩展顺序有些特定。特别是,配方(即以[tab]开头的行)在整个emfile(预处理)后 后展开,但在配方的第一行之前由外壳执行。我想,这是您困惑的主要根源。

  

我发现使用CONTENTS = $$(cat $ {FILE))可以正常工作

$$是一种在扩展过程之后获取单个文字$的方法。因此,$$(cat $(FILE))展开后变成$(cat /tmp/example.txt),这是bash中command substitution的合法语法。这意味着它将仅作为bash命令(配方行)的一部分工作。如果那是您想要的,那没关系。