.PHONY在makefile中的目的是什么?
.PHONY
在Makefile中意味着什么? 我已经经历了这个 ,但是太复杂了。
有人可以用简单的语言来解释一下吗?
默认情况下,Makefile目标是“文件目标” – 它们用于从其他文件构build文件。 假设它的目标是一个文件,这使得编写Makefiles相对容易:
foo: bar create_one_from_the_other foo bar
但是,有时您希望Makefile运行不代表文件系统中物理文件的命令。 很好的例子是“干净”和“全部”的共同目标。 有可能情况并非如此,但是您的主目录中可能会有一个名为clean
的文件。 在这种情况下,Make将会被混淆,因为默认情况下, clean
目标将与这个文件相关联,并且Make只会在文件看起来与其依赖关系不是最新的时候才运行。
这些特殊的目标被称为虚假的 ,你可以明确地告诉让他们不与文件关联,例如:
.PHONY: clean clean: rm -rf *.o
现在make clean
将按预期运行,即使你有一个名为clean
的文件。
在Make方面,假目标只是一个总是过时的目标,所以无论何时你问make <phony_target>
,目标都会独立于文件系统的状态运行。 一些常见的伪造目标是: all
, install
, clean
, distclean
, TAGS
, info
, check
。
欲了解更多信息,这里有一个很好的教程解释它。
假设你已经install
目标,这在makefiles中是非常常见的。 如果您不使用.PHONY
,并且名为install
的文件与Makefile位于同一目录中,那么make install
将不会执行任何操作 。 这是因为Make将规则解释为“执行这样的配方来创build名为install
的文件”。 由于该文件已经存在,并且其依赖关系没有改变,所以不会做任何事情。
但是,如果您将install
目标PHONY,它会告诉make工具,目标是虚构的,这使得不应该期望它创build实际的文件。 因此,它不会检查install
文件是否存在,这意味着:a)如果文件存在,它的行为不会被改变; b)不会调用额外的stat()
。
一般来说,Makefile中所有不产生与目标名称同名的输出文件的目标应该是PHONY。 这通常包括all
, install
, clean
, distclean
等。
.PHONY: install
- 表示“安装”一词在这个Makefile中不代表文件名;
- 意味着Makefile与在同一个目录下的“install”文件无关。
注 :make工具读取makefile,并检查规则中':'符号两侧文件的修改时间戳。
例
在“testing”目录中存在以下文件:
prerit@vvdn105:~/test$ ls hello hello.c makefile
在makefile中,一个规则定义如下:
hello:hello.c cc hello.c -o hello
现在假定文件“hello”是一个包含一些数据的文本文件,它是在'hello.c'文件之后创build的。 所以'hello'的修改(或创build)时间戳会比'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中存在的任何名为'hello'的文件,并且每次执行该命令。
现在假设makefile中没有目标依赖关系:
hello: cc hello.c -o hello
和'hello'文件已经存在于pwd'test'中,那么'make hello'将总是显示为:
make: `hello' is up to date.
这是一个不是文件名的构build目标。
最好的解释是GNU make手册本身: 4.6 Phony Targets部分 。
.PHONY
是make的特殊内置目标名称之一 。 还有其他的目标,你可能会感兴趣,所以这是值得浏览这些参考。
当考虑一个.PHONY目标的时候,make会无条件地运行它的配方,不pipe这个名字的文件是否存在,或者它的最后修改时间是什么。
你也可能对make的标准目标感兴趣,比如all
和clean
。
也有一个重要的棘手的对待“凤凰” – 当一个物理目标取决于假目标依赖于另一个物理目标:
TARGET1 – > PHONY_FORWARDER1 – > PHONY_FORWARDER2 – > TARGET2
如果你更新了TARGET2,那么你应该认为TARGET1应该被认为是针对TARGET1的,所以TARGET1应该被重build。 这真的是这样 。
棘手的部分是,当TARGET2对TARGET1 没有陈旧时 – 在这种情况下,你应该期望TARGET1不应该被重build。
这令人惊讶地不起作用,因为: 假冒目标是无论如何运行(通常假冒目标) ,这意味着假目标被认为是更新 。 而且因为这个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
你可以玩这个:
- 首先做“准备”,准备“源文件”
- 通过触摸特定的文件来查看更新
你可以看到fileall依赖于文件1通过一个虚假的目标间接的 – 但它总是得到重build,由于这种依赖。 如果您将fileall
的依赖关系从filefwd
为file
,那么现在fileall
都不会重buildfileall
,但只有当依赖目标中的任何一个针对它作为文件陈旧时才会被重build。