为什么这个makefile循环无限,但只在一台机器上?



考虑以下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

唯一存在的文件是:makefiletarget.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两次可以让第二次运行看到第一次运行的更新时间戳,因此它不会触发第二次运行。

相关内容

最新更新