makefile介绍
- makefile有点类似于windows中的vs一下,它定义了一些规则来指定,哪些文件需要先编译哪些文件需要后编译,甚至一些更复杂的操作,makefile也可以写shell脚本一样的命令。makefile相比于在shell下一直使用gcc去写方便的是只需要一个make命令就可以搞定,比如在当前目录使用
make
。 - makefile中会使用gcc去对源代码进行编译,生成.o或者可执行文件。
- makefile的命名规则:makefile或Makefile。
makefile的基本规则
makefile由一组规则组成,规则如下:
目标:依赖
(tab)命令
①目标:要生成的目标文件,比如main
②依赖:目标文件由哪些文件生成比如main.c
③命令:通过执行这条命令由依赖生成目标例如gcc -o main main.c
下面举个例子来更清楚的了解下如何使用makefile:
当前目录下,有一个main.c fun1.c fun2.c sum.c,根据这个依赖生成可执行文件main。
(1)第一次
#在当前文件夹下 vi makefile
#然后在makefile里面输入
main:main.c fun1.c fun2.c sum.c
gcc -o main main.c fun1.c fun2.c sum.c -I./
不过这明显可以看出效率很低,如果文件很多,我们就要疯狂输入。
通过这个我们来说明下makefile的原理:
若想生成目标文件,就要检查规则的依赖是否存在。依赖存在与否,又可以分为下面两种情况:
①依赖存在:检查规则中是否需要更新,比如你main是之前生成的,main.c的内容后面经过了修改,它会对比这个时间,判断你修改时间先后,再去进行重新生成。
②依赖不存在:如果依赖不存在,那么往下面找,如果下面能找到这个依赖生成,那么先执行下面那条,再回过来执行上面的。如果到结束都没找到生成这个依赖的,那么就报错。
(2)第二次
main:main.o fun1.o fun2.o sum.o
gcc -o main main.o fun1.o fun2.o sum.o
main.o:main.c
gcc -o main.o -c main.c -I./
fun1.o:fun1.c
gcc -o fun1.o -c fun1.c
fun2.o:fun2.c
gcc -o fun2.o -c fun2.c
sum.o:sum.c
gcc -o sum.o -c sum.c
这里我们可以发现,它其实从main.o到sum.o这里,代码出现了冗余,就是很多重复的,这样编写起来还挺麻烦的。所以我们引入了makefile的变量来进行改善。
makefile中的变量
makefile中的变量有点类似于C语言的宏定义,相当于替换一样,使用变量可以使makefile方便维护,修改起来更加简单。
makefie有三种类型的变量:
①普通变量
变量定义直接用“=”符号
使用变量值用“$(变量名)”。举个例子
hello = world //定义变量并赋值
hi = $(hello) //使用变量
上面定义了两个变量hello和hi,其中hi的值士hello值的引用。
②自带变量
除了使用用户自定义的变量,makefile中也提供了一些变量供用户直接使用,我们可以直接对其进行赋值。
CC = gcc
CPPFLAGS: C 预处理的选项 -I
CFLAGS: C编译器的选项 -Wall -g -c
LDFLAGS: 链接器选项 -L -l
③自动变量
$@:表示规则中的目标
$<:表示规则中的第一个条件
$^:表示规则中的所有条件,组成一个list,以空格隔开,如果有重复选项则消除重复项。
注意:自动变量只能再规则的命令中使用
还有一个“%”的使用,如果目标中有“%”,在依赖条件中同样可以使用“%”,依赖条件中的“%”取决于其目标:
比如第二次中有好多次的.o和.c,他们都是xx.o:xx.c,我们我们就可以用%来代替xx,这样通过%和我们的变量就形成了我们的第三次
(3)第三次
target=main
object main.o fun1.o fun2.o sum.o
CC=gcc
CPPFLAGS=-I./
$(target):$(object)
$(CC) -o $@ $^
%.o:%.c
$(CC) -o $@ -c $< $(CPPFLAGS)
这样是不是比第二次简洁多了。
下面我们引入makefile的函数来进行第四次操作。
makefile函数
makefile中函数有很多,基本只要知道最常用的就行,wildcard和patsubst是最常用的,下面对他们进行介绍。
1.wildcard —— 查找指定目录下的指定类型的文件
src=$(wildcard *c) //找到当前目录下所有后缀为.c的文件,并赋值给src
2.patsubst——匹配替换
obj=$(patsubst %.c,%.o,$(src))//把src变量里所有后缀为.c的文件变成.o的文件
通过上面两个函数,我们就出现了第四次的版本
src=$(wildcard ./*.c)
object = $(patsubst %.c,%.o,$(src))
target=main
CC=gcc
CPPFLAGS=-I./
$(target):$(object)
$(CC) -o $@ $^
%.o:%.c
$(CC) -o $@ -c $< $(CPPFLAGS)
这次可以说基本都涉及了,而且都不用怎么修改,不过还存在的一个就是,它每次生成的.o文件和目标文件在每次编译前都需要进行手动rm。下面就介绍怎么清理。
makefile的清理操作
用途:用来清理生成的.o和目标文件
使用方法:make clean,如果当前目录下有同名clean文件,则不执行clean对应的命令,解决方案:
伪目标声明:
.PHONY:clean
声明目标为伪目标之后,makefile将不会检查该目标是否存在或该目标是否需要更新。
- clean命令中的特殊符号:
- “-”此条命令出错,make也会继续执行后续的命令。如:“-rm main.o”
- rm -f:强制执行,比如若要删除的文件不存在使用-f不会报错。
- “@”不显示命令本身,只显示结果。如果“@echo clean done”
- 其它
- make 默认执行第一个出现的目标,可通过make dest指定要执行的目标
- make -f:-f执行一个makefile文件名称,使用make执行执行的makefile:make -f mainmak
通过清理就出现了第五个版本
target=main
object main.o fun1.o fun2.o sum.o
CC=gcc
CPPFLAGS=-I./
$(target):$(object)
$(CC) -o $@ $^
%.o:%.c
$(CC) -o $@ -c $< $(CPPFLAGS)
.PHONY:clean
clean:
-rm -f $(target) $(object)
这也是使用比较多的,综合了变量、函数、模式规则和清理命令。
参考资料:
某机构的学习笔记