文章目录
一、Makefile的引入
Makefile的介绍:Makefile是一个编译辅助器,并不是编译器。它只是提供辅助我们进行编译的一个工具。就像一个Shell脚本一样来通过执行一些命令,来实现某些操作。
Makefile的引入:在Linux没有像我们的Windows上比如VS,CLion,等这些方便的图形化编译软件,那我们怎么编译呢?在Linux上我们通过命令行
gcc -o xxx xxx.c xxx.c
来对文件进行编译,但是我们在这个过程中发现,如果文件很多,而且我们还需要多次编译来进行调试,那岂不是要敲这样的指令很多次,岂不是麻烦的要死。正因如此,Makefile就诞生了。
(见下展示,实际调试)
Makefile的功能:给大家一个背景(在有成千上完个文件需要我们进行编译,在你没有接触Makefile之前,那就通过gcc -o xxx xxx这样把所有的编译一下,但是突然你发现,我有个文件的某个地方有个小错误,难道我就用这个值再来一遍?是不是显得太过于麻烦了!!!)。因此为了方便我们进行文件的编译。
展示
这是两个非常简单的代码a.c b.c
a.c
#include<stdio.h>
int main(){
fun();
return 0;
}
b.c
#include<stdio.h>
void fun(){
printf("This is B!\n");
return;
}
编译结果
结果分析
上面的警告可以不用管,这个指令的过程是编译a.c 和b.c生成 一个build的可执行文件。a调用了b的函数,所以生成了这个build实际上是在编译a.c 和 b.c的时候,生成了a.o和b.o最后通过链接才生成了可执行文件build。可以通过指令(对这个过程不懂的,推荐去看一下(一))
二、Makefile的使用
相信通过上面的介绍,你对Makefile也有了一个清晰的认识,至少当别人问起你的时候,你能清楚的向他人介绍起Makefile。那么下面我们就开始对Makefile的使用进行分析
1.Makefile的规则
Makefile的规则可以归纳为如下
一个目标文件可以有多个依赖
比如我们上一个例子我们可以进行细分
目标文件build: 通过
gcc -o build a.o b.o
链接生成
而依赖文件a.o通过
gcc -c -o a.o a.c
编译生成
依赖文件b.o通过
gcc -c -o b.o b.c
编译生成
因此我们可以简单的写出一个Makefile如下
build:a.o b.o
gcc -o build a.o b.o
a.o:a.c
gcc -c -o a.o a.c
b.o:b.c
gcc -c -o b.o b.c
在Linux上运行一下
可以看出,通过编写这样的一个Makefile文件就能达到我们的效果,但是我再make一下,系统就提示,build已经是最新的了,所以不需要再编译,就用原来那个就行。(这就是Makefile的原理)
Makefile的原理:通过比较目标文件和依赖文件的时间,如果目标文件不存在,表明需要对文件进行编译,生成新的文件。如果依赖文件进行了修改,表明需要重新编译依赖文件。(比如a.c进行了修改,那么通过比较a.o和a.c的最后修改时间发现,a.c比a.o新,所以我们就需要重新编译一下a.c。自然a.o更新了,build同理也要更新。但是b.c,b.o从始至终都没有更新过,所以他们两个自然就不需要进行再次编译)这就是Makefile的工作原理
2.Makefile的语法
对于嵌入式应用开发来说,我们只需要掌握基本的Makefile操作就行,下面我们就对Makefile的基本语法进行分析。
2.1 通配符 %
对比上面的Makefile,我们可以进行如下的修改,同时也添加了c.c
c.c
#include <stdio.h>
void fun_c(){
printf("This is C!\n");
return;
}
Makefile
build:a.o b.o c.o
gcc -o build $^
%.o:%.c
gcc -c -o $@ $<
运行结果:
分析:
当我们需要生成目标文件build的时候,需要依赖文件a.o,b.o,c.o。因此程序会自动地向下去找,发现了
这样地一个规则可以来生成我们的a.o ,b.o c.o。
%:表示通配符
$@:表示目标文件
$<:表示第一个依赖文件(从左到右)
$^:表示所有的依赖文件
2.2 假想目标 .PHONY
为什么需要假想目标呢?
比如我们需要删除期间生成的a.o,b.o,c.o。那我们就只需要改一下Make file如下
build:a.o b.o c.o
gcc -o build $^
%.o:%.c
gcc -c -o $@ $<
clean:
rm *.o build
然后在终端上执行如下
所以我们就成功删除了不想要的文件。
为什么要加clean呢?
因为如果你执行make,他会默认执行生成你的第一个目标文件,加一个clean。就表示你要生成的目标文件(如果文件不存在就执行下面的代码——Makefile的规则嘛)。
但是,此时你想如果文件内已经有这个clean文件了怎么办?他还会执行吗?如下
因为系统发现,clean文件已经是最新的了,所以就不会执行后面的指令。因此,我们就无法删除那些我们不需要的文件。
为了解决这样的一个文件,我们就需要使用假想目标,只需要更改一下代码,如下
build:a.o b.o c.o
gcc -o build $^
%.o:%.c
gcc -c -o $@ $<
clean:
rm *.o build
.PHONY:clean
结果
所以即使有这个文件也不影响我们执行clean下面的指令。(系统会假想没有这个文件)
2.3 变量
A:=xxxx (表示的是这是一个即时变量,就是说这个值已经确定)
B = xxx (表示的是这是一个延时变量,只会在你使用到他的时候,他的值才确定 验证如是实验一)
?= (表示延时变量,区别是只会在第一次定义时才会起作用,如果前已经定义了,就忽略掉)
+= (表示附加,类型取决于前面是怎么定义的,验证如实验二)
实验一
编写Makefile如下
A :=$(C)
B = $(C)
C=123
all:
@echo A=$(A)
@echo B=$(B)
运行结果:
分析:因为最开始的时候,C的值为NULL,因此A此时赋值的结果就是NULL。而B是使用的时候,会去在程序中找,C的值,因此B的值就是123
实验二
更改Makefile如下
A :=$(C)
B = $(C)
C=123
all:
@echo A=$(A)
@echo B=$(B)
B+=456
B?=789
运行结果:
分析:如果你把B?=789放在声明B的前面且后面不对B进行赋值(因为你再赋值就会覆盖掉前面的值),那么结果就如下·所示
3.Makefile函数
对于Makefile函数下面主要介绍4个函数。对于函数不熟悉,建议参考文档
http://www.gnu.org/software/make/manual/
3.1 $foreach()函数
$(foreach f , $(A), $(f) . o)
这个函数的功能:就是将变量A中的值执行函数f(即替换为.o)
3.2 $filter()函数
$ (filter %/, $©)
功能:在变量C中查找%/格式的元素
3.3 $filter-out()函数
(
f
i
l
t
e
r
−
o
u
t
(filter-out %/,
(filter−out©)
功能:在变量C中查找不符合%/格式的元素
3.4 $wildcard()函数
$(wildcard *.c)
功能:查找当前目录中真实存在的文件
3.5 $patsubst()函数
$ (patsubst %.c ,%.d, $(file2))
功能:将变量file2中%.c格式的元素 替换为 %.d格式
Makefile代码:
A :=a b c
B = $(foreach f ,$(A),$(f).0)
C=a b c d/
D=$(filter %/,$(C))
E=$(filter-out %/,$(C))
files=$(wildcard *.c)
file2=a.c b.c c.c d.c e.c abc
file3=$(wildcard &(file2))
dep_file=$(patsubst %.c,%.d,$(file2))
all:
@echo B=$(B)
@echo D=$(D)
@echo E=$(E)
@echo files=$(files)
@echo file3=$(file3)
@echo dep_file=$(dep_file)
总结
希望上面的介绍和实验对你了解熟悉Makefile能有帮助!