Makefile基础总结
文章目录
前言
如果大家以前一直使用 IDE来编写 C 语言的话肯定没有听说过 Makefile 这个东西,其实这些 IDE 是有的,只不过这些 IDE对其进行了封装,提供给大家的是已经经过封装后的图形界面了,我们在 IDE 中添加要编译的C 文件,然后点击按钮就完成了编译。在 Linux 下用的最多的是 GCC 编译器,这是个没有 UI的编译器,因此 Makefile 就需要我们自己来编写了。作为一个专业的程序员,是一定要懂得Makefile 的,一是因为在 Linux 下你不得不懂 Makefile,再就是通过 Makefile 你就能了解整个工程的处理过程。
本篇只介绍Makefile的基础知识,若有不足,请见谅哈!
一、Makefile是什么?
在linux使用GCC编译器进行编译(Makefile不仅仅只是在Linux系统中使用),然而少数文件的时候可以使用gcc命令来完成编译,但当一个项目工程文件有几十,上百甚至上万的时候,用终端使用gcc命令显然是不现实的。如果能够编写一个文件,这个文件描述了编译哪些源码文件、如何编译那就好了,每次需要编译工程的时只需要使用这个文件就行了。这种问题怎么可能难倒聪明的程序员,为此提出了一个解决大工程编译的工具:make,描述哪些文件需要编译、哪些需要重新编译的文件就叫做 Makefile, Makefile 就跟脚本文件一样,Makefile 里面还可以执行系统命令。使用的时候只需要一个 make即可完成整个工程的自动编译,极大的提高了软件开发的效率。
总而言之,Makefile解决的是编译问题,而它就像是一个脚本文件,Makefile不是必须的,但它能减少你很多麻烦。
二、Makefile是如何工作的
在默认的方式下,也就是只输入 make 命令。那么,
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),并把这个文件作为最终的目标文件。
- 如果目标文件不存在,或是目标文件所依赖的文件(.o 文件)修改时间要比 这个目标文件新,那么,他就会执行后面所定义的命令来生成 目标文件。
- 如果 目标 所依赖的 文件 (.o 文件) 也不存在,那么make会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。
- make会通过C/C++文件和H文件来生成 .o 文件,然后再用 .o 文件生成make的终极任务,也就是目标文件。
这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即如果找了依赖关系之后,一些源文件不存在,则make不再进行工作。
Makefile的工作方式:
- 读入主Makefile (主Makefile中可以引用其他Makefile)
- 若有引用其他Makefile,则读取其他的Makefile
- 初始化变量
- 推导隐晦规则, 并分析所有规则
- 为所有的目标文件创建依赖关系链
- 根据依赖关系, 决定哪些目标要重新生成执行生成命令
三、Makefile的主要部分
makeflie主要有五个部分(显示规则, 隐晦规则, 变量定义, 文件指示, 注释)。
makefile的基础格式如下:
target ... : prerequisites ...
command
...
如:
main : main.o
gcc -o main main.o
main.o : main.c
gcc -c main.c
其中:
target - 目标文件, 可以是 Object File, 也可以是可执行文件
prerequisites - 生成 target 所需要的文件或者目标
command - make需要执行的命令 (任意的shell命令),
Makefile中的命令必须以 [tab] 开头
- 显示规则 : 说明如何生成一个或多个目标文件(包括 生成的文件, 文件的依赖文件, 生成的命令)
- 隐晦规则 : make的自动推导功能所执行的规则
- 变量定义 : Makefile中定义的变量
- 文件指示 : Makefile中引用其他Makefile; 指定Makefile中有效部分; 定义一个多行命令
- 注释 : Makefile行注释符为“#”
四、Makefile的基础语法
1. Makefile规则格式
Makefile 里面是由一系列的规则组成的,这些规则格式如下:
target ... : prerequisites ...
command
...
如:
main : main.o test.o
gcc -o main main.o test.o
main.o : main.c
gcc -c main.c
test.o : test.c
gcc -c test.c
如果command太长可以用 \ 进行换行
main是目标文件,main.o是main的依赖文件,如果要更新main,则必须先更新main.o,即编译时,会先执行gcc -c mian.c和gcc -c test.c 生成main.o,test.o ,再更新main
2. Makefile变量
跟 C 语言一样 Makefile 也支持变量的。
在上述的例子中,main.o test.o输入了两次,万一文件多起来,重复输入也会费时间,而且非常容易输错,为了解决这个问题,Makefile 加入了变量支持。不像 C 语言中的变量有 int、char等各种类型,Makefile 中的变量都是字符串!类似 C 语言中的宏。使用变量将上面的代码修改,修改以后如下所示:
Object = main.o test.o
main : $(Object)
gcc -o main $(Object)
main.o : main.c
gcc -c main.c
test.o : test.c
gcc -c test.c
Makefile使用的符号如下:
= 相当于引用,如a=b,b的值变,a也变
:= 直接赋值,如a:=b,b变,a不变
?= 如果不存在则进行赋值,存在不赋值,如a?=b a?=c,则a为b
+= 追加符,如a+=b a+=c,则a = b c
注:变量不一定在Makefile中定义,也可通过 make 时定义,如make a=b
3. Makefile 模式规则
上述 Makefile 例子中是将对应的.c 源文件编译为.o 文件,每一个 C 文件都要写一个对应的规则,如果工程中 C 文件很多的话显然不能这么做。为此,可以使用 Makefile 中的模式规则,通过模式规则就可以使用一条规则来将所有的.c 文件编译为对应的.o 文件。模式规则中,至少在规则的目标定定义中要包涵“%”,否则就是一般规则,目标中的“%”表示对文件名的匹配,“%”表示长度任意的非空字符串,类似与通配符,a.%.c 就表示以 a.开头,以.c 结束的所有文件。当“%”出现在目标中的时候,目标中“%”所代表的值决定了依赖中的“%”值,使用方法如下:
%.o : %.c
命令
如:
Object = main.o test.o
main : $(Object)
gcc -o main $(Object)
%.o : %.c
gcc -c %.c
Makefile的通配符还有:
1. * 表示任意一个或多个字符
2. ? 表示任意一个字符
3. [...] 如 [abcd] 表示a,b,c,d中任意一个字符
4. ~ 表示用户的home目录
4. Makefile 自动化变量
上面讲的模式规则中,目标和依赖都是一系列的文件,每一次对模式规则进行解析的时候都会是不同的目标和依赖文件,而命令只有一行,如何通过一行命令来从不同的依赖文件中生成对应的目标?自动化变量就是完成这个功能的!所谓自动化变量就是这种变量会把模式中所定义的一系列的文件自动的挨个取出,直至所有的符合模式的文件都取完,自动化变量只应该出现在规则的命令中,常用的自动化变量如下:
上诉的7个变量中,常用的有三种:$@,$< 和 $^,结合上面例子,有:
Object = main.o test.o
main : $(Object)
gcc -o main $(Object)
%.o : %.c
gcc -c $<
5. Makefile 伪目标
Makefile 有一种特殊的目标(伪目标),一般的目标名都是要生成的文件,而伪目标不代表真正的目标名,在执行 make 命令的时候通过指定这个伪目标来执行其所在规则的定义的命令。而声明伪目标的命令为 .PHONY,常用的是 clean,make clean 只是执行了Makefile的伪目标,如:
Object = main.o test.o
main : $(Object)
gcc -o main $(Object)
%.o : %.c
gcc -c $<
.PHONY : clean
clean:
rm -rf *.o
6. Makefile条件判断
Makefile也支持语法判断,格式有两种(类似于if—else):
<条件关键字>
<条件为真时执行的语句>
endif
以及
<条件关键字>
<条件为真时执行的语句>
else
<条件为假时执行的语句>
endif
其中条件关键字有 4 个:ifeq、ifneq、ifdef 和 ifndef,这四个关键字其实分为两对
ifeq 与ifneq:判断是否相等 或不相等 两个参数
ifdef 与 ifndef:判断是否定义 或 未定义 一个参数
7. Makefile函数
Makefile 支持函数,类似 C 语言一样,Makefile 中的函数是已经定义好的,可直接使用,不支持自定义函数,函数有:
- 函数 subst $(subst <from>,<to>,<text>) ,用来替换字符
- 函数 patsubs $(patsubst <pattern>,<replacement>,<text>),用来完成模式字符串替换:
$(patsubst %.c,%.o,a.c b.c c.c) 将字符串“a.c b.c c.c”中的所有符合“%.c”的字符串,替换为“%.o”,替换完成以后的字符串为“a.o b.o c.o” - 函数 dir $(dir <names…>) 用来获取目录
- 函数 notdir $(notdir <names…>) 是提取文件名
- 函数 foreach $(foreach , ,
) 用来完成循环 - 函数 wildcard $(wildcard PATTERN…)
通配符“%”只能用在规则中,只有在规则中它才会展开,如果在变量定义和函数使用时,通配符不会自动展开,这个时候就要用到函数 wildcard 如 $(wildcard *.c) 获取所有的.c文件
总结
本篇只是对 Makefile 做了最基本的讲解,确保大家能够了解及读懂一般Makefile,Makefile 还有大量的知识没有提到,有兴趣的可以参考其他文档进行深入学习