makefile中.PHONY的目的是什么?

Laz*_*zer 1535 makefile phony-target

.PHONYMakefile 中的含义是什么?我已经完成了这个,但它太复杂了.

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

Eli*_*sky 1749

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

foo: bar
  create_one_from_the_other foo bar
Run Code Online (Sandbox Code Playgroud)

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

这些特殊目标称为假冒,您可以明确告诉他们与文件无关,例如:

.PHONY: clean
clean:
  rm -rf *.o
Run Code Online (Sandbox Code Playgroud)

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

就Make而言,虚假目标只是一个总是过时的目标,所以无论何时你问make <phony_target>,它都会运行,独立于文件系统的状态.一些常见的make是经常假目标是:all,install,clean,distclean,TAGS,info,check.

  • @Lazer:我不知道你是不是母语为英语的人.我不是.虚假这个词并不代表它听起来像什么.http://en.wiktionary.org/wiki/phony说:欺诈; 假; 有误导性的外表. (83认同)
  • 这个答案并不完全 - 虽然可以在链接教程中解决..PHONY强制构建Makefile中的标签/文件,如果它是拓扑的一部分 - 无论你的目标是什么.也就是说,如果你有一个设置为phony的'cleanup:'标签,并且你的安装标签是以清理作为先决条件定义的 - 即'install:cleanup',当makefile尝试构建时,将运行清理_always_ '安装'.这对于您总是想要采取的步骤非常有用,无论它们是否成功 - 它将忽略时间戳并强制它. (50认同)
  • @eSKay:'为什么叫'假'?' - 因为它不是真正的目标.也就是说,目标名称不是由该目标的命令生成的文件. (36认同)
  • 请注意,只要您没有与任务同名的文件,就不需要使用.PHONY.无论如何总是会执行任务,Makefile会更具可读性. (6认同)
  • 我想我现在对此有了一些想法(浏览过您提到的链接后)。顺便说一句,为什么它被称为“假”? (4认同)
  • “虚假目标只是永远过期的目标”-很棒的解释! (4认同)
  • .PHONY 目标适用于所有品牌还是仅适用于 GNU 品牌? (2认同)
  • @User10482你会写这样的东西“.PHONY:全部干净” (2认同)

Geo*_* Y. 703

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

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

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

  • @Kyle是的,我不确定我过去的自我意味着什么.这些天我一直使用`.PHONY` ...... (9认同)
  • @PineappleUndertheSea接受的答案已经从最初的无价值水平得到了显着改善,现在和这个一样好.我必须查看其修订历史以了解您的评论. (6认同)
  • @JasonTu这不一定是真的.Bash脚本约定要求你省略".sh"或`.bash`扩展名为"程序",它们运行就像它们有一个main函数一样,并保留为你包含的库添加扩展名(`source mylib.sh`).事实上,我得到了这个问题,因为我在Makefile的同一目录中有一个名为`install`的脚本 (6认同)
  • 这似乎有点无意义,因为我的代码库中永远不会有名为'install'的文件.大多数文件都有文件扩展名,没有文件扩展名的文件通常都是"自述文件".再说一遍,如果你有一个名为'install'而不是'install.sh'的bash脚本,那么你将会遇到糟糕的时间. (2认同)
  • @JasonTu这里的解决方案很简单:建立一个时间机器并“替换”您过去的自我。我建议您铲一把铲子,以免没人意识到您是`.PHONY`版本。 (2认同)

小智 102

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

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

prerit@vvdn105:~/test$ ls
hello  hello.c  makefile
Run Code Online (Sandbox Code Playgroud)

在makefile中,规则定义如下:

hello:hello.c
    cc hello.c -o hello
Run Code Online (Sandbox Code Playgroud)

现在假设文件'hello'是包含一些数据的文本文件,该文件是在'hello.c'文件之后创建的.因此'hello'的修改(或创建)时间戳将比'hello.c'更新.因此,当我们从命令行调用'make hello'时,它将打印为:

