Makefile 中的 .PHONY
是什么意思?我已经通过this,但它太复杂了。
有人可以简单地向我解释一下吗?
默认情况下,Makefile 目标是“文件目标”——它们用于从其他文件构建文件。 Make 假设它的目标是一个文件,这使得编写 Makefiles 相对容易:
foo: bar
create_one_from_the_other foo bar
但是,有时您希望 Makefile 运行不代表文件系统中的物理文件的命令。很好的例子是常见的目标“干净”和“全部”。情况并非如此,但您可能可能在您的主目录中有一个名为 clean
的文件。在这种情况下,Make 会感到困惑,因为默认情况下 clean
目标将与此文件相关联,并且 Make 只会在文件在其依赖项方面看起来不是最新的时才会运行它。
这些特殊目标被称为假的,您可以明确告诉 Make 它们与文件无关,例如:
.PHONY: clean
clean:
rm -rf *.o
现在,即使您确实有一个名为 clean
的文件,make clean
也会按预期运行。
就 Make 而言,虚假目标只是一个始终过期的目标,因此无论何时询问 make <phony_target>
,它都会运行,与文件系统的状态无关。一些常见的 make
目标通常是虚假的:all
、install
、clean
、distclean
、TAGS
、info
、check
。
假设您有 install
目标,这在 makefile 中很常见。如果您不使用 .PHONY
,并且名为 install
的文件与 Makefile 存在于同一目录中,那么 make install
将什么都不做。这是因为 Make 将规则解释为“执行某某配方以创建名为 install
的文件”。由于文件已经存在,并且它的依赖关系没有改变,所以什么都不会做。
但是,如果您将 install
目标设为 PHONY,它将告诉 make 工具该目标是虚构的,并且 make 不应期望它创建实际文件。因此它不会检查 install
文件是否存在,这意味着:a) 如果文件确实存在,它的行为将不会改变,并且 b) 不会调用额外的 stat()
。
通常,Makefile 中所有不生成与目标名称同名的输出文件的目标都应该是 PHONY。这通常包括 all
、install
、clean
、distclean
等。
.sh
或 .bash
扩展,这些“程序”就像它们具有主函数一样运行,并保留为您包含的库添加扩展 (source mylib.sh
)。事实上,我之所以遇到这个 SO 问题,是因为我在与 Makefile 相同的目录中有一个名为 install
的脚本
.PHONY
...
.PHONY
版本。
注意:make 工具读取 makefile 并检查规则中 ':' 符号两侧的文件的修改时间戳。
例子
在目录“测试”中存在以下文件:
prerit@vvdn105:~/test$ ls
hello hello.c makefile
在 makefile 中,规则定义如下:
hello:hello.c
cc hello.c -o hello
现在假设文件'hello'是一个包含一些数据的文本文件,它是在'hello.c'文件之后创建的。所以'hello'的修改(或创建)时间戳将比'hello.c'的更新。因此,当我们从命令行调用“make hello”时,它将打印为:
make: `hello' is up to date.
现在访问“hello.c”文件并在其中放置一些空格,这不会影响代码语法或逻辑,然后保存并退出。现在 hello.c 的修改时间戳比 'hello' 的更新。现在,如果您调用“make hello”,它将执行以下命令:
cc hello.c -o hello
并且文件“hello”(文本文件)将被新的二进制文件“hello”覆盖(上述编译命令的结果)。
如果我们在 makefile 中使用 .PHONY 如下:
.PHONY:hello
hello:hello.c
cc hello.c -o hello
然后调用'make hello',它将忽略pwd'test'中存在的任何文件并每次执行命令。
现在假设,'hello' 目标没有声明依赖项:
hello:
cc hello.c -o hello
并且 'hello' 文件已经存在于 pwd 'test' 中,那么 'make hello' 将始终显示为:
make: `hello' is up to date.
make
变得有意义,一切都与文件有关!谢谢你的回答。
.PHONY: install
表示“安装”一词不代表此 Makefile 中的文件名;
表示 Makefile 与同一目录中名为“install”的文件无关。
它是一个不是文件名的构建目标。
最好的解释是 GNU make 手册本身:4.6 Phony Targets section。
.PHONY
是 make 的 Special Built-in Target Names 之一。还有其他您可能感兴趣的目标,因此值得浏览这些参考资料。
当需要考虑 .PHONY 目标时,make 将无条件地运行其配方,无论是否存在具有该名称的文件或其最后修改时间是什么。
您可能还对 make 的 Standard Targets 感兴趣,例如 all
和 clean
。
特殊目标 .PHONY:
允许声明虚假目标,因此 make
不会将它们检查为实际文件名:即使此类文件仍然存在,它也会一直工作。
您可以在 Makefile
中放置多个 .PHONY:
:
.PHONY: all
all : prog1 prog2
...
.PHONY: clean distclean
clean :
...
distclean :
...
还有另一种声明虚假目标的方法:只需将 ::
放在没有先决条件的情况下:
all :: prog1 prog2
...
clean ::
...
distclean ::
...
::
有其他特殊含义,请参阅 here,但没有先决条件,它总是执行配方,即使目标已经存在,因此充当假目标。
::
时才暗示 phony,请参阅上面 gnu make 文档中的链接。
“.PHONY”还有一个重要的棘手处理 - 当一个物理目标依赖于依赖另一个物理目标的虚假目标时:
TARGET1 -> PHONY_FORWARDER1 -> PHONY_FORWARDER2 -> TARGET2
您只是希望,如果您更新了 TARGET2,那么 TARGET1 应该被认为相对于 TARGET1 是陈旧的,因此应该重建 TARGET1。它确实以这种方式工作。
棘手的部分是当 TARGET2 对 TARGET1 不陈旧时——在这种情况下,您应该期望 TARGET1 不应该被重建。
这令人惊讶地不起作用,因为:假目标无论如何都运行了(就像假目标通常那样),这意味着假目标被认为是更新的。并且正因为如此,TARGET1 被认为对虚假目标是陈旧的。
考虑:
all: fileall
fileall: file2 filefwd
echo file2 file1 >fileall
file2: file2.src
echo file2.src >file2
file1: file1.src
echo file1.src >file1
echo file1.src >>file1
.PHONY: filefwd
.PHONY: filefwd2
filefwd: filefwd2
filefwd2: file1
@echo "Produced target file1"
prepare:
echo "Some text 1" >> file1.src
echo "Some text 2" >> file2.src
你可以玩这个:
首先做'make prepare'来准备“源文件”
通过触摸特定文件以查看它们的更新来解决这个问题
您可以看到 fileall 通过虚假目标间接依赖于 file1 - 但由于这种依赖关系,它总是会被重建。如果您将 fileall
中的依赖关系从 filefwd
更改为 file
,现在 fileall
不会每次都重建,但只有当任何依赖目标作为文件过时时才会重建。
我经常使用它们来告诉默认目标不要开火。
superclean: clean andsomethingelse
blah: superclean
clean:
@echo clean
%:
@echo catcher $@
.PHONY: superclean
如果没有 PHONY,make superclean
将触发 clean
、andsomethingelse
和 catcher superclean
;但是对于 PHONY,make superclean
不会触发 catcher superclean
。
我们不必担心告诉 make clean
目标是 PHONY,因为它不是完全虚假的。虽然它从不生成干净的文件,但它有要触发的命令,所以 make 会认为它是最终目标。
但是,superclean
目标确实是假的,因此 make 会尝试将它与任何其他为 superclean
目标提供 deps 的东西叠加在一起——这包括其他 superclean
目标和 %
目标。
请注意,我们根本没有说任何关于 andsomethingelse
或 blah
的内容,因此它们显然是针对捕手的。
输出看起来像这样:
$ make clean
clean
$ make superclean
clean
catcher andsomethingelse
$ make blah
clean
catcher andsomethingelse
catcher blah