书写规则
————
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
在Makefile中,规则的顺序是很重要的,由于,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,因此必定要让make知道你的最终目标是什么。通常来讲,定义在Makefile中的目标可能会有不少,可是第一条规则中的目标将被确立为最终的目标。若是第一条规则中的目标有不少个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。
好了,仍是让咱们来看一看如何书写规则。
1、规则举例
foo.o : foo.c defs.h # foo模块
cc -c -g foo.c
看到这个例子,各位应该不是很陌生了,前面也已说过,foo.o是咱们的目标,foo.c和defs.h是目标所依赖的源文件,而只有一个命令“cc -c -g foo.c”(以Tab键开头)。这个规则告诉咱们两件事:
一、文件的依赖关系,foo.o依赖于foo.c和defs.h的文件,若是foo.c和defs.h的文件日期要比foo.o文件日期要新,或是foo.o不存在,那么依赖关系发生。
二、若是生成(或更新)foo.o文件。也就是那个cc命令,其说明了,如何生成foo.o这个文件。(固然foo.c文件include了defs.h文件)
2、规则的语法
targets : prerequisites
command
...
或是这样:
targets : prerequisites ; command
command
...
targets是文件名,以空格分开,可使用通配符。通常来讲,咱们的目标基本上是一个文件,但也有多是多个文件。
command是命令行,若是其不与“target:prerequisites”在一行,那么,必须以[Tab键]开头,若是和prerequisites在一行,那么能够用分号作为分隔。(见上)
prerequisites也就是目标所依赖的文件(或依赖目标)。若是其中的某个文件要比目标文件要新,那么,目标就被认为是“过期的”,被认为是须要重生成的。这个在前面已经讲过了。
若是命令太长,你可使用反斜框(‘\’)做为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和如何成成目标文件。
通常来讲,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。
3、在规则中使用通配符
若是咱们想定义一系列比较相似的文件,咱们很天然地就想起使用通配符。make支持三各通配符:“*”,“?”和“[...]”。这是和Unix的B-Shell是相同的。
波浪号(“~”)字符在文件名中也有比较特殊的用途。若是是“~/test”,这就表示当前用户的$HOME目录下的test目录。而“~hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据环境变量“HOME”而定。
通配符代替了你一系列的文件,如“*.c”表示因此后缀为c的文件。一个须要咱们注意的是,若是咱们的文件名中有通配符,如:“*”,那么能够用转义字符“\”,如“\*”来表示真实的“*”字符,而不是任意长度的字符串。
好吧,仍是先来看几个例子吧:
clean:
rm -f *.o
上面这个例子我不很少说了,这是操做系统Shell所支持的通配符。这是在命令中的通配符。
print: *.c
lpr -p $?
touch print
上面这个例子说明了通配符也能够在咱们的规则中,目标print依赖于全部的[.c]文件。其中的“$?”是一个自动化变量,我会在后面给你讲述。
objects = *.o
上面这个例子,表示了,通符一样能够用在变量中。并非说[*.o]会展开,不!objects的值就是“*.o”。Makefile中的变量其实就是C/C++中的宏。若是你要让通配符在变量中展开,也就是让objects的值是全部[.o]的文件名的集合,那么,你能够这样:
objects := $(wildcard *.o)
这种用法由关键字“wildcard”指出,关于Makefile的关键字,咱们将在后面讨论。
4、文件搜寻
在一些大的工程中,有大量的源文件,咱们一般的作法是把这许多的源文件分类,并存放在不一样的目录中。因此,当make须要去找寻文件的依赖关系时,你能够在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。
Makefile文件中的特殊变量“VPATH”就是完成这个功能的,若是没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。若是定义了这个变量,那么,make就会在当当前目录找不到的状况下,到所指定的目录中去找寻文件了。
VPATH = src:../headers
上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(固然,当前目录永远是最高优先搜索的地方)
另外一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很相似,可是它更为灵活。它能够指定不一样的文件在不一样的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:
一、vpath <pattern> <directories>
为符合模式<pattern>的文件指定搜索目录<directories>。
二、vpath <pattern>
清除符合模式<pattern>的文件的搜索目录。
三、vpath
清除全部已被设置好了的文件搜索目录。
vapth使用方法中的<pattern>须要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示全部以“.h”结尾的文件。<pattern>指定了要搜索的文件集,而<directories>则指定了<pattern>的文件集的搜索的目录。例如:
vpath %.h ../headers
该语句表示,要求make在“../headers”目录下搜索全部以“.h”结尾的文件。(若是某文件在当前目录没有找到的话)
咱们能够连续地使用vpath语句,以指定不一样搜索策略。若是连续的vpath语句中出现了相同的<pattern>,或是被重复了的<pattern>,那么,make会按照vpath语句的前后顺序来执行搜索。如:
vpath %.c foo
vpath % blish
vpath %.c bar
其表示“.c”结尾的文件,先在“foo”目录,而后是“blish”,最后是“bar”目录。
vpath %.c foo:bar
vpath % blish
而上面的语句则表示“.c”结尾的文件,先在“foo”目录,而后是“bar”目录,最后才是“blish”目录。
5、伪目标
最先先的一个例子中,咱们提到过一个“clean”的目标,这是一个“伪目标”,
clean:
rm *.o temp
正像咱们前面例子中的“clean”同样,即然咱们生成了许多文件编译文件,咱们也应该提供一个清除它们的“目标”以备完整地重编译而用。 (以“make clean”来使用该目标)
由于,咱们并不生成“clean”这个文件。“伪目标”并非一个文件,只是一个标签,因为“伪目标”不是文件,因此make没法生成它的依赖关系和决定它是否要执行。咱们只有经过显示地指明这个“目标”才能让其生效。固然,“伪目标”的取名不能和文件名重名,否则其就失去了“伪目标”的意义了。
固然,为了不和文件重名的这种状况,咱们可使用一个特殊的标记“.PHONY”来显示地指明一个目标是“伪目标”,向make说明,不论是否有这个文件,这个目标就是“伪目标”。
.PHONY : clean
只要有这个声明,不论是否有“clean”文件,要运行“clean”这个目标,只有“make clean”这样。因而整个过程能够这样写:
.PHONY: clean
clean:
rm *.o temp
伪目标通常没有依赖的文件。可是,咱们也能够为伪目标指定所依赖的文件。伪目标一样能够做为“默认目标”,只要将其放在第一个。一个示例就是,若是你的Makefile须要一口气生成若干个可执行文件,但你只想简单地敲一个make完事,而且,全部的目标文件都写在一个Makefile中,那么你可使用“伪目标”这个特性:
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
咱们知道,Makefile中的第一个目标会被做为其默认目标。咱们声明了一个“all”的伪目标,其依赖于其它三个目标。因为伪目标的特性是,老是被执行的,因此其依赖的那三个目标就老是不如“all”这个目标新。因此,其它三个目标的规则老是会被决议。也就达到了咱们一口气生成多个目标的目的。“.PHONY : all”声明了“all”这个目标为“伪目标”。
随便提一句,从上面的例子咱们能够看出,目标也能够成为依赖。因此,伪目标一样也可成为依赖。看下面的例子:
.PHONY: cleanall cleanobj cleandiff
cleanall : cleanobj cleandiff
rm program
cleanobj :
rm *.o
cleandiff :
rm *.diff
“make clean”将清除全部要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。咱们能够输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不一样种类文件的目的。