天天看点

GNU make手册翻译系列(4)

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
           

(这里似乎缺少关于上面这个例子的讲解内容)    

继续阅读