考虑以下makefile:
-include target.d
build:
%.d: %.c
@echo ">>> %.d: %.c"
touch $@
target.d: dependency
# @echo ">>> target.d: dependency"
dependency:
@echo ">>> dependency"
.PHONY: build
如果您将此文件放在具有两个(空)文件的目录中,则目标。d和target.c并运行make
,然后在我使用的一个VM上,它产生以下输出:
$ make
>>> dependency
>>> %.d: %.c
touch target.d
>>> dependency
>>> %.d: %.c
touch target.d
make: Nothing to be done for `build'.
在另一个VM上,它无限循环。两个虚拟机都运行Centos7,并且都使用相同版本的GNU make(3.82)。
(注意:如果取消target.d
目标下面的注释行,则两者产生完全相同的输出;这种行为对我来说是有意义的,至少)
我知道,通过将配方添加到target.d: dependency
使其优先于通用配方,同时简单地添加依赖项。但我不明白的是,为什么一个系统会导致无限循环,而另一个极其相似的系统却不会。
这种奇怪行为的原因是什么?
(编辑:我发现我可以简化makefile,仍然看到相同的行为)
我认为这里的问题是有一个名为target.d
的目标,它与包含文件target.d
相同,当您编辑目标时。D,通过触摸它它不知何故调用了一个"未定义的"的行为。在这种情况下,它似乎触发了对makefile的重新调用。我可以把你的问题归纳成这样:
-include target.d
target.d: dep
touch target.d
dep:
@echo dep
唯一存在的文件是:makefile
。target.d
是在第一次运行make
时创建的。
$ ls
makefile
$ make
dep
touch target.d
dep
touch target.d
dep
touch target.d
:
etc
:
$ ls
makefile target.d
我不认为在makefile中包含一个目标也是一个项目是正确的。通常使用.d
(例如,使用gcc自动生成深度文件),您将在%.o: %.c ...
这样的编译规则中生成这些文件,该规则创建。o和。d文件。然后你有你的include %.d
类型行。但是您不应该有一个直接生成.d文件的目标。我不认为这是有效的-如果我错了,请有人纠正我。
。生成我认为你想要的文件:
-include target.d
.PHONY: build
build: target.o
# Simulates a compile line that generates and object file (.o) and dependency file (.d)
target.o: target.c dependency
touch target.o
touch target.d
.PHONY: dependency
dependency:
@echo ">>> dependency"
输出:
$ ls
makefile target.c
$ make
>>> dependency
touch target.o
touch target.d
$ ls
makefile target.c target.d target.o
因此,正如@code_fodder的回答所建议的那样,整个makefile可以大大简化为
-include a
a: b
touch a
b:
@echo b
有一个有效的问题,即包含您创建的makefile是否是一个好主意;正如在上面的评论中所述,这是我正在使用但无法控制的构建系统的一部分的蒸馏,所以c'est la vie。
所以仍然存在一个问题,为什么这在两个不同的系统上表现不同?
问题是这样的:一个VM在远程服务器上运行,而另一个VM在本地运行。特别是,我的主机是一台mac,我在mac和VM之间共享一些文件系统。我在Mac上使用的文件系统似乎只将时间戳保持在秒精度,而vm上的时间戳保持在亚毫秒精度。因此,Mac无法接收"更新"的软件。文件。
通过添加不同长度的睡眠命令,我可以在与mac共享文件的VM上看到这个:
-include a
a: b
sleep 0.8
touch a
b:
@echo b
,这将导致它运行有限但准随机的次数。或者,更简单地说,运行make; make
,生成
$ make; make
b
touch a
b
touch a
make: `a' is up to date.
b
touch a
make: `a' is up to date.
。在短时间内连续运行make
两次可以让第二次运行看到第一次运行的更新时间戳,因此它不会触发第二次运行。