Linux基础(9):linux中makefile文件
makefile文件的规则编写,工作过程,模式匹配,函数运用等重要知识点
定义&目的
make是linux自带的项目构建工具,帮助完成大量文件编译的工作。它根据makefile中的指令来工作,自动完成项目的构建。
makefile中规定了哪些文件需要先编译,后编译,重编译等问题。当makefile文件构建后之后,只需要一个make命令便可以实现自动编译。
规则
1 2 3 4 5 6
| target1,target2...: depend1, depend2,... command1 command2 ....... .......
|
三个组成部分:目标,依赖,命令
注意事项
- 每个命令前需要有一个Tab缩进并且每个命令独占一行
- 依赖可以是已经存在的文件,也可以是别的规则下生成的目标
- 因为有多个命令,所以也可以生成多个目标
具体示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
app:a.c b.c c.c gcc a.c b.c c.c -o app
app,app1:a.c b.c c.c d.c gcc a.c b.c -o app gcc c.c d.c -o app1
app:a.o b.o c.o gcc a.o b.o c.o -o app
a.o:a.c gcc -c a.c
b.o:b.c gcc -c b.c
c.o:c.c gcc -c c.c
|
注意:一般情况下,在嵌套中,后面的目标通常是作为第一条规则的依赖。
工作原理
make当完成第一条规则的执行后,便结束任务。当第一条规则的依赖不存在或者不完整的时候,会去接下来的规则中寻找。如果想单独执行makefile中的某一条指令, make 该条规则的目标文件
make会比较目标文件和依赖文件的时间戳来判定是不是需要执行make操作。
- 当目标文件不存在,直接make生成目标文件
- 当目标文件时间比依赖文件时间早,make更新目标文件
- 当目标文件时间比依赖文件时间晚,不执行make命令
make具有自动推导的能力,当makefile中规则不完全时,也能依赖自动推导来正确执行。
1 2 3 4 5 6 7 8 9 10 11 12
| gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c head.h main.c makefile sub.c gao@gao-VirtualBox:~/桌面/test/make_file$ cat makefile
cal : add.c sub.c main.c gcc *.c -o cal gao@gao-VirtualBox:~/桌面/test/make_file$ make gcc *.c -o cal gao@gao-VirtualBox:~/桌面/test/make_file$ ./cal a+b=20 a-b=0
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c head.h main.c makefile sub.c gao@gao-VirtualBox:~/桌面/test/make_file$ vim makefile gao@gao-VirtualBox:~/桌面/test/make_file$ cat makefile
cal : add.o sub.o main.o gcc *.o -o cal
add.o: add.c gcc -c add.c -o add.o
sub.o: sub.c gcc -c sub.c -o sub.o
main.o: main.c gcc -c main.c -o main.o gao@gao-VirtualBox:~/桌面/test/make_file$ make gcc -c add.c -o add.o gcc -c sub.c -o sub.o gcc -c main.c -o main.o gcc *.o -o cal gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c add.o cal head.h main.c main.o makefile sub.c sub.o gao@gao-VirtualBox:~/桌面/test/make_file$ ./cal a+b=20 a-b=0
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| gao@gao-VirtualBox:~/桌面/test/make_file$ vim makefile gao@gao-VirtualBox:~/桌面/test/make_file$ cat makefile
cal : add.o sub.o main.o gcc *.o -o cal
gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c head.h main.c makefile sub.c gao@gao-VirtualBox:~/桌面/test/make_file$ make cc -c -o add.o add.c cc -c -o sub.o sub.c cc -c -o main.o main.c gcc *.o -o cal gao@gao-VirtualBox:~/桌面/test/make_file$ ./cal a+b=20 a-b=0
|
变量
目的:采用变量简化makefile文件
类型:自定义变量,预定义(内置)变量,自动变量
操作示例
1 2 3 4 5 6 7 8 9
| cal : add.o sub.o main.o gcc *.o -o cal
obj=add.o main.o sub.o target=cal $(target):$(obj) gcc $(obj) -o $(target)
|
操作示例
1 2 3 4 5 6 7 8 9 10
| cal:add.o main.o sub.o gcc add.o main.o sub.o -o cal
obj=add.o main.o sub.o target=cal CFLAGS=-O3 $(target):$(obj) $(CC) $(obj) -o $(target) $(CFLAGS)
|
操作示例
1 2 3 4 5 6 7 8
| cal:add.o main.o sub.o gcc add.o main.o sub.o -o cal
cal:add.o main.o sub.o gcc $^ -o $@
|
模式匹配
基础语法
操作示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| cal : add.o sub.o main.o gcc *.o -o cal
add.o: add.c gcc -c add.c -o add.o
sub.o: sub.c gcc -c sub.c -o sub.o
main.o: main.c gcc -c main.c -o main.o
cal : add.o sub.o main.o gcc *.o -o cal
%.o:%.c gcc $< -c
|
结合变量&模式匹配的简化makefile文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| cal : add.o sub.o main.o gcc *.o -o cal
add.o: add.c gcc -c add.c -o add.o
sub.o: sub.c gcc -c sub.c -o sub.o
main.o: main.c gcc -c main.c -o main.o
target= cal obj= add.o sub.o main.o $(target): $(obj) gcc $(obj) -o $(target)
%.o: %.c gcc $^ -c
|
为什么要采用逐条.o的方案
因为采用.c的方案只要依赖中的某一个源文件被修改,所有的源文件都需要被重新编译,太耗时、效率低,而采用.o分条之后,修改哪一个源文件,哪个源文件被重新编译,不修改就不重新编译
函数
wildcard函数:主要作用是为了获取指定目录下的指定类型的文件。
使用格式
1 2 3 4 5 6
| $(wildcard 参数) 参数的类型:某个目录下的某种类型的文件 例如:当前文件夹下的.c文件 ./*.c ,也可以简化写为*.c 或者,具体某个文件夹下的.c文件,./sub/*.c
返回值:得到相应文件的绝对路径列表
|
使用案例
1 2 3 4
| src = $(wildcard /home/robin/a/*.c /home/robin/b/*.c *.c)
/home/robin/a/a.c /home/robin/a/b.c /home/robin/b/c.c /home/robin/b/d.c e.c f.c
|
patsubst函数:主要作用是按照指定的模式替换指定文件的后缀
使用格式
1 2 3 4 5 6 7 8 9 10 11 12 13
| $(patsubst <pattern>,<replacement>,<text>)
参数功能: pattern: 这是一个模式字符串,需要指定出要被替换的文件名中的后缀是什么 文件名和路径不需要关心,因此使用 % 表示即可 [通配符是 %] 在通配符后边指定出要被替换的后缀,比如: %.c, 意味着 .c 的后缀要被替换掉 replacement: 这是一个模式字符串,指定参数 pattern 中的后缀最终要被替换为什么 还是使用 % 来表示参数 pattern 中文件的路径和名字 在通配符 % 后边指定出新的后缀名,比如: %.o 这表示原来的后缀被替换为 .o text: 该参数中存储这要被替换的原始数据 返回值: 函数返回被替换过后的字符串。
|
使用案例
1 2 3 4
| src = a.cpp b.cpp c.cpp e.cpp
obj = $(patsubst %.cpp, %.o, $(src))
|
结合变量&模式匹配&函数的简化makefile文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| target= cal
src= $(wildcard *.c)
obj= $(patsubst %.c, %.o, $(src))
$(target): $(obj) gcc $^ -o $@
%.o: %.c gcc $^ -c
|
进一步优化
问题:不能去删除过程中产生的.o文件和可执行程序,不利于多次编译
办法:添加新的规则,执行删除命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| target= cal
src= $(wildcard *.c)
obj= $(patsubst %.c, %.o, $(src))
$(target): $(obj) gcc $^ -o $@
%.o: %.c gcc $^ -c
clean: rm $(obj) $(target)
|
由于这是一条伪命令,直接执行make并不会执行该条命令,实际使用中要make clean指令来执行这条规则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c head.h main.c makefile sub.c
gao@gao-VirtualBox:~/桌面/test/make_file$ make gcc main.c -c gcc add.c -c gcc sub.c -c gcc main.o add.o sub.o -o cal
gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c add.o cal head.h main.c main.o makefile sub.c sub.o
gao@gao-VirtualBox:~/桌面/test/make_file$ make clean rm add.o main.o sub.o cal
gao@gao-VirtualBox:~/桌面/test/make_file$ ls add.c head.h main.c makefile sub.c
|
问题因为make执行时需要判断目标与依赖的时间戳,clean指令并不想执行这个功能,可以采用如下方式(借助.PHONY关键字)来声明clean是个伪目标,使得clean指令不需要对比时间戳。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| target= cal
src= $(wildcard *.c)
obj= $(patsubst %.c, %.o, $(src))
$(target): $(obj) gcc $^ -o $@
%.o: %.c gcc $^ -c
.PHONY: clean clean: rm $(obj) $(target)
|
一个小tips:在shell命令前增加-,可以保证指令强制运行,如果执行失败也不会终止
测试如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| target= cal
src= $(wildcard *.c)
obj= $(patsubst %.c, %.o, $(src))
$(target): $(obj) gcc $^ -o $@
%.o: %.c gcc $^ -c
.PHONY: clean clean: -rm $(obj) $(target) echo "hello!"
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| gao@gao-VirtualBox:~/桌面/test/make_file$ make gcc main.c -c gcc add.c -c gcc sub.c -c gcc main.o add.o sub.o -o cal gao@gao-VirtualBox:~/桌面/test/make_file$ ./cal a+b=20 a-b=0 gao@gao-VirtualBox:~/桌面/test/make_file$ make clean rm add.o main.o sub.o cal echo "hello!" hello!
gao@gao-VirtualBox:~/桌面/test/make_file$ make clean rm main.o add.o sub.o cal rm: 无法删除 'main.o': 没有那个文件或目录 rm: 无法删除 'add.o': 没有那个文件或目录 rm: 无法删除 'sub.o': 没有那个文件或目录 rm: 无法删除 'cal': 没有那个文件或目录 make: [makefile:20:clean] 错误 1 (已忽略) echo "hello!" hello!
|
此处的makefile文件就是最终比较完美版的啦~
makefile文件优秀示例
附上一份丙哥的makefile相对复杂的范本
1 2 3 4 5 6 7 8 9 10
| . ├── include │ └── head.h ==> 头文件, 声明了加减乘除四个函数 ├── main.c ==> 测试程序, 调用了head.h中的函数 └── src ├── add.c ==> 加法运算 ├── div.c ==> 除法运算 ├── mult.c ==> 乘法运算 └── sub.c ==> 减法运算
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| target = app
src=$(wildcard *.c ./src/*.c)
obj=$(patsubst %.c, %.o, $(src))
include=./include
$(target):$(obj) gcc $^ -o $@
%.o:%.c gcc $< -c -I $(include) -o $@
.PHONY:clean
clean: -rm $(obj) $(target) -f
|