1. 依赖的类型
GUN make中存在两种类型的依赖:
常规依赖:依赖文件中任何一个比目标文件新,则认为规则的目标过期需要重建。
order-only依赖:当order-only依赖中的文件更新时,若目标不存在,则生成目标; 若目标存在,则不更新目标。
常规、order-only依赖书写规则:
TARGETS:NORMAL-PREREQUISITES | ORDER-ONLY-PREREQUISITES
使用 “ | ”分隔常规依赖和order-only依赖,如果有文件同时出现在两边,则算为常规依赖。
2. 文件名通配符
2.1 通配符类型
makefile中文件名可以使用通配符,有 * , ? , [...]
通配符可以使用以下场合:
- 使用在规则的目标,依赖中, make在读取makefile时自动对其进行匹配处理(通配符展开)。
- 可出现在规则的命令中,通配处理是在shell执行此命令时完成的。
- 除上述两种情况之外的其他上下文中,不能直接使用通配符。
2.2 wildcard函数
在规则中,通配符会被自动展开,但在变量定义和函数引用时,通配符将失效。
若此时需要通配符生效,使用wildcard函数:
$(wildcard PATTERN)
在makefile中,它被展开为已存在的,使用空格分开的,匹配模式的所有文件列表。
比如:
$(wildcard *.c)来获取工作目录下的所有.c文件列表。
3. 目录搜索
3.1 一般搜索(VPATH)
通过变量VPATH可以指定依赖文件的搜索路径,使用 空格 或在 冒号: 将多个需要搜索的目录文件分开,搜索顺序是按照目录排列顺序进行。
当规则的依赖文件在当前目录不存在时,make会在此变量所指定的目录下寻找这些依赖文件。
比如:
VPATH = src:../headers
指定了src 和../headers两个搜索目录,对于foo:foo.c ,如果foo.c存在于src目录下,此规则等价于foo:src:/foo.c
3.2选择性搜索(vpath)
选择性搜索可以为不同的类型文件指定不同的搜索目录。
vpath中的PATTERN需要包含模式字符%, 其表示匹配一个或多个字符。表示具有相同特征的一类文件。
DIRECTORIES指定了搜索此类文件目录。
三种使用方法:
- vpath PATTERN DIRECTORIES
为所有符合模式PATTERN的文件指定搜索目录DIRECTORIES,多个目录使用空格 或者 冒号: 分隔。
- vpath PATTERN
清除之前为符合模式PATTERN 的文件文件设置的搜索路径。
- vpath 清除所有已被设置的文件搜索路径。
比如:
vpath %.h ../headers
表示makefile中出现的.h文件,如果不能在当前目录下找到,则到目录../headers 下寻找。
注意,这里的指定路径仅限于在makefile文件内容中出现的.h文件,并不能指定源文件中包含的头文件所在的路径。在.c源文件中包含的头文件路径需要使用gcc的 -I 来指定。
3.3 目录搜索的机制
make在解析Makefile文件执行规则时对文件路径保存或废弃所依据的算法如下:
- 如果规则的 目标文件 在makefile文件所在的目录下不存在,那么执行目录搜索。
- 如果目录搜索成功,在指定的目录下存在此目标的 规则,那么搜索到的完整的路径名就被作为临时目标文件被保存。
- 对于规则中所有的 依赖文件 使用相同的方法处理。
- 完成第三步依赖处理后,make程序就可以决定规则的目标是否需要重建,存在两种情况:
- 规则的目标不需要重建,规则中的所有文件的完整路径名有效,已经存在的目标文件所在的目录不会被改变。
- 规则的目标需要重建,则规则的目标文件会在工作目录下被重建,而不是在目录搜索时所得到的目录。
- 若使用GPATH而不是VPATH指定搜索目录,则目标在重建时,会在目录搜索时所得到的目录中进行重建。
3.4 库文件和搜索目录
makefile可以使用 -INAME 对静态库.a和动态库.so进行目录搜索。
搜索的依赖库文件名由 .LIBPATTERNS指定,其一般是多个包含模式字符%的字,多个字之间使用空格分开。
按照 .LIBPATTERNS出现的字顺序,用NAME逐个代替字的模式字符,得到库文件名,根据这个库文件名在搜索目录下搜索。
默认情况下, .LIBPATTERNS值为 lib%.so lib%.a ,所以一般先搜索libNAME.so库,再搜索libNAME.a库。
4. Makefile中的目标
4.1 伪目标
伪目标使用:
.PHONY:clean //使用.PHONY声明伪目标clean
clean:
rm *.o temp
伪目标优点:
- 当工作目录下不存在clean这个文件的时候,输出make clean时,对应规则一定会执行。但是如果当前工作目录下存在clean这个文件时,当输入make clean,由于这个规则没有依赖文件,所以目标被认为是最新的而不去执行规则所定义的命令。
- 当一个目标被声明为伪目标的时候,make在执行此规则时不会试图去查找隐含规则来创建它。
4.2 强制目标
强制目标的目标是一个不存在的文件名,并且没有命令和依赖。
在执行该强制目标时,目标总是被认为是最新的。因此,如果强制目标作为依赖,导致该规则一定会执行。
比如:
clean:FORCE //强制目标作为依赖
rm $(objects)
FORCE: //强制目标(没有依赖和命令)
4.3 多目标
多目标规则意味着所有的目标具有相同的依赖文件。
多目标使用情况:
aaa bbb:text.g
generate text.g -$(subst output,$@) > $@
等价于:
aaa:text.g
generate text.g -$(subst output,$@) > aaa
bbb:text.g
generate text.g -$(subst output,$@) > bbb
4.4 多规则目标
对于一个多规则的目标,重建此目标的命令只能出现在一个规则中。
如果多个规则同时给出重建此目标的命令,make将使用最后一个规则中所定义的命令。
4.5 静态模式
静态模式规则:
规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。并且依赖文件必须是类似的而不是完全相同的。
4.5.1 语法:
TARGETS...:TARGET-PATTERN:PREREQ-PATTERN...
COMMANDS
...
TARGETS列出了此规则的一系列目标文件。
TARGET-PATTERN和PREREQ-PATTERNS说明了如何为每一个目标文件生成的依赖文件。从目标模式TARGET-PATTERN的目标名字中抽取一部分字符串(称为 茎),使用茎代替依赖模式PREREQ-PATTERNS中相应部分来产生对应目标的依赖文件。
比如:
objects=foo.o bar.o
all:$(objects)
$(objects):%o:%c
$(CC) -c $(CFLAGS) $< -o $@
规则中 %o:%c 描述了所有的 .o 文件的依赖文件为对应的 .c 文件。
对目标foo.o取其茎 foo 代替对应的依赖模式%.c中的模式字符%之后可得到目标的依赖文件foo.c。
上边的规则描述了以下两个具体的规则:
foo.o:foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o:bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
4.5.2 静态模式和隐含规则区别
- 隐含规则可以被用在 任何 和它相匹配的目标上。当存在多个隐含规则和目标模式相匹配时,只执行其中一个规则,具体执行哪个取决于定义规则的顺序。
- 静态模式规则只能用在规则中 明确 指出的那些文件的重建过程中。如果一个目标存在两个规则,则make执行会提示错误。
4.6 自动产生依赖
gcc 通过 -M 选项自动寻找源文件中包含的头文件,并生成文件的依赖。
当不需要依赖关系中考虑标准库头文件时,使用 -MM 参数。
比如,main.c中只包含了头文件def.h,则:
gcc -M main.c
其输出是:
main.o:main.c def.h