Makefile中把那些没有任何依赖只有执行动作的目标称为“伪目标”(phony targets)
clean :
rm edit $(objects)
在实际应用时,我们会把这个规则写成如下稍微复杂一些的样子。以防止出现始料未及的情况。
.PHONY : clean
clean :
-rm edit $(objects)
这两个实现有两点不同:
1. 通过“.PHONY”特殊目标将“clean”目标声明为伪目标。防止当磁盘上存在一个名为“clean”文件时,“clean”所在规则的命令无法执行。
2. 在命令行之前使用“-”,意思是忽略命令“rm”的执行错误。
上例中因为目标“clean”没有出现在终极目标“edit”依赖关系中,所以我们执行“make”时,目标“clean”所在的规则将不会被处理。
也可以通过多个“-f”或者“--file”选项来指定多个需要读取的makefile文件,多个makefile文件将会被按照指定的顺序进行连接并被make解析执行。当通过“-f”或者“--file”指定make读取makefile的文件时,make就不再自动查找这三个标准命名的makefile文件。
注释:通过命令指定目标使用make的隐含规则:
当前目录下不存在以“GNUmakefile”、“makefile”、“Makefile”命名的任何文件,
1. 当前目录下存在一个源文件foo.c的,我们可以使用“make foo.o”来使用make的隐含规则自动生成foo.o。当执行“make foo.o”时。我们可以看到其执行的命令为:
cc –c –o foo.o foo.c
之后,foo.o将会被创建或者更新。
2. 如果当前目录下没有foo.c文件时,就是make对.o文件目标的隐含规则中依赖文件不存在。如果使用命令“make foo.o”时,将回到到如下提示:
make: *** No rule to make target ‘foo.o’. Stop.
3. 如果直接使用命令“make”时,得到的提示信息如下:
make: *** No targets specified and no makefile found. Stop.
关于include
FILENAMES是shell所支持的文件名(可以使用通配符)。
示符“include”和文件名之间、多个文件之间使用空格或者[Tab]键隔开。行尾的空白字符在处理时被忽略。
使用指示符包含进来的Makefile中,如果存在变量或者函数的引用。它们将会在包含它们的Makefile中被展开。
include foo *.mk $(bar)
等价于
include foo a.mk b.mk c.mk bish bash
如果指示符“include”指定的文件不是以斜线开始(绝对路径,如/usr/src/Makefile...),而且当前目录下也不存在此文件;
make将根据文件名试图在以下几个目录下查找:
首先,查找使用命令行选项“-I”或者“--include-dir”指定的目录,如果找到指定的文件,则使用这个文件;
否则依此搜索以下几个目录(如果其存在):“/usr/gnu/include”、“/usr/local/include”和“/usr/include”。
为了和其它的make程序进行兼容。也可以使用“sinclude”来代替“-include”(GNU所支持的方式)。
======makefile的内置变量========
变量MAKEFILES
如果当前环境定义了一个“MAKEFILES”的环境变量,make执行时首先将此变量的值作为需要读入的Makefile文件,多个文件之间使用空格分开。
1. 环境变量指定的makefile文件中的“目标”不会被作为make执行的“终极目标”。
2. 环境变量所定义的文件列表,在执行make时,如果不能找到其中某一个文件(不存在或者无法创建)。make不会提示错误,也不退出。
3. make在执行时,首先读取的是环境变量“MAKEFILES”所指定的文件列表,之后才是工作目录下的makefile文件,“include”所指定的文件是在make发现此关键字的时、暂停正在读取的文件而转去读取“include”所指定的文件。
变量“MAKEFILES”主要用在“make”的递归调用过程中的的通信。实际应用中很少设置此变量。一旦设置了此变量,在多层make调用时;由于每一级make都会读取“MAKEFILES”变量所指定的文件,这样可能导致执行的混乱(可能不是你想看到的执行结果)。
变量MAKEFILE_LIST
make程序在读取多个makefile文件时,包括由环境变量“MAKEFILES”指定、命令行指、当前工作下的默认的以及使用指示符“include”指定包含的,在对这些文件进行解析执行之前make读取的文件名将会被自动的追加到变量“MAKEFILE_LIST”的定义域中。
这样我们就可以通过测试此变量的最后一个字来得知当前make程序正在处理的是具体的那个makefile文件。具体地说就是一个makefile文件中当使用指示符“include”包含另外一个文件之后,变量“MAKEFILE_LIST”的最后一个只可能是指示符“include”指定所要包含的那个文件的名字。如果一个makefile的内容如下:
name1 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
include inc.mk
name2 := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
all:
@echo name1 = $(name1)
@echo name2 = $(name2)
执行make,则看到的将是如下的结果:
name1 = Makefile
name2 = inc.mk
此例子中涉及到了make的函数的和变量定义的方式,这些将在后续的章节中有详细的讲述
注:words $(MAKEFILE_LIST) words的功能是获取其后变量的值中有多少个单词(空格隔开)。
其他特殊变量
GNU make支持一个特殊的变量,此变量不能通过任何途经给它赋值。此变量展开以后是一个特定的值。第一个重要的特殊的变量是“.VARIABLES”。它被展开以后是此引用点之前、makefile文件中所定义的所有全局变量列表。包括:空变量(未赋值的变量)和make的内嵌变量,但不包含目标指定的变量,目标指定变量值在特定目标的上下文有效。
当使用“-t(--touch)”选项来对Makefile目标文件进行时间戳更新时,对于哪些makefile文件的目标是无效的。
就是说即使执行make时使用了选项“-t”,那些目标是makefile文件的规则同样也会被make执行(而其它的规则不会被执行,make只是简单的更新规则目标文件的时间戳);类似还有选项“-q(—question)”和“-n(—just-print) ”,这主要是因为一个过时的makefile文件对其它目标的重建规则在当前看来可能是错误的。
正因为如此,执行命令“make –f mfile –n foo”首先会试图重建“mfile文件”、并重新读取它,之后会打印出更新目标“foo”规则中所定义的命令但不执行此命令。
一类是这些依赖文件的更新需要对应更新目标文件,另一类是这些依赖的更新不会导致目标被重建。第二类的依赖我们就称他为:“order-only”依赖。在书写规则时,“order-only”依赖使用管道符号“|”开始,作为目标的一个依赖文件。规则的依赖列表中管道符号“|”左边的是常规依赖文件,所有出现在管道符号右边的就是“order-only”依赖。这样的规则书写格式如下:
TARGETS : NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES
通配符也可以用在规则的依赖文件名中。看看下面这个例子。执行“make print”,执行的结果是打印当前工作目录下所有的在上一次打印以后被修改过的“.c”文件。
print: *.c
lpr -p $?
touch print
两点说明:
1. 上述的规则中目标“print”时一个空目标文件。(不存在一个这样的文件,此目标不代表一个文件,它只是记录了一个所要执行的动作或者命令。参考4.8 空目标文件 一节)。
2. 自动环变量“$?”用在这里表示依赖文件列表中被改变过的所有文件。
变量定义中使用的通配符不会被展开(因此在定义变量不能按照这这种方式,下一小节将会详细讨论)。如果Makefile有这样一句:“objects = *.o”。那么变量“objects”的值就是“*.o”,而不是使用空格分开的所有.o文件列表。如果需要变量“objects”代表所有的.o文件,则需要是用函数“wildcard”来实现(objects = $(wildcar *.o))。
关于隐含规则
当一个隐含规则的目标是另外一个隐含规则的依赖时,我们称它们是一个隐含规则链
4.9 Makefile的特殊目标
在Makefile中,有一些名字,当它们作为规则的目标出现时,具有特殊含义。它们是一些特殊的目标,GNU make所支持的特殊的目标有:
.PHONY:
目标“.PHONY”的所有的依赖被作为伪目标。伪目标时这样一个目标:当使用make命令行指定此目标时,这个目标所在规则定义的命令、无论目标文件是否存在都会被无条件执行。参考 3.6 Makefile伪目标 一节
.SUFFIXES:
特殊目标“SUFFIXES”的所有依赖指出了一系列在后缀规则中需要检查的后缀名(就是当前make需要处理的后缀)。参考 9.7 后缀规则 一节
.DEFAULT
Makefile中,目标“.DEFAULT”所在规则定义的命令,被用在重建那些没有具体规则的目标(明确规则和隐含规则)。就是说一个文件作为某个规则的依赖,但却不是另外一个规则的目标时。Make程序无法找到重建此文件的规则,此种情况时就执行“.DEFAULT”所指定的命令。
.PRECIOUS
目标“.PRECIOUS”的所有依赖文件在make过程中会被特殊处理:当命令在执行过程中被中断时,make不会删除它们(可参考 4.5 中断make的执行 一节)。而且如果目标的依赖文件是中间过程文件,同样这些文件不会被删除。这一点目标“.PRECIOUS”和目标“.SECONDAY”实现的功能相同。参考 9.4 make隐含规则链 一节
另外,目标“.PRECIOUS”的依赖文件也可以是一个模式,例如“%.o”。这样可以保留有规则创建的中间过程文件。
.INTERMEDIATE
目标“.INTERMEDIATE”的依赖文件在make时被作为中间过程文件对待。没有任何依赖文件的目标“.INTERMEDIATE”没有意义。参考 9.4 make隐含规则链 一节
.SECONDARY
目标“.SECONDARY”的依赖文件被作为中间过程文件对待。但这些文件不会被自动删除(可参考 9.4 make隐含规则链 一节)
没有任何依赖文件的目标“.SECONDARY”的含义是:将所有的文件作为中间过程文件(不会自动删除任何文件)。
.DELETE_ON_ERROR
如果在Makefile中存在特殊目标“.DELETE_ON_ERROR”,make在执行过程中,如果规则的命令执行错误,将删除已经被修改的目标文件。参考 4.4 命令执行的错误 一节
.IGNORE
如果给目标“.IGNORE”指定依赖文件,则忽略创建这个文件所执行命令的错误。给此目标指定命令是没有意义的。当此目标没有依赖文件时,将忽略所有命令执行的错误。参考 4.4 命令执行的错误 一节
.LOW_RESOLUTION_TIME
目标“.LOW_RESOLUTION_TIME”的依赖文件被make认为是低分辨率时间戳文件。给目标“.LOW_RESOLUTION_TIME”指定命令是没有意义的。
通常文件的时间辍都是高分辨率的,make在处理依赖关系时、对规则目标-依赖文件的高分辨率的时间戳进行比较,判断目标是否过期。但是在系统中并没有提供一个修改文件高分辨率时间辍的机制(方式),因此类似“cp -p”这样的命令在根据源文件创建目的文件时,所产生的目的文件的高分辨率时间辍的细粒度部分被丢弃(来源于源文件)。可能会造成目的文件的时间戳和源文件的相等甚至不及源文件新。处理此类命令创建的文件时,需要将命令创建的文件作为目标“.LOW_RESOLUTION_TIME”的依赖,声明这个文件是一个低分辨率时间辍的文件。例如:
.LOW_RESOLUTION_TIME: dst
dst: src
cp -p src dst
首先规则的命令“cp –p src dst”,所创建的文件“dst”在时间戳上稍稍比“src”晚(因为命令不能更新文件“dst”的细粒度时间)。因此make在判断文件依赖关系时会出现误判,将文件作为目标“.LOW_RESOLUTION_TIME”的依赖后,只要规则中目标和依赖文件的时间戳中的初始时间相等,就认为目标已经过期。这个特殊的目标主要作用是,弥补系统在没有提供修改文件高分辨率时间戳机制的情况下,某些命令在make中的一些缺陷。
对于静态库文件(文档文件)成员的更新也存在这个问题。make在创建或者更新静态库时,会自动将静态库的所有成员作为目标“.LOW_RESOLUTION_TIME”的依赖。
.SILENT
出现在目标“.SILENT”的依赖列表中的文件,make在创建这些文件时,不打印出重建此文件所执行的命令。同样,给目标“.SILENT”指定命令行是没有意义的。
没有任何依赖文件的目标“.SILENT”告诉make在执行过程中不打印任何执行的命令。现行版本make支持目标“.SILENT”的这种功能和用法是为了和旧版本的兼容。在当前版本中如果需要禁命令执行过程的打印,可以使用make的命令行参数“-s”或者“--silent”。参考 8.7 make的命令行选项 一节
.EXPORT_ALL_VARIABLES
此目标应该作为一个简单的没有依赖的目标,它的功能含义是将之后所有的变量传递给子make进程。参考 4.6 make的递归执行 一节
.NOTPARALLEL
Makefile中,如果出现目标“.NOPARALLEL”,则所有命令按照串行方式执行,即使存在make的命令行参数“-j”。但在递归调用的子make进程中,命令可以并行执行。此目标不应该有依赖文件,所有出现的依赖文件将被忽略。
所有定义的隐含规则后缀作为目标出现时,都被视为一个特殊目标,两个后缀串联起来也是如此,例如“.c.o”。这样的目标被称为后缀规则的目标,这种定义方式是已经过时的定义隐含规则的方法(目前,这种方式还被用在很多地方)。原则上,如果将其分为两个部分、并将它们加到后缀列表中,任何目标都可采用这种方式来表示。实际中,后缀通常以“.”开始,因此,以上的这些特别目标同样是以“.”开始。可参考 9.7 后缀规则 一节