make: `hello' is up to date.
Run Code Online (Sandbox Code Playgroud)

现在访问'hello.c'文件并在其中放入一些空格,这不会影响代码语法或逻辑,然后保存并退出.现在hello.c的修改时间戳比'hello'的修改时间戳更新.现在,如果你调用'make hello',它将执行以下命令:

cc hello.c -o hello
Run Code Online (Sandbox Code Playgroud)

文件'hello'(文本文件)将被一个新的二进制文件'hello'覆盖(上面的编译命令的结果).

如果我们在makefile中使用.PHONY如下:

.PHONY:hello

hello:hello.c
    cc hello.c -o hello
Run Code Online (Sandbox Code Playgroud)

然后调用'make hello',如果pwd中存在任何名为'hello'的文件,它将忽略每次执行命令.

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

hello:
    cc hello.c -o hello
Run Code Online (Sandbox Code Playgroud)

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

make: `hello' is up to date.
Run Code Online (Sandbox Code Playgroud)

  • 这不仅使我运行的命令有意义,最终导致`make`作为一个整体有意义,它是关于文件的!谢谢你的回答. (3认同)
  • 这是一个简单的规则: ``` target: dependency ...command ... ``` 参考:https://www.gnu.org/software/make/ (2认同)

You*_*Bet 73

.PHONY: install
Run Code Online (Sandbox Code Playgroud)
  • 表示"install"一词不代表此Makefile中的文件名;
  • 表示Makefile与同一目录中名为"install"的文件无关.


Joh*_*McG 41

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


Jam*_*ald 29

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

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

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

您可能还对make的标准目标感兴趣,例如allclean.


Edo*_*iel 20

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

你可以把几个.PHONY:放在你的Makefile

.PHONY: all

all : prog1 prog2

...

.PHONY: clean distclean

clean :
    ...
distclean :
    ...
Run Code Online (Sandbox Code Playgroud)

还有另一种声明虚假目标的方法:简单地::不带先决条件:

all :: prog1 prog2

...

clean ::
    ...
distclean ::
    ...
Run Code Online (Sandbox Code Playgroud)

::有其他的含义,看到这里,但没有先决条件总是执行的食谱,即使目标已经存在,从而充当假目标。


Eth*_*ris 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
Run Code Online (Sandbox Code Playgroud)

你可以玩这个:

  • 首先做"准备"准备"源文件"
  • 通过触摸特定文件来查看它们的更新

你可以看到fileall通过假目标间接依赖于file1 - 但由于这种依赖性,它总是被重建.如果您更改依赖fileallfilefwdfile现在fileall没有得到重建,每一次,但只有当依赖目标的任何反对它陈旧的文件.


小智 10

因此,假设运行 make 的目录中有一个名为“clean”的文件。现在让我们以下面的 Makefile 为例:

clean:
        rm lol
Run Code Online (Sandbox Code Playgroud)

现在,当您运行“make clean”时,您将得到以下输出: 在此输入图像描述

但如果将“.PHONY: clean”添加到 Makefile 并运行“make clean”,您将看到以下输出: 在此输入图像描述

发生了什么?由于目录中存在文件,所以第一次将 clean 视为目标。但添加 .PHONY 后,make 会忽略该文件(以及时间戳跟踪)并将其解释为正常干净。

现在,这可以应用于许多情况,您希望 make 忽略作为目标给出的参数(当您在该目录中具有相同名称的文件时)。


jet*_*ero 5

我经常用它们来告诉默认目标不要开火。

\n\n
superclean: clean andsomethingelse\n\nblah: superclean\n\nclean:\n   @echo clean\n\n%:\n   @echo catcher $@\n\n.PHONY: superclean\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果没有 PHONY,make superclean则会触发cleanandsomethingelse、 和catcher superclean;但对于 PHONY,make superclean不会触发catcher superclean.

\n\n

我们不必担心告诉 makeclean target is PHONY, because it isn't completely phony. Though it never produces the clean file, it has commands to fire so make will think it's a final target.

\n\n

然而,superclean目标确实是假的,所以 make 会尝试将它与任何其他为目标 \xe2\x80\x94 提供依赖的东西堆叠起来,superclean这包括其他superclean目标和% target.

\n\n

请注意,我们根本没有提及andsomethingelseblah, so they clearly go to the catcher.

\n\n

输出看起来像这样:

\n\n
$ make clean\nclean\n\n$ make superclean\nclean\ncatcher andsomethingelse\n\n$ make blah \nclean\ncatcher andsomethingelse\ncatcher blah\n
Run Code Online (Sandbox Code Playgroud)\n