为什么这个 makefile 无限循环,但只在一台机器上?
Why does this makefile loop infinitely, but only on one machine?
考虑以下 makefile:
-include target.d
build:
%.d: %.c
@echo ">>> %.d: %.c"
touch $@
target.d: dependency
# @echo ">>> target.d: dependency"
dependency:
@echo ">>> dependency"
.PHONY: build
如果你把它放在一个有两个(空)文件的目录中,target.d 和 target.c 和 运行 make
,然后在我使用它的一个虚拟机上产生以下输出:
$ make
>>> dependency
>>> %.d: %.c
touch target.d
>>> dependency
>>> %.d: %.c
touch target.d
make: Nothing to be done for `build'.
在另一个虚拟机上,它无限循环。两台虚拟机都是 运行ning Centos7,并且都使用相同版本的 GNU make (3.82)。
(注意:如果您取消注释 target.d
目标下方的注释行,那么两者都会产生完全相同的输出;至少这种行为对我来说是有意义的)
我知道通过将配方添加到 target.d: dependency
可以使其优先于通用配方,同时只需添加依赖项。但我不明白的是,为什么一个系统会导致无限循环发生,而另一个极其相似的系统却不会。
这种奇怪行为的原因是什么?
(编辑:我发现我可以简化 makefile 并且仍然看到相同的行为)
我认为这里的问题是有一个名为 target.d
的目标,它与包含文件 target.d
相同,这样当您通过触摸它来编辑 target.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 自动生成 dep 文件),您将在编译规则中生成这些文件,例如 %.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 是否是个好主意,这是一个有效的问题;正如上面的评论所述,这是我正在使用但无法控制的构建系统的一部分的提炼,所以这就是生活。
所以仍然存在一个问题,即为什么这在两个不同的系统上表现不同?
问题如下:一台虚拟机 运行 在远程服务器上,另一台虚拟机在本地 运行。特别是,我的主机 machine 是一个 mac,我在 mac 和 VM 之间共享一些文件系统。看来我在 Mac 上使用的文件系统只将时间戳保持为秒级精度,而虚拟机上的时间戳保持为亚毫秒级精度。因此,Mac 不会选择“更新的”文件。
通过添加各种长度的睡眠命令,我能够在与 mac 共享文件的虚拟机上看到这一点:
-include a
a: b
sleep 0.8
touch a
b:
@echo b
这将导致它 运行 有限但准随机的次数。或者,更简单地说,运行ning 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
两次让第二个 运行 看到第一个的更新时间戳,因此它不会触发第二次。
考虑以下 makefile:
-include target.d
build:
%.d: %.c
@echo ">>> %.d: %.c"
touch $@
target.d: dependency
# @echo ">>> target.d: dependency"
dependency:
@echo ">>> dependency"
.PHONY: build
如果你把它放在一个有两个(空)文件的目录中,target.d 和 target.c 和 运行 make
,然后在我使用它的一个虚拟机上产生以下输出:
$ make
>>> dependency
>>> %.d: %.c
touch target.d
>>> dependency
>>> %.d: %.c
touch target.d
make: Nothing to be done for `build'.
在另一个虚拟机上,它无限循环。两台虚拟机都是 运行ning Centos7,并且都使用相同版本的 GNU make (3.82)。
(注意:如果您取消注释 target.d
目标下方的注释行,那么两者都会产生完全相同的输出;至少这种行为对我来说是有意义的)
我知道通过将配方添加到 target.d: dependency
可以使其优先于通用配方,同时只需添加依赖项。但我不明白的是,为什么一个系统会导致无限循环发生,而另一个极其相似的系统却不会。
这种奇怪行为的原因是什么?
(编辑:我发现我可以简化 makefile 并且仍然看到相同的行为)
我认为这里的问题是有一个名为 target.d
的目标,它与包含文件 target.d
相同,这样当您通过触摸它来编辑 target.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 自动生成 dep 文件),您将在编译规则中生成这些文件,例如 %.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的
-include a
a: b
touch a
b:
@echo b
关于包含您创建的 makefile 是否是个好主意,这是一个有效的问题;正如上面的评论所述,这是我正在使用但无法控制的构建系统的一部分的提炼,所以这就是生活。
所以仍然存在一个问题,即为什么这在两个不同的系统上表现不同?
问题如下:一台虚拟机 运行 在远程服务器上,另一台虚拟机在本地 运行。特别是,我的主机 machine 是一个 mac,我在 mac 和 VM 之间共享一些文件系统。看来我在 Mac 上使用的文件系统只将时间戳保持为秒级精度,而虚拟机上的时间戳保持为亚毫秒级精度。因此,Mac 不会选择“更新的”文件。
通过添加各种长度的睡眠命令,我能够在与 mac 共享文件的虚拟机上看到这一点:
-include a
a: b
sleep 0.8
touch a
b:
@echo b
这将导致它 运行 有限但准随机的次数。或者,更简单地说,运行ning 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
两次让第二个 运行 看到第一个的更新时间戳,因此它不会触发第二次。