题 makefile中.PHONY的目的是什么?


是什么 .PHONY 在Makefile中意味着什么?我经历过 这个,但它太复杂了。

有人可以用简单的语言向我解释一下吗?


1211
2018-01-27 09:08


起源




答案:


默认情况下,Makefile目标是“文件目标” - 它们用于从其他文件构建文件。假设它的目标是一个文件,这使得编写Makefile相对容易:

foo: bar
  create_one_from_the_other foo bar

但是,有时您希望Makefile运行不代表文件系统中的物理文件的命令。这方面的好例子是“清洁”和“全部”的共同目标。机会不是这样,但是你 可能 可能有一个名为的文件 clean 在您的主目录中。在这种情况下,Make会被混淆,因为默认情况下 clean target将与此文件关联,而Make只会在文件看起来与其依赖关系不相关时才运行它。

这些特殊目标被称为  你可以明确地告诉他们他们没有与文件相关联,例如:

.PHONY: clean
clean:
  rm -rf *.o

现在 make clean 即使你有一个名为的文件,也会按预期运行 clean

就Make而言,虚假目标只是一个总是过时的目标,所以无论何时你问 make <phony_target>,它将独立于文件系统的状态运行。有些常见 make 通常虚假的目标是: allinstallcleandistcleanTAGSinfocheck


1348
2018-01-27 09:11



@eSKay:'为什么叫'假'?' - 因为它不是真正的目标。也就是说,目标名称不是由该目标的命令生成的文件。 - Bernard
@Lazer:我不知道你是不是母语为英语的人。我不是。虚假这个词并不代表它听起来像什么。 en.wiktionary.org/wiki/phony 说:欺诈;假;有误导性的外表。 - Bahbar
这个答案并不完全 - 尽管可以在链接教程中解决。 .PHONY强制构建Makefile中的标签/文件,如果它是拓扑的一部分 - 无论你的目标是什么。也就是说,如果你有一个设置为phony的'cleanup:'标签,并且你的安装标签是以清理为先决条件定义的 - 即'install:cleanup',清理将 总是 当Makefile尝试构建'install'时运行。这对于您总是想要采取的步骤非常有用,无论它们是否成功 - 它将忽略时间戳并强制它。 - synthesizerpatel
@synthesizerpatel所以,如果 a: b, b: c, c: ph 哪里 ph 是 .PHONY,你要求 a,和 b 是最新的,你的评论意味着 ph 将运行。事实上,没有一个 a, b, c 实际上,它可以是最新的。 - Evgeni Sergeev
请注意,只要您没有与任务同名的文件,就不需要使用.PHONY。无论如何总是会执行任务,Makefile会更具可读性。 - Alkaline


我们假设你有 install 目标,这在makefile中很常见。如果你这样做  使用 .PHONY,以及一个名为的文件 install 然后存在于与Makefile相同的目录中 make install 会做 没有。这是因为Make将规则解释为“执行这样的配方来创建名为的文件” install“。由于文件已经存在,并且其依赖关系没有改变,因此不会做任何事情。

但是,如果你做了 install 目标PHONY,它将告诉make工具目标是虚构的,并且make不应指望它创建实际文件。因此它不会检查是否 install 文件存在,意思是:a)如果文件存在则不会改变其行为,b)额外 stat() 不会被叫。

通常,Makefile中不生成与目标名称同名的输出文件的所有目标都应该是PHONY。这通常包括 allinstallcleandistclean, 等等。


636
2017-08-26 10:54



@PineappleUndertheSea接受的答案已经从最初的无价值水平大大提高,现在和这个一样好。我必须查看其修订历史以了解您的评论。 - Mark Amery
这似乎有点无意义,因为我的代码库中永远不会有名为'install'的文件。大多数文件都有文件扩展名,没有文件扩展名的文件通常都是大写的,比如'README'。再说一遍,如果你有一个名为'install'而不是'install.sh'的bash脚本,你将会遇到糟糕的时间。 - Jason Tu
@JasonTu这不一定是真的。 Bash脚本约定要求你省略 .sh 要么 .bash “程序”的扩展名,它们具有主要功能,并为您添加的库添加扩展名(source mylib.sh)。事实上,我得到了这个问题,因为我在一个名为Makefile的目录中有一个脚本 install - Kyle
@Kyle是的,我不确定我过去的自我意味着什么。这几天我用 .PHONY 每时每刻... - Jason Tu
@JasonTu这里的解决方案很简单:建立一个时间机器并“替换”你过去的自我。我建议随身携带一把铁锹,这样就没有人意识到你就是这样 .PHONY 版。 - Mateen Ulhaq


注意:make工具读取makefile并检查规则中“:”符号两侧的文件的修改时间戳。

在目录'test'中存在以下文件:

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中存在任何名为'hello'的文件,它将忽略每次执行命令。

现在假设如果makefile中没有target的依赖项:

hello:
    cc hello.c -o hello

'hello'文件已存在于pwd'test'中,然后'make hello'将始终显示为:

make: `hello' is up to date.

64
2017-09-30 14:51



这不仅使我运行的命令有意义,最终导致 make 作为一个整体有意义,它是关于文件!谢谢你的回答。 - Kzqai


.PHONY: install
  • 表示“安装”一词不代表此处的文件名 Makefile文件;
  • 表示Makefile与名为“install”的文件无关 在同一目录中。

63
2017-08-25 22:39





它是一个不是文件名的构建目标。


34
2018-01-27 16:48





最好的解释是GNU make手册本身: 4.6 Phony Targets部分

.PHONY 是制造商之一 特殊内置目标名称。您可能感兴趣的还有其他目标,因此值得浏览这些参考资料。

当需要考虑.PHONY目标时,make将运行其配方   无条件地,无论具有该名称的文件是否存在或    它的最后修改时间是什么。

您可能也对make有兴趣 标准目标 如 all 和 clean


21
2018-04-03 23:49





还有一个重要的棘手的“.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

你可以玩这个:

  • 首先做“准备”准备“源文件”
  • 通过触摸特定文件来查看它们的更新来解决这个问题

你可以看到fileall通过虚假的目标间接依赖于file1 - 但它 总是 由于这种依赖性而重建。如果您更改了依赖项 fileall 从 filefwd 至 file,现在 fileall 每次都不会重建,但只有当任何依赖目标作为文件陈旧时才会被重建。


8
2018-02-11 10:11