目录
- 简介
- 通配符
- 文件搜寻
- 伪目标
- 多目标
- 静态模式
- 生成依赖关系
简介
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。在 Makefile 中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。
规则举例
1 | targets : prerequisites |
通配符
支持 * ,? ,[…]和shell中的含义是相同的:
*
匹配任意字符0或无数次?
匹配任意字符1次[…]
匹配括号中给出的任意一个字符,而在括号中加入!
,表示匹配不在括号中给出的字符
文件搜寻
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉 make,让make在自动去找。Makefile文件中的特殊变量VPATH
就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。
1 | VPATH = src:../headers |
上面的的定义指定两个目录,“src”和“../headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然当前目录永远是最高优先搜索的地方)
另一个设置文件搜索路径的方法是使用 make的vpath
关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH
变量很类似,但是它更为灵活。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:
vpath <pattern> <directories>
为符合模式<pattern>
的文件指定搜索目录<directories>
。vpath <pattern>
清除符合模式<pattern>
的文件的搜索目录。vpath
清除所有已被设置好了的文件搜索目录。
vapth 使用方法中的<pattern>
需要包含%
字符,意思是匹配零或若干字符,
例如,“%.h”表示所有以“.h”结尾的文件。
1 | vpath %.h ../headers |
该语句表示,要求 make 在“../headers”目录下搜索所有以.h
结尾的文件。(如果某文件在当前目录没有找到的话) .
补充:经过实测,文件搜寻后,在执行命令时,将源文件加入了相对路径,而生成的中间文件还是在当前目录.
伪目标
简单来说就是避免文件重名,例如make clean
只是想要执行下面的makefile命令,并不是要生成clean这个文件,一般来讲我们的all,clean等动作都可以加入伪目标,统一只执行makefile中写好的命令即可,例如:
1 |
|
多目标
Makefile 的规则中的目标可以不止一个,其支持多目标,有可能我们的多个目标同时依赖于一个文件,并且其生成的命令大体类似。于是我们就能把其合并起来。
静态模式
1 | <targets ...>: <target-pattern>: <prereq-patterns ...> |
例子:
1 | objects = foo.o bar.o |
上面的例子中,指明了我们的目标从$(object)
中获取,%.o
表明要所有以.o
结尾的目标,也就是foo.o bar.o
,而依赖模式%.c
则取模式%.o
的%
,也就是foo bar
,并为其加下.c
的后缀,于是我们的依赖目标就是foo.c bar.c
。
备注:这里感觉和模式规则很像,其实把最左边的目标去掉就是模式规则啦,可能这样写比较明显吧
生成依赖关系
为什么要使用后缀名为.d的依赖文件
在Makefile中,目标文件的依赖关系需要包含源文件和一系列的头文件,但是一般在我们的Makefile中的依赖关系都是省略头文件的,这就有一个致命的问题,就是头文件修改时,目标文件不会重新生成。
如果是一个比较大型的工程,我们必需清楚每一个源文件都包含了哪些头文件,并且在加入或删除某些头文件时,也需要一并修改Makefile,这是一个很没有维护性的工作,所以可以使用C/C++编译器的-M选项自动获取源文件中包含的头文件,并生成一个依赖关系,这个依赖关系就保存在.d文件中。
生成方式
选项 | 特点 | 共同点 |
-M | 依赖关系包含标准库 | 默认打开-E参数,使得编译器在预处理结束就停止编译 |
-MM | 依赖关系不包含标准库 | |
-MD | 依赖关系包含标准库 | 不打开-E参数 |
-MMD | 依赖关系不包含标准库 | |
-MF + fileName | 将依赖关系写入到fileName文件中 | |
-MT | 在生成的依赖文件中,指定规则中的目标 |
gcc -M main.c
终端输出
1
2
3
4
5
6
7
8
9
10
11main.o: main.c defs.h \
/usr/include/stdio.h \
/usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/types.h \
/usr/include/bits/pthreadtypes.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.hgcc -MM main.c
终端输出1
main.o: main.c defs.h
gcc -M -MF main.d main.c
则 “-M” 输出的内容就保存在 main.d 文件中了-MT选项
这个最为重要,可以将.d文件本身作为目标加入到依赖文件中,这样就可在头文件更新时,也更新依赖文件
1
2
3gcc main.c -MM -MF main.d -MT main.d -MT main.o
$ cat main.d #查看生成的依赖文件的内容
main.d main.o: main.c defs.h注:依赖规则中 main.d 和 main.o 目标都是通过 “-MT” 选项指定的