为什么 "make all" 在不将 "all" 添加到 .PHONY 目标的情况下按预期工作?

Why "make all" works as expected without adding "all" to .PHONY target?

我知道 .PHONY 的作用。

如果在我的 Makefile 所在的文件夹中,我添加了一个名为 clean 的空文件,并且在我 运行 make clean 之后所有的 clean 目标都不会被执行由于文件中没有任何更改,因此目标不会 运行,这是正确的。 如果我添加 .PHONY: clean,则 clean 被视为命令,这也是正确的。

我的问题是为什么这种行为不会发生在 all 目标上,因为我在 [=] 中添加了一个 all 文件33=] 基本上所有目标仍然像 .PHONY: all

一样执行

我有休闲的 makefile 代码。

all: test1 test2

test1: test1.o
test1.o: test1.c

test2: test2.o
test2.o: test2.c

clean:
        rm -rf *.o test1 test2

你怎么知道all规则是"still executing"?该规则没有配方,因此不可能 "executed".

如果您的意思是即使 all 文件存在于本地目录中,make 仍在构建目标 test1test2,就是这样make 有效(这与虚假目标与非虚假目标没有任何关系)。当 make 决定是否首先构建特定目标时,它会尝试构建该目标的所有先决条件,以及 those 目标的所有先决条件,等等。只有在所有这些都完成之后,才能make 知道是否构建第一个目标(在本例中为 all)。

make clean 这里没有任何依赖关系,所以把一个名为 clean 的文件放在那里足以让目标被认为是构建的。

make all 另一方面具有依赖性。即使你把一个名为 all 的文件放在那里,Make 也必须检查 all 文件是否比 test1test2 新。此过程会触发 test1test2 的构建,它恰好具有与 all 是虚假目标相同的效果。


基础是 all: test1 test2 是构建名为 allfile 的方法,它依赖于 files test1test2.

如果你 运行 make all,Make 会做这样的事情:

  1. 分析 Makefile.
  2. 发现all依赖于test1test2
  3. 查看all的时间戳,看是不是"up to date"。
    • 如果 none 个依赖项比自身更新,则为 "up to date"。
      • 换句话说,如果文件比所有依赖项都新,Make 可以跳过构建文件。
  4. 构建过时或丢失的文件。

现在,如果您想阻止 Make 将目标视为文件,您可以将它们指定为 phony targets。这是 all.

等非文件目标的最佳做法

(这个答案并不反对现有的任何一个答案,而是提出了另一种思考方式)。

当你有像

这样的规则时
dst: src
    action

你在说两件事(如你所知):

  1. 如果 dst 不存在,或者早于 src,则执行 action;和
  2. action 完成时,文件 dst 将存在。

对于allclean这样的目标,第二种说法当然不成立。 Make 不会让你遵守 (2) 中的承诺,所以当你说 make all 时,它会计算并生成所需的依赖项,并且不会抱怨之后没有文件 all。你在骗 Make,但它并不介意(这很酷......)。也就是说,这基本上是 makefile hack.

当然,如果由于某种原因恰好有一个名为 allclean 的文件,就会出错。然后Make在计算依赖关系时会考虑文件的修改日期all,可能会得出一个你没想到的结论

所以 .PHONY: all 所做的就是使黑客合法化,并告诉 Make ‘即使文件 all 存在,也假装它不存在”;你基本上是在取消承诺 (2)。

因此,正如其他答案所述,没有必要提及 .PHONY。当意外创建与虚假目标匹配的文件时,它只是防止错误 - 容易犯错但很容易错过。