make
1.1 常用符号
1.1.1 编译器
CC // C语言编译器,默认值为gcc 默认的变量,无需用户自定义,也可以改变其值
CXX // C++语言编译器,默认值为g++ 默认的变量,无需用户自定义,也可以改变其值
CFLAGS // C语言编译器的编译选项
LDFLAGS // C语言编译器的链接选项
CXXFLAGS // C++语言编译器的编译选项
1.1.2 自动化变量
$@ 代表规则中的目标文件名。如果目标是一个文档(Linux中,一般称.a文件为文档),那么它代表这个文档的文件名。在多目标的模式规则中,它代表的是哪个触发规则被执行的目标文件名。
$% 规则的目标文件是一个静态库文件时,代表静态库的一个成员名。例如,规则的目标是“foo.a(bar.o)”,那么,“$%”的值就为“bar.o”,“$@”的值为“foo.a”。如果目标不是函数库文件,其值为空。
$< 规则的第一个依赖文件名。如果是隐含规则,则它代表通过目标指定的第一个依赖文件。
$? 所有比目标文件更新的依赖文件列表,空格分割。如果目标是静态库文件名,代表的是库成员(.o文件)的更新情况。
$^ 规则的所有依赖文件列表,使用空格分隔。如果目标是静态库文件名,它所代表的只能是所有库成员(.o文件)名。一个文件可重复的出现在目标的依赖中,变量“$^”只记录它的一次引用情况。就是说变量“$^”会去掉重复的依赖文件。
$+ 类似“$^”,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时,库的交叉引用场合。
$(@D) 代表目标文件的目录部分(去掉目录部分的最后一个斜杠)。如果“$@”是“dir/foo.o”,那么“$(@D)”的值为“dir”。如果“$@”不存在斜杠,其值就是“.”(当前目录)。注意它和函数“dir”的区别!
$(@F) 目标文件的完整文件名中除目录以外的部分(实际文件名)。如果“$@”为“dir/foo.o”,那么“$(@F)”只就是“foo.o”。“$(@F)”等价于函数“$(notdir $@)”。
$(%D),$(%F) 当以如“archive(member)”形式静态库为目标时,分别表示库文件成员“member”名中的目录部分和文件名部分。它仅对这种形式的规则目标有效。
$(<D),$(<F) 分别表示规则中第一个依赖文件的目录部分和文件名部分。
$(^D),$(^F) 分别表示所有依赖文件的目录部分和文件部分(不存在同一文件)。
$(+D),$(+F) 分别表示所有依赖文件的目录部分和文件部分(可存在重复文件)。
$(?D),$(?F) 分别表示被更新的依赖文件的目录部分和文件部分。
% 是通配符,%.c表示工程里的.c文件
@ 命令行以'@'打头的含义: 在执行到的时候不回显相应的命令内容,只显示命令的输出。
“-” 命令行以'-'打头的含义: 在执行到的时候如果发生错误(退出返回非零状态)时,不中断make过程。
“+” 命令行以'+'打头的含义: makefile中以+开头的命令的执行不受到 make的-n,-t,-q三个参数的影响。我们知道,在make的时候,如果加上-n, -t, -q这样的参数,都是不执行相应命令的,而以'+'开头的命令,则无论make命令后面是否跟着三个参数,都会被执行。
1.2 变量
1.2.1 变量定义
OBJ = ../objs
make变量(Makefile中定义的或者是make的环境变量)的引用使用“$(VAR)”格式。
1.2.2 变量赋值
= 当它的右边赋值是变量时,这个变量的定义在本条语句之前或之后都可以,即可以递归展开。
:= 它右边赋得值如果是变量,只能使用在这条语句之前定义好的,而不能使用本条语句之后定义的变量,即不可以递归展开。
?= 该符号左边的变量,如果在本条语句之前没有定义过,则执行本语句,如果已经定义,那么本语句什么都不做。
+= 是添加等号后面的值
1.2.3 变量操作
$(VAR:A=B) 同${VAR:A=B}
${VAR:A=B} 替换变量“VAR”中所有“A”字符结尾的字为“B”结尾的字。“结尾”的含义是空格之前(变量值的多个字以空格分开)
1、wildcard : 扩展通配符 src=$(wildcard *.c ./sub/*.c) 取当前路径和sub路径下的所有.c
src = a.c b.c ./sub/c.c ./sub/d.c
2、notdir : 去除路径 file=$(notdir $(src))
file = a.c b.c c.c d.c
3、dir:获取路径 dir=$(dir $(src))
dir = . . ./sub ./sub
4、patsubst :替换通配符 obj=$(patsubst %.c,%.o,$(file) ) 可用于构造目标文件
obj = a.o b.o c.o d.o
5、sed:替换指定字符串 sed 's/要替换的字符串/新的字符串/g' (要替换的字符串可以用正则表达式),可以用 –i 直接替换文件内容
6、grep:(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种文本搜索,使用正则表达式搜索文本,并把匹配的行打印出来。
grep "\$(COMPONENT_NAME)_SRC =" $MK_PATH/$1.mk >> makefiles/$1_files.mk
1.3 Makefile的编写
Makefile的作用就是实现自动化编译,它关系到了整个工程的编译规则。一个工程中可能包含很多的源文件,并且按功能,模块等分别存放在特定的目录中,makefile定义了一系列的跪着来指定哪些文件需要先编译,哪些文件需要后编译,哪些要重新编译,以及更复杂的操作。Makefile写好之后,只需要一个make命令,整个工程就可以自动编译,极大地提高了软件开发的效率。
1.3.1 依赖关系
Makefile其实就是为了指定文件依赖性的问题,下面的例子中,target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),作为一个伪目标存在。下面的command就是make要执行命令,可以是编译或者链接命令或者是任意的shell命令,但是命令前必须用tab缩进。Make会把include指令所指定的任何文件视为一个需要更新的工作目标(GNU+Make项目管理第三版P43)。
target ... : prerequisites ...
command
...
1.3.2 工作方式
当我们输入make 命令时:
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
3、如果edit文件不存在,或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新,那么,他就会执行后面所定义的命令来生成edit这个文件。
4、如果edit所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。(这有点像一个堆栈的过程)
5、当然,你的C文件和H文件是存在的啦,于是make会生成 .o 文件,然后再用 .o 文件生命make的终极任务,也就是执行文件edit了。
通常makefile的最后会有一个clean目标,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显式要make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
1.3.3 自动生成依赖关系
在19B的编译过程中,每次编译都需要执行clean命令清除所有的所有的目标文件,否则,新的代码修改就不会生效,极大地降低了编译效率,通常这种情况的原因是makefile的依赖关系不全或者依赖关系有误。
对19B的编译过程进行梳理发现其依赖关系中仅对.c文件进行了依赖,没有将头文件包含进去,这样当头文件更改时,不会触发任何编译。所以出现了必须clean重编译的效率问题。
完善所有目标文件的依赖关系工作量是巨大的,好在gcc为我们提供了自动生成依赖关系的选项。
————————————————
版权声明:本文为CSDN博主「yantao_wang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yantao_wang/article/details/83000242