Answers:
默认情况下,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
是经常假目标是:all
,install
,clean
,distclean
,TAGS
,info
,check
。
假设您有install
目标,这在makefile中很常见。如果不使用.PHONY
,并且install
与Makefile所在的目录中存在一个名为的文件,make install
则将不执行任何操作。这是因为Make将规则解释为“执行某类配方以创建名为install
” 的文件。由于该文件已经存在,并且其依存关系没有更改,因此不会执行任何操作。
但是,如果将install
目标设为PHONY,它将告诉make工具该目标是虚构的,并且该make不应该期望它创建实际的文件。因此,它将不会检查install
文件是否存在,这意味着:a)如果文件确实存在,其行为将不会改变; b)stat()
不会调用extra 。
通常,Makefile中所有不会产生与目标名称相同名称的输出文件的目标都应为PHONY。这通常包括all
,install
,clean
,distclean
,等。
.sh
或.bash
扩展名,就像它们具有主要功能一样,并保留为包含的库添加扩展名(source mylib.sh
)。实际上,我遇到了这样的问题,因为我在与我的Makefile相同的目录中有一个脚本install
.PHONY
一直都在用...
.PHONY
版本。
注意: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.
make
整个命令有意义,这全都与文件有关!感谢您的回答。
.PHONY: install
最好的解释是GNU make手册本身:4.6 Phony Targets部分。
.PHONY
是make的特殊内置目标名称之一。您可能还对其他目标感兴趣,因此值得一看这些参考资料。
当需要考虑.PHONY目标时,make将无条件运行其配方,而不管该名称的文件是否存在或其最后修改时间是多少。
您可能还对make的标准目标(例如all
和)感兴趣clean
。
“ .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
不会每次都重建,而仅在将任何依赖目标作为文件过期时才会重建。
特殊目标.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
命令块将一个接一个地调用。
我经常使用它们来告诉默认目标不触发。
superclean: clean andsomethingelse
blah: superclean
clean:
@echo clean
%:
@echo catcher $@
.PHONY: superclean
如果没有假,make superclean
会火clean
,andsomethingelse
和catcher 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