0

我有一个文件,tmp 包含:

{
    "VolumeId": "vol--22222222", 
}

和一个makefile,仅包含:

MYFILE =latest

x:  
    \cp tmp $(MYFILE)
    grep VolumeId $(MYFILE)
    @echo $(shell grep VolumeId $(MYFILE))

如果我运行 make x,我会得到:

\cp tmp latest
grep VolumeId latest
    "VolumeId": "vol--22222222", 
VolumeId: vol--22222222,

正如预期的那样。如果我修改文件 tmp,用 4 替换 2,我得到:

\cp tmp latest
grep VolumeId latest
    "VolumeId": "vol--44444444444", 
VolumeId: vol--22222222,

……嗯?greps 返回不同的结果!第二个包含复制之前文件中的数据。

我跑

rm latest ; make x

我得到:

\cp tmp latest
grep VolumeId latest
    "VolumeId": "vol--4444444444", 

这里发生了什么?

GNU Make 3.81。VMWare 5 上的 Ubuntu 12.04。


更新 1

这是一个更明确的例子

CMD0 =$(shell date +"%s.%N")
CMD1 =date +"%s.%N"
CMD2 =date +"%s.%N"
y:
    @sleep 2
    @date +"%s.%N"    # 2nd 
    @echo $(CMD0)     # 1st A
    @sleep 2
    @date +"%s.%N"    # 3rd
    @sleep 2
    @$(CMD1)          # 4th
    @sleep 2
    @echo $(shell $(CMD2) )   # 1st B

输出:

1381596581.761093768
1381596579.743610973
1381596583.769058027
1381596585.774766561
1381596579.751625601

它看起来像所有 $(shell ... ) 命令在 y 配方的任何行之前一起评估。

这有点令人惊讶(并且违反直觉 - 这种奇怪设计的基本原理是什么?)。如果 shell 命令有副作用,它会产生重要的后果。此外,我在制作手册中找不到任何描述此评估顺序的文档。


更新 2

上述内容特别奇怪的是,很难为评估顺序提出一个心智模型。是不是任何形式的规则, $(function ... ) 在配方的每一行之前执行?如果是这样,为什么在配方之前评估 $(CMD0),而不是 $(CMD1)。CMD0包含一个 $(f ... ) 确实如此-即使 CMD0 被声明为延迟评估变量(即用 = not := 声明),也确实如此吗?


更新 3

将其简化为基本组件:

notSafe:
    backup-everything                        # Format executed even if backup fails
    echo $(shell format-disk) | tee log.txt  # 

CMDX =$(shell format-disk)
alsoNotSafe:
    backup-everything           # Format executed even if backup fails
    echo $(CMDX) | tee log.txt  # Even though CMDX is a delayed evaluation variable


CMDZ =format-disk
safe:
    backup-everything        # Works as expected. 
    $(CMDZ) | tee log.txt    # Evaluation of CMDZ is delayed until line is executed.

疯狂的。这是谁设计的?

4

1 回答 1

2

你并没有告诉我们一切:当你第一次运行它时,你会回显一个空行。相反,您会收到 grep file not found 错误。

这就是决定性的提示:在运行规则的命令之前$,-表达式由 make 的任何变体评估。即,在以后的每次运行中,您都会看到前一次运行的文件内容。

顺便提一句。您的前导反斜杠是虚假的,并导致命令被传递给 Shell,而不是由 make 直接执行。使用makepp 的内置命令(以 为前缀&)和固定依赖项:

latest: tmp
    &cp $(input) $(output)
    &grep VolumeId $(output)
    @&echo $(&grep VolumeId $(output))

这并没有改变问题,但是通过使用适当的规则变量,可以清楚地看出什么是错误的。

于 2013-10-12T12:00:02.960 回答