4.5 伪目标
有这么一种目标,它不代表一个真正的文件名,执行make时可以指定这个目标来执行其所在规则定义的一系列配方,这种目标就叫伪目标.使用伪目标有两个原因:一个是避免跟同名文件出现冲突,另一个是提高执行make 时的效率.
如果你要书写这么一条规则:它的配方不会创建目标文件,并且反复指定make该目标时,它的配方都会被执行.下面就是一个例子:
clean:
rm*.o temp
由于
rm命令并不会创建一个名为
"clean"的文件
,当前目录下通常也不会存在这个同名文件
,所以每次你输入
"makeclean"时
,规则中的
rm命令总会被执行
.
在这个例子中,如果当前目录下已经存在一个名为"clean"的文件时,则目标"clean"所在的规则就不会被执行.因为没有依赖,目标"clean",也就是文件"clean",总被认为是最新的,所以它的配方将不会被执行.为了避免这个问题,你需要将目标"clean"声明为伪目标,方法是将它作为特殊目标".PHONY"的依赖,如下:
.PHONY: clean
clean:
rm*.o temp
目标
"clean"声明为伪目标之后
,无论在当前目录下是否存在
"clean"这个文件
,输入
"makeclean"后都会执行目标
"clean"对应的配方
.
伪目标在make程序执行递归的过程中同样有用.在这种情况中,Makefile通常包含了这么一个变量,该变量定义为所有需要make的子目录.处理这类场景的一种简单的方法是定义一条规则,在规则的配方中使用shell循环遍历每个子目录进行make,就像下面这样:
SUBDIRS= foo bar baz
subdirs:
fordir in $(SUBDIRS); do \
$(MAKE)-C $$dir;\
done
但这种方法存在几个问题
.第一
,子目录中执行
make时出现的任何错误都会被忽略掉
,就是说
,在对某一个目录执行
make失败以后
,会继续对其他的目录进行
make.尽管可以在配方中添加出错检测并退出的
shell命令
,不幸的是
,如果在执行
make 时使用了
"-k"选项
,此方法将失效
.第二
,更重要的一点是
,这种方法使你无法利用
make并行处理规则目标的功能
,因为只有一条规则
.
将子目录声明为伪目标(必须这样做,因为子目录显然总是存在的),就可以解决这些问题:
SUBDIRS= foo bar baz
.PHONY: subdirs $(SUBDIRS)
subidrs: $(SUBDIRS)
$(MAKE)-C $@
foo: baz
这里我们还声明了一条规则
"foo: baz",这条规则用来确保只有在子目录
"baz"make完毕后才会对子目录
"foo"执行
make.在
make并行处理规则目标时
,这种顺序关系声明特别重要
.
make程序不会对伪目标尝试搜索隐含规则,也不关心是否存在和伪目标同名的文件,这也就是为什么将目标声明为伪目标可以提高make执行效率的原因.
伪目标通常不会作为一个真正的目标文件的依赖,这是因为如果伪目标成为一个目标文件的依赖时,每次make程序尝试刷新该目标文件时,作为依赖的伪目标,其配方必然会被执行.只要伪目标不作为一个真正目标的依赖,那么该伪目标的配方只会在make程序显式指定执行该目标时被执行.
伪目标可以有自己的依赖.当一个目录下需要创建多个可执行程序时,一种最方便的方法就是将所有程序的重建规则在一个Makefile中进行描述.因为Makefile中第一个目标就是默认目标,约定的做法是使用一个称为"all"的伪目标来作为默认目标,它的依赖文件就是那些需要构建的独立程序.下边就是一个例子:
all: prog1 prog2 prog3
.PHONY: all
prog1: prog1.o utils.o
cc-o prog1 prog1.o utils.o
prog2: prog2.o
cc-o prog2 prog2.o
prog3: prog3.o sort.o utils.o
cc-o prog3 prog3.o sort.o utils.o
现在我们只需要简单的输入
"make"就可以构建所有的程序
,或者在
make的命令行参数中指定要构建的程序
(比如
"makeprog1 prog3").伪目标的特性是不会被继承的
:除非特定声明某个目标为伪目标
,否则伪目标的依赖不会也变成伪目标
.
当一个伪目标作为另外一个伪目标的依赖时,make程序会将该伪目标作为另外一个伪目标的子程序来处理.在下面这个例子中,输入"makeclean"就会删除所有".o"文件,".diff"文件,以及"program"文件:
.PHONY: cleanall cleanobj cleandiff
cleanall: cleanobj cleandiff
rm program
cleanobj:
rm *.o
cleanobj:
rm *.diff
(这里似乎缺少关于上面这个例子的讲解内容)