我有一个文件,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.
疯狂的。这是谁设计的?