makefile中.PHONY的目的是什么?

时间:2010-01-27 09:08:30

标签: makefile phony-target

.PHONY在Makefile中的含义是什么?我经历了this,但它太复杂了。

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

9 个答案:

答案 0 :(得分:1589)

默认情况下,Makefile目标是“文件目标” - 它们用于从其他文件构建文件。假设它的目标是一个文件,这使得编写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的文件,现在clean也会按预期运行。

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

答案 1 :(得分:684)

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

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

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

答案 2 :(得分:91)

注意: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'test'中的任何文件,并且每次都执行命令。

现在假设'hello'目标没有声明依赖项:

hello:
    cc hello.c -o hello

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

make: `hello' is up to date.

答案 3 :(得分:70)

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

答案 4 :(得分:38)

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

答案 5 :(得分:27)

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

.PHONY是其中一个Special Built-in Target Names。您可能感兴趣的还有其他目标,因此值得浏览这些参考文献。

  

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

您可能也对allcleanStandard Targets感兴趣。

答案 6 :(得分:11)

还有一个重要的狡猾对待&#34; .PHONY&#34; - 当物理目标依赖于依赖于另一个物理目标的虚假目标时:

TARGET1 - &gt; PHONY_FORWARDER1 - &gt; PHONY_FORWARDER2 - &gt; 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

你可以玩这个:

  • 首先做好准备&#39;准备&#34;源文件&#34;
  • 通过触摸特定文件来查看更新
  • 来解决这个问题

您可以看到fileall通过假目标间接依赖于file1 - 但由于此依赖性,始终会重建。如果您将fileall中的依赖项从filefwd更改为file,则现在fileall不会每次都重建,但只有当任何相关目标对其作为文件。

答案 7 :(得分:1)

我经常用它们来告诉默认目标不射击。

superclean: clean andsomethingelse

blah: superclean

clean:
   @echo clean

%:
   @echo catcher $@

.PHONY: superclean

没有PHONY,make superclean将触发cleanandsomethingelsecatcher superclean;但是使用PHONY时,make superclean不会触发catcher superclean

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

但是,superclean目标确实是伪造的,因此make会尝试将其与为superclean目标提供情报的任何其他内容(包括其他superclean目标和%目标。

请注意,我们对andsomethingelseblah一无所知,因此它们显然是去捕手的。

输出看起来像这样:

$ make clean
clean

$ make superclean
clean
catcher andsomethingelse

$ make blah 
clean
catcher andsomethingelse
catcher blah

答案 8 :(得分:1)

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

您可以在.PHONY:中放入几个Makefile

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...

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

all :: prog1 prog2

...

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

'::'具有特殊含义:目标是假的 plus ,它们可以出现几次:

clean ::
    rm file1

...


clean ::
    rm file2

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