一个phony target 并不是一个文件的名称。使用phony target的2个原因:
- 避免与同名文件冲突
- 提高性能
如果你写了一个没有创建实际目标文件的规则,一旦该目标文件被标记调用,那么它所对应的命令会被执行。
clean:
rm *.o temp
因为rm 命令不会创建名为clean的文件,如果本地也没有一个同名的clean文件,那么你每次调用make clean
时,都会调用rm命令。
在上述例子中,如果本地有一个clean文件被创建,那么clean规则不会正常执行。因为它没有必要条件,clean总是被认为是最新的,它的命令不会被调用。为了避免这个问题,你可以显式的声明该目标是一个phony,通过标记该目标是特殊目标.PHONY
的必要条件。
.PHONY : clean
clean:
rm *.o temp
一旦标记clean为phony,那么make clean总是会调用rm,不论本地是否有一个clean文件。
Phony target 在递归调用make中也是很有用的技术。这种情况下,makefile经常包含一个需要构建的子目录的列表。处理这个的简单方法是,定义一个规则,在该规则中循环调用子目录的make。例如
SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS);do \
$(MAKE) -C $$dir; \
done
然而这个会有一些问题。
- 如果子目录中的make调用发生了错误,则该规则会忽略,继续执行剩下的子目录。这个问题可以通过增加shell返回值来检测。
- 由于是一个规则,无法使用的make并行技术。
但是 .PHONY 可以解决这些问题
SUBDIRS =foo bar baz
.PHONY : subdirs $(SUBDIRS)
subdirs : $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@
foo: baz
在上例中,我们声明 直到baz构建完成后才能构建foo.在并行构建时,这种相对关系声明是很重要的。
在.PHONY目标中,隐式规则会忽略,这就是为什么可以提高性能的原因了。
一个PHONY 目标不应该是一个实际目标的必要条件。因为每次执行make时,.phony target都会被执行,则对应的实际目标也被构建(浪费时间)。只要.phony 不是实际目标的必要条件,则调用.phony target时必须在make中显式指定。