一、目标,依赖,命令
也许大家觉得这个不重要,但今天我有了新的认识,所以写了下来。这三个就是Makefile的全部,但今天我要重点说一下它的执行顺序。每个Makefile都有且只有一个终极目标,下设若干子目标,make的规则会检查目标与依赖的时间戳,依赖中的某一个比目标新,说明目标已经过时,需要更新。这里要着重说一下,make会将目标的依赖及依赖的依赖全部展开,然后才能决定是否需要更新终极目标。
a.out
/ | \
/ | \
_ / _ \_
/ | \
hello.o world.o main.o
_/ | \_
/ | \
hello.c world.c main.c
对于这个例子,相信大家都能写出Makefile。make在解析Makefile时,就会生成上面的依赖树,它会自底向上比较时间戳,若下层的新,则执行上层目标所对应的命令,然后,依次执行上上层的命令(因为,底层更新了,所以上面的都需要更新)。如果底层不比上层的新,则不执行上层目标所对应的命令(这就是make的聪明之处)。例如:我们更改了hello.c文件,make在解析Makefile时,首先比较的不是a.out与hello.o谁更新(如果make真的这么做,那么make就会直接退出,因为自你上次执行make时,a.out会比hello.o更新,而我们修改hello.c,并不会影响hello.o的时间戳), 而是依赖树的最底端,hello.c与hello.o的时间戳,因为hello.c的修改,目标hello.o的命令会被执行,进而触发a.out所对应的命令。目标werld.o与main.o所对应的命令当然不会被执行。make的输出看上去是这个样子的
gcc -c hello.c
gcc hello.o world.o main.o
在make 的info文档中有这样一段话:(注意粗体字)
3.9 How `make' Reads a Makefile ===============================
GNU `make' does its work in two distinct phases. During the first phase it reads all the makefiles, included makefiles, etc. and internalizes all the variables and their values, implicit and explicit rules, and constructs a dependency graph of all the targets and their prerequisites. During the second phase, `make' uses these internal structures to determine what targets will need to be rebuilt and to invoke the rules necessary to do so.
4 Writing Rules ***************
A "rule" appears in the makefile and says when and how to remake certain files, called the rule's "targets" (most often only one per rule). It lists the other files that are the "prerequisites" of the target, and "commands" to use to create or update the target.
The order of rules is not significant, except for determining the "default goal": the target for `make' to consider, if you do not otherwise specify one. The default goal is the target of the first rule in the first makefile. If the first rule has multiple targets, only the first target is taken as the default. There are two exceptions: a target starting with a period is not a default unless it contains one or more slashes, `/', as well; and, a target that defines a pattern rule has no effect on the default goal. (*Note Defining and Redefining Pattern Rules: Pattern Rules.)
二、伪目标
伪目标总被认为是过期的,总被认为需要被更新。伪目标又称标签,之所以这样叫,是为了与真实的文件进行区别。那伪目标是如何工作的,我们可以将它当作普通文件,但不管出现在目标中,还是依赖中,都以为着它是将要被更新的普通文件。
1、伪目标做目标,普通文件做依赖
这是最常见的一种,典型的all伪目标 .PHONY:all
all: prog1 prog2 #执行make后,会一次生成两个可执行程序
prog1: a.c ...
command
...
prog2: b.c ...
command
...
分析:在这个Makefile中。.PHONY是第一个目标,但它以点开头,不会作为终极目标(在上面的英文中有解释),所以终极目标是all,make的职责所在就是重建这个all目标。因为all是伪目标,make命令总认为它需要被更新,而更新它,需要首先更新prog1 和 prog2 ,这就达到了一次make而生成两个可执行文件。
2、伪目标做目标,伪目标又做依赖(虽然称为伪目标,但也可做依赖,目标与依赖是相互转化的,就像上面的hello.o,它是hello.c的目标,又是a.out的依赖)
.PHONY: all subdir apps
all: subdir apps
subdir: prog1 prog2
apps: prog3 prog4
prog1: a.c
command...
prog2: b.c
command...
prog3: c.c
command...
prog4: d.c
command...
分析:all是终极目标,它依赖于subdir 和 apps ,因这两个都是伪目标,即:他们需要被更新。subdir又依赖于prog1 和 prog2 这就回到了上面的那种情况。这种形式经常出现在大的工程中,是对上面一种情况的扩展,我们可以修改all 或 subdir 或 apps 中的依赖,来决定我们需要那些应用程序,裁剪掉那些应用程序。你应该听说过内核裁剪与移植吧,所谓裁剪,就是修改Makefile的目标,不生成我们不需要的东东;所谓移植,就是修改Makefile的编译参数,换成其他的编译工具。
3、普通文件做目标,伪目标做依赖
all: prog
prog: a.c prt
command...
.PHONY: prt
prt:
@echo "this is prt"
分析:每次执行make时,this is prt都会出现在屏幕上。尽管prog不需要被更新,但prt中的命令依然要被执行。make会这样来解析,prog依赖于prt,而prt有需要被更新(因为它是伪目标),这意味着this si prt会出现在屏幕上,prt更新完成后,prog目标中的command...就会被执行(我们假定a.c没有被更改)。事实上我们那个文件也没有更改,但是prog目标中的命令还是被执行了,info make中不建议这样做
A phony target should not be a prerequisite of a real target file; if it is, its commands are run every time `make' goes to update that file.
总结:在伪目标中始终贯穿着这样一句话:伪目标总被认为是需要被更新的。我也是依据这句话来分析上述伪目标的应用。伪目标可以与真实文件重名,典型的clean用法,因为make知道,我们只是想执行clean下的命令,而不是去重建一个叫做clean的文件,这也是我们有时把它解释称标签的原因。关于上述第二种情况,info make中有这样一个解释
When one phony target is a prerequisite of another, it serves as a subroutine of the other. For example, here `make cleanall' will delete the object files, the difference files, and the file `program':
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff rm program
cleanobj : rm *.o
cleandiff : rm *.diff
这三个是makefile的全部内容,现在来说一下它的执行顺序