Makefile中.PHONY的作用是什么?


1734

.PHONYMakefile是什么意思?我已经经历过,但是它太复杂了。

有人可以简单地向我解释吗?

Answers:


1944

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

foo: bar
  create_one_from_the_other foo bar

但是,有时您希望Makefile运行不代表文件系统中物理文件的命令。常见的目标“干净”和“全部”就是很好的例子。可能不是这种情况,但是您可能clean在主目录中有一个命名的文件。在这种情况下,Make会感到困惑,因为默认情况下clean目标将与此文件关联,而Make仅在文件的依赖关系似乎不是最新时才运行它。

这些特殊的目标称为phony,您可以明确地告诉Make与文件无关,例如:

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

make clean即使您有一个名为的文件,Now 也将按预期运行clean

就Make而言,伪造目标只是永远过期的目标,因此,只要您询问make <phony_target>,它就可以独立于文件系统的状态运行。一些常见的make是经常假目标是:allinstallcleandistcleanTAGSinfocheck


49
@eSKay:“为什么叫'phony'?” -因为这不是真正的目标。也就是说,目标名称不是由该目标的命令生成的文件。
伯纳德

96
@Lazer:我不知道你是否会说英语。我不是。phony一词并不代表听起来像什么。en.wiktionary.org/wiki/phony说:欺诈;假; 具有误导性的外观。
巴哈巴(Bahbar)2010年

57
尽管答案可能在链接的教程中得到了解决,但这个答案并不完全完整。.PHONY强制构建一个Makefile中的标签/文件,如果它是目标的拓扑排序的一部分。这就是说,如果您有一个设置为phony的“ cleanup:”标签,并且您的安装标签是使用cleanup作为前提条件定义的,即“ install:cleanup”,则在Makefile尝试构建时始终会运行cleanup '安装'。无论您是否成功执行此步骤,它都非常有用-它会忽略时间戳并强制执行。
synthesizerpatel

9
请注意,只要您没有与任务同名的文件,就不需要使用.PHONY。无论如何,该任务将始终执行,并且Makefile更具可读性。
伯纳德

15
“虚假目标只是永远过期的目标”-很棒的解释!
Danijel

729

假设您有install目标,这在makefile中很常见。如果使用.PHONY,并且install与Makefile所在的目录中存在一个名为的文件,make install则将不执行任何操作。这是因为Make将规则解释为“执行某类配方以创建名为install” 的文件。由于该文件已经存在,并且其依存关系没有更改,因此不会执行任何操作。

但是,如果将install目标设为PHONY,它将告诉make工具该目标是虚构的,并且该make不应该期望它创建实际的文件。因此,它将不会检查install文件是否存在,这意味着:a)如果文件确实存在,其行为将不会改变; b)stat()不会调用extra 。

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


6
@PineappleUndertheSea接受的答案已从其最初的毫无价值的水平得到了显着改善,现在已经和这个答案一样好。我必须查看其修订历史以了解您的评论。
Mark Amery 2014年

2
这似乎没有意义,因为我的代码库中永远不会有名为“ install”或类似名称的文件。大多数文件都将具有文件扩展名,而没有文件扩展名的文件通常都大写,例如“ README”。再说一次,如果您有一个名为'install'而不是'install.sh'的bash脚本,那将是一段糟糕的时光。
核苷潮2015年

6
@JasonTu这不一定是正确的。Bash脚本约定要求您省略“程序”的运行方式.sh.bash扩展名,就像它们具有主要功能一样,并保留为包含的库添加扩展名(source mylib.sh)。实际上,我遇到了这样的问题,因为我在与我的Makefile相同的目录中有一个脚本install
Kyle

10
@Kyle是的,我不确定我过去的自我含义。这些天,我.PHONY一直都在用...
核苷信号

2
@JasonTu这里的解决方案很简单:建立一个时间机器并“替换”您过去的自我。我建议您铲一把铲子,以使没人意识到您就是那个.PHONY版本。
Mateen Ulhaq

120

注意: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

并且pwd“测试”中已经存在“ hello”文件,那么“ make hello”将始终显示为:

make: `hello' is up to date.

3
这不仅使我运行的命令有意义,最终使make整个命令有意义,这全都与文件有关!感谢您的回答。
喀恰

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



11

“ .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从更改filefwdfile,则现在fileall不会每次都重建,而仅在将任何依赖目标作为文件过期时才会重建。


5

特殊目标.PHONY:允许声明虚假目标,因此不会make将其作为实际文件名进行检查:即使此类文件仍然存在,它也始终有效。

你可以把一些.PHONY:Makefile

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

声明虚假目标的另一种方法是:只需将'::'

all :: prog1 prog2

...

clean ::
    ...
distclean ::
    ...

该“::”具有特殊的含义:目标是假冒加上他们可以出现多次:

clean ::
    rm file1

...


clean ::
    rm file2

命令块将一个接一个地调用。


3

我经常使用它们来告诉默认目标不触发。

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

如果没有假,make superclean会火cleanandsomethingelsecatcher superclean; 但是使用PHONY make superclean不会触发catcher superclean

我们不必担心告诉clean目标是PHONY,因为它不是完全虚假的。尽管它从不产生干净文件,但它具有要触发的命令,因此make会认为这是最终目标。

但是,superclean目标实际上是伪造的,因此make会尝试将其与为superclean目标提供帮助的其他任何东西(包括其他superclean目标和%目标)堆叠在一起。

请注意,我们不说根本不谈andsomethingelse或者blah,让他们清楚地去接球手。

输出看起来像这样:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.