什么导致这个怪异的时间与make一起旅行?

时间:2013-10-12 03:38:28

标签: makefile

我有一个文件,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,替换2的机智4,我得到:

\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命令有副作用,它会产生重要的后果。另外,我在make手册中找不到描述此评估顺序的任何文档。


更新2

上述特别奇怪的是,很难为评估顺序提出心理模型。是否在表格的每一行之前执行了形式的任何规则$(function ...)?如果是这样,为什么在配方之前评估$(CMD0),而不是$(CMD1)。确实CMD0 包含 a $(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.

疯狂。谁设计了这个?

1 个答案:

答案 0 :(得分:2)

你没有告诉我们所有事情:当你第一次跑它时,你会回响一条空行。相反,你得到一个找不到grep文件的错误。

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

顺便说一下。你的前导反斜杠是假的,导致命令被传递给Shell,而不是由make直接执行。使用makepp's内置命令(以&为前缀)和固定的依赖关系:

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

这不会改变问题,但通过使用适当的规则变量,可以明显地解决问题。