Makefile Learn
一、什么是Makefile
二、编译过程&原理
hello.c -> 预处理(cpp) -> hello.i -> 编译器(ccl) -> hello.s -> 汇编器(as) -> hello.o -> 链接器(ld) -> hello可执行二进制目标程序
| 文件后缀 | 后缀含义 | 文件后缀 | 后缀含义 |
|---|---|---|---|
| .a | 静态库文件 | .c | C语言源程序文件 |
| .h | C语言头文件 | .i | 预处理文件 |
| .o | 目标文件可用于链接 | .s | 汇编文件 |
| .so | 动态(共享)库文件 |
1. 预处理指令
gcc -E [C源文件] //不输出任何预处理文件
gcc -E [C源文件] -o [输出的预处理文件] //输出指定名称的预处理文件
2. 生成汇编语言指令
gcc -S [C源文件] //不输出任何汇编文件
gcc -S [C源文件] -o [输出的汇编文件] //输出指定名称的汇编文件
3. 生成目标文件指令
gcc -c [C源文件] //不输出任何目标文件
gcc -c [C源文件] -o [输出的目标文件] //输出指定名称的目标文件
4. 直接由源码生成可执行文件
gcc [C源文件] //直接输出 "a.out" 文件,使用./a.out可执行目标文件
gcc [C源文件] -o [输出的目标文件] //指定输出的目标文件名称
//虽然没有写 -E -S -c 等命令,但不意味着上述过程没有执行
5. 生成静态库文件
- 编译成.o文件
gcc -c [.c] -o [自定义文件名]
- 编译静态库
ar -r [lib自定义库名称.a] [.o] [.o]
- 编译成可执行文件
gcc [.c] [.a] -o [自定义输出文件名]
gcc [.c] -o [自定义输出文件名] -l[库名] -L[库所在路径] - 链接静态库和C源文件生成可执行文件
gcc [C源文件] [静态库文件] -o [自定义输出文件名]
6. 生成动态库文件
- 编译输出.o文件
gcc -c -fpic [.c] [.c] ...
- 编译动态库
gcc -shared [.o] [.o] ... -o [lib自定义库名.so]
- 链接动态库和C源文件生成可执行文件
gcc [.c] -o [自定义可执行文件] -l[库名] -L[库路径] -Wl , -rpath=[库路径]
三、Makefile基本格式、规则、伪目标
1. 基本格式
targets(目标文件) : prerequisties(依赖项)
[tab]command(shell命令)
targets可以是一个[lable]
命令前加一个 @ 可以不打印命令
2. Makefile规则
make会在当前目录下找到一个名为Makefile的文件。
找到后会查找第一个目标文件(targets),并把这个文件作为最终的目标文件。
如果target文件不存在,或者是target文件依赖的.o文件(prerequities)的文件修改时间要比target这个文件新,就会执行后面所定义的命令command来生成target这个文件。
如果target以来的.o文件(prerequisties)也存在,make会在当前文件中找到target为.o文件的依赖项,如果找到,再根据那个规则生成.o文件。
3. 伪目标
"伪目标"不是一个文件,只是一个标签。我们要显示地指明这个"目标"才能让其生效。
"伪目标"的取名不能和文件名重名,否则不会执行命令
为了避免和文件重名的这种情况。我们可以使用一个特殊标记, .PHONY来显示地指明一个目标是"伪目标",向make说明,不管是否有这个文件,这个目标就是"伪目标"。
.PHONY : clean
四、Makefile变量
1. 变量的定义
c := src/main.c #只是一个字符串
obj := objs/main.o
2. 变量的引用
- 可以使用()或者{}
$(obj) : ${cpp} #目标文件$(obj) 依赖文件${cpp}
3. 预定义变量
- $@ : 目标(target)的完整名称,所有的目标文件
- $< : 第一个依赖文件(prerequisties)的名称
- $^ : 所有的依赖文件(prerequisties),以空格分开,不包含重复的依赖文件
五、Makefile常用符号
1. =
- 简单的赋值运算符
- 将右边的值分配给左边
- 如果在后面的语句中重新定义了该变量,则使用新的值
2. :=
- 立即赋值运算符
- 用于在定义变量时立即求值
- 该值在定义后不再更改
- 即使在后面的语句中重新定义了该变量,变量值也不会再改变
3. ?=
- 默认赋值运算符
- 如果该变量已经定义,则不进行任何操作
- 如果该变量尚未定义,则求值并分配
4. +=
- 将右边的值加空格累加到左边的变量中
5. \
- 续行符,可以多行实际为一行
六、Makefile的常用函数
函数调用,很像变量的使用,也是 "$" 来标识的,其语法如下:
$(fn arguments1 , argument2 , ...) or ${fn arguments1 , argument2 , ...}
# fn:函数名
# arguments:函数参数,参数间以逗号,分隔 , 而函数名和参数之间以空格分隔
1. shell
$(shell <command> <arguments>)
- 名称:shell命令函数 ——shell
- 功能:调用shell的命令
- 返回:函数返回shell命令的执行结果
例子
#shell 指令 , 在src文件夹下找到全部的.cpp文件
cpp_src := $(shell find src -name *.cpp)
2. subst
$(subst <from>,<to>,<text>)
- 名称:字符串替换函数——subst
- 功能:把字符串
中的 字符串替换成 - 返回:函数返回被替换后的字符串
name1 := test1
name2 := test2
$(subst test,${name1},{name2})
#将name2中的test替换为test1,最后输出的结果为test12
3. patsubst
$(patsubst <pattern>,<replacement>,<text>)
- 名称:模式字符串替换函数——patsubst
- 功能:通配符%,表示任意长度的字符串,从text中取出pattern,替换成replacement
- 返回:函数返回被替换后的字符串
例子
name1 := test123
$(patsubst test,right,$(name1))
#将name1中的test替换为right并返回
4. foreach
$(foreach <var>,<list>,<text>)
- 名称:循环函数——foreach
- 参数:临时变量不会影响外部变量的值
- 功能:把字串
- 中的元素逐一取出来,执行
包含的表达式 - 返回:
所返回的每个字符串组成的整个字符串(以空格分隔)
name1 := myname
name2 := $(foreach item,$(name1),-L$(item))
#将name1中的字符串取出加上-L
5. dir
$(dir <names...>)
- 名称:取目录函数——dir
- 功能:从文件名序列中取出目录部分。目录部分是指最有一个反斜杠("/")之前的部分。如果没有反斜杠,那么返回"./"
- 返回:返回文件名序列的目录部分
示例
$(dir src/foo.c hacks)
#返回值是"src/ ./"
6. notdir
$(notdir <names...>)
- 功能:传入变量或列表,将其中的元素的路径去掉
7. filter
$(filter <names...>)
- 功能:根据参数过滤掉某些文件
8. basename
$(basename <names...>)
- 功能:去掉后缀
更多makefile函数参见:makefile函数
七、Makefile编译
1. Makefile编译步骤
1.1 预处理
1.2 编译成汇编语言
1.3 编译成.o文件
1.4 链接成可执行文件或库
2. 编译选项
编译选项
- -m64:指定编译为64位应用程序
- -std=:指定编译标准,例如:-std=c++11、-std=c++14
- -g:包含调试信息
- -w:不显示警告
- -O:优化等级,通常使用:-O3
- -I:加在头文件路径前
- -fpic:产生的没有绝对地址,全部使用相对地址,代码可以被加载到内存的任意位置,且可以正确的执行。
链接选项
- -l:加在库名前面
- -L:加在库路径前面
- -Wl,<选项>:将逗号分隔的<选项>传递给链接器
- -rpath=:"运行"的时候,去找的目录。运行的时候,要找.so文件,会从这个选项里指定的地方去找
浙公网安备 33010602011771号