为什么 "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
仍在构建目标 test1
和 test2
,就是这样make 有效(这与虚假目标与非虚假目标没有任何关系)。当 make 决定是否首先构建特定目标时,它会尝试构建该目标的所有先决条件,以及 those 目标的所有先决条件,等等。只有在所有这些都完成之后,才能make
知道是否构建第一个目标(在本例中为 all
)。
make clean
这里没有任何依赖关系,所以把一个名为 clean
的文件放在那里足以让目标被认为是构建的。
make all
另一方面具有依赖性。即使你把一个名为 all
的文件放在那里,Make 也必须检查 all
文件是否比 test1
和 test2
新。此过程会触发 test1
和 test2
的构建,它恰好具有与 all
是虚假目标相同的效果。
基础是 all: test1 test2
是构建名为 all
的 file 的方法,它依赖于 files test1
和 test2
.
如果你 运行 make all
,Make 会做这样的事情:
- 分析
Makefile
.
- 发现
all
依赖于test1
和test2
。
- 查看
all
的时间戳,看是不是"up to date"。
- 如果 none 个依赖项比自身更新,则为 "up to date"。
- 换句话说,如果文件比所有依赖项都新,Make 可以跳过构建文件。
- 构建过时或丢失的文件。
现在,如果您想阻止 Make 将目标视为文件,您可以将它们指定为 phony targets。这是 all
.
等非文件目标的最佳做法
(这个答案并不反对现有的任何一个答案,而是提出了另一种思考方式)。
当你有像
这样的规则时
dst: src
action
你在说两件事(如你所知):
- 如果
dst
不存在,或者早于 src
,则执行 action
;和
- 当
action
完成时,文件 dst
将存在。
对于all
或clean
这样的目标,第二种说法当然不成立。 Make 不会让你遵守 (2) 中的承诺,所以当你说 make all
时,它会计算并生成所需的依赖项,并且不会抱怨之后没有文件 all
。你在骗 Make,但它并不介意(这很酷......)。也就是说,这基本上是 makefile hack.
当然,如果由于某种原因恰好有一个名为 all
或 clean
的文件,就会出错。然后Make在计算依赖关系时会考虑文件的修改日期all
,可能会得出一个你没想到的结论
所以 .PHONY: all
所做的就是使黑客合法化,并告诉 Make ‘即使文件 all
存在,也假装它不存在”;你基本上是在取消承诺 (2)。
因此,正如其他答案所述,没有必要提及 .PHONY
。当意外创建与虚假目标匹配的文件时,它只是防止错误 - 容易犯错但很容易错过。
我知道 .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
仍在构建目标 test1
和 test2
,就是这样make 有效(这与虚假目标与非虚假目标没有任何关系)。当 make 决定是否首先构建特定目标时,它会尝试构建该目标的所有先决条件,以及 those 目标的所有先决条件,等等。只有在所有这些都完成之后,才能make
知道是否构建第一个目标(在本例中为 all
)。
make clean
这里没有任何依赖关系,所以把一个名为 clean
的文件放在那里足以让目标被认为是构建的。
make all
另一方面具有依赖性。即使你把一个名为 all
的文件放在那里,Make 也必须检查 all
文件是否比 test1
和 test2
新。此过程会触发 test1
和 test2
的构建,它恰好具有与 all
是虚假目标相同的效果。
基础是 all: test1 test2
是构建名为 all
的 file 的方法,它依赖于 files test1
和 test2
.
如果你 运行 make all
,Make 会做这样的事情:
- 分析
Makefile
. - 发现
all
依赖于test1
和test2
。 - 查看
all
的时间戳,看是不是"up to date"。- 如果 none 个依赖项比自身更新,则为 "up to date"。
- 换句话说,如果文件比所有依赖项都新,Make 可以跳过构建文件。
- 如果 none 个依赖项比自身更新,则为 "up to date"。
- 构建过时或丢失的文件。
现在,如果您想阻止 Make 将目标视为文件,您可以将它们指定为 phony targets。这是 all
.
(这个答案并不反对现有的任何一个答案,而是提出了另一种思考方式)。
当你有像
这样的规则时dst: src
action
你在说两件事(如你所知):
- 如果
dst
不存在,或者早于src
,则执行action
;和 - 当
action
完成时,文件dst
将存在。
对于all
或clean
这样的目标,第二种说法当然不成立。 Make 不会让你遵守 (2) 中的承诺,所以当你说 make all
时,它会计算并生成所需的依赖项,并且不会抱怨之后没有文件 all
。你在骗 Make,但它并不介意(这很酷......)。也就是说,这基本上是 makefile hack.
当然,如果由于某种原因恰好有一个名为 all
或 clean
的文件,就会出错。然后Make在计算依赖关系时会考虑文件的修改日期all
,可能会得出一个你没想到的结论
所以 .PHONY: all
所做的就是使黑客合法化,并告诉 Make ‘即使文件 all
存在,也假装它不存在”;你基本上是在取消承诺 (2)。
因此,正如其他答案所述,没有必要提及 .PHONY
。当意外创建与虚假目标匹配的文件时,它只是防止错误 - 容易犯错但很容易错过。