一、makefile编写规则
1. makefile文件什么作用
总的来说就是编译生成工程文件的过程,可以参考 CMake, 指定哪些文件需要先编译,哪些文件后编译,哪些文件需要重新编译,里面是由一条条gcc指令组成的。
2. make命令
make解析并执行 makefile 文件,执行make命令,就可以根据makefile文件自动编译整个工程。
写了makefile文件,以后对于整个项目进行修改后,只需要执行以下make命令就可以重新编译工程,不用写一堆gcc脚本。
3. makefile文件规则
3.1. 文件命名
工程目录里面的makefile文件应该叫 makefile 或者 Makefile
3.2. 编写格式规则
目标 ... : 依赖 ...
(tab)命令(shell命令)
目标:最终要生成的文件
依赖:生成目标文件所依赖的文件
命令:通过执行命令,利用依赖文件生成目标文件。(注意:命令前面必须加tab)
4. 简单makefile文件
通过gcc -o命令,编译几个 .c文件生成可执行程序 “app”
nowcoder@nowcoder:~/Linux/lession07$ ls
add.c div.c head.h main.c mult.c sub.c
nowcoder@nowcoder:~/Linux/lession07$ vim Makefile
app:sub.c add.c mult.c div.c main.c
gcc sub.c add.c mult.c div.c main.c -o app
//执行make指令
nowcoder@nowcoder:~/Linux/lession07$ make
gcc sub.c add.c mult.c div.c main.c -o app
nowcoder@nowcoder:~/Linux/lession07$ ls
add.c app div.c head.h main.c Makefile mult.c sub.c //生成了可执行文件app
nowcoder@nowcoder:~/Linux/lession07$ ./app
a = 20, b = 12
a + b = 32
a - b = 8
a * b = 240
a / b = 1.666667
5. makefile原理
5.1. gcc命令执行之前,需要检查规则中的依赖是否存在
1)存在,执行命令,利用依赖文件生成目标文件
2)不存在,检查下面其他规则,检查是否有其他规则是用来生成此依赖的,找到了则执行命令。
示例,如果上面不是通过.c文件直接生成,而是要有中间过渡的.o文件作为依赖,那么就会用到上述规则。
nowcoder@nowcoder:~/Linux/lession07$ vim Makefile
app:sub.o add.o mult.o div.o main.o
gcc sub.o add.o mult.o div.o main.o -o app
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
div.o:div.c
gcc -c div.c -o div.o
main.o:main.c
gcc -c main.c -o main.o
nowcoder@nowcoder:~/Linux/lession07$ make
gcc -c sub.c -o sub.o
gcc -c add.c -o add.o
gcc -c mult.c -o mult.o
gcc -c div.c -o div.o
gcc -c main.c -o main.o
gcc sub.o add.o mult.o div.o main.o -o app
可以观察命令执行顺序,是先执行命令生成.o文件,然后才生成app文件。
Makefile文件中其他命令一般都是为第一条命令服务的。
如果Makefile文件中其他后面的命令和第一条命令没什么关系,一般是不会执行第一条之外的命令的。
5.2. 检测更新,执行命令时,会比较目标和依赖文件时间
1)如果依赖的时间比目标文件的时间早,说明依赖文件没有任何改变,那么就不用重新编译生成目标文件。
2)如果依赖的时间比目标文件时间晚,说明目标文件过时了,需要重新生成。
nowcoder@nowcoder:~/Linux/lession07$ make
make: “app”已是最新。
未进行任何修改,不会重新编译。
nowcoder@nowcoder:~/Linux/lession07$ vim main.c
nowcoder@nowcoder:~/Linux/lession07$ make
gcc -c main.c -o main.o
gcc sub.o add.o mult.o div.o main.o -o app
修改其中一个c文件,只会重新编译这一个。类比vs里面的“生成”和“重新生成”操作。
6. 比较两个版本的makefile文件
版本一:
app:sub.c add.c mult.c div.c main.c
gcc sub.c add.c mult.c div.c main.c -o app
版本二:
app:sub.o add.o mult.o div.o main.o
gcc sub.o add.o mult.o div.o main.o -o app
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
div.o:div.c
gcc -c div.c -o div.o
main.o:main.c
gcc -c main.c -o main.o
第二个版本更好,第一个版本虽然写着方便,但是一旦其中一个文件有改动,只能执行唯一一条命令,会重新编译所有依赖的文件。第二个版本只需要编译改变的那个文件即可。
二、makefile语法
变量、模式匹配、函数都是为了能够更简单的写makefile文件的内容。
1. 变量
1.1. 自定义变量
变量名=变量值 var=hello
1.2. 预定义变量
1.3. 获取变量的值
$(变量名)
1.4. 变量使用举例
app:sub.c add.c mult.c div.c main.c
gcc sub.c add.c mult.c div.c main.c -o app
利用变量进行替换
app:sub.c add.c mult.c div.c main.c
$(CC) $^ -o $@
src=sub.o add.o mult.o div.o main.o
target=app
$(target):$(src)
$(cc) $(src) -o $(target)
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
div.o:div.c
gcc -c div.c -o div.o
main.o:main.c
gcc -c main.c -o main.o
2. 模式匹配
利用模式匹配简化下面代码,可以发现都是x.c:x.o的格式,那就用一个通配符‘%’表示X即可。
src=sub.o add.o mult.o div.o main.o
target=app
$(target):$(src)
$(cc) $(src) -o $(target)
sub.o:sub.c
gcc -c sub.c -o sub.o
add.o:add.c
gcc -c add.c -o add.o
mult.o:mult.c
gcc -c mult.c -o mult.o
div.o:div.c
gcc -c div.c -o div.o
main.o:main.c
gcc -c main.c -o main.o
src=sub.o add.o mult.o div.o main.o
target=app
$(target):$(src)
$(CC) $(src) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
3. 函数
上面的makefile脚本,要把src=sub.o add.o mult.o di都写出来,如果这个文件有蛇多难道要全部写一遍吗?函数的引入就是为了解决这个问题。
3.1. wildcard函数
$(wildcard *.c ./sub/*.c)
返回值格式 a.c b.c c.c d.c e.c
解释一下:在$括号内部写一个wildcard表示函数调用,参数是文件目录加上目标目录下的文件类型。返回这些目录下对应类型的文件,有多个的话,用空格分隔返回文件名。
3.2. patsubst函数
$(patsubst %.c,%.o,x.c bar.c)
返回格式:x.o bar.o
解释:patsubst代表进行字符串替换, pattern是%.c,replacement是%.o,text是x.c bar.c。也就是在字符串x.c bar.c中,找到带有.c的字符串,替换为带.o的字符串。
src=$(wildcard ./*.c)
objs=$(patsubst %.c, %.o, $(src))
target=app
$(target):$(objs)
$(CC) $(objs) -o $(target)
%.o:%.c
$(CC) -c $< -o $@
4. 怎么去掉多余的.o文件 clean
clean:
rm $(objs) -f