Makefile介绍
Makefile是一种强大的工程管理编译工具,对于一个大型的工程来说,其按照类型、功能、模块等放在不同的路径下,makefile定义了一些规则来指定哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于更复杂的操作,其也可以执行操作系统的命令。
Makefile规则
用一句话来说,其实就是:
目标文件:依赖文件
TAB键 命令
运行规则为:当我的依赖比我的目标新或者目标不存在的时候,就执行这个命令。否则不执行这个命令。
示例
假如说我现在有两个文件,分别为a.c和b.c,程序如下:
a.c程序:
#include<stdio.h>
int main()
{
func_b();
return 0;
}
b.c程序:
#include<stdio.h>
void func_b(void)
{
printf("This is a Test Demo -- File B.\n");
}
我们可以使用gcc对这个小Demo进行编译,假如我们生成的可执行文件为test,则需要经历以下几个步骤:
1.gcc -c -o a.o a.c
2.gcc -c -o b.o b.c
3.gcc -o test a.o b.o
设想一下,我们的程序可能不止两个源文件,可能成百上千个,我们每一次编译生成的可执行文件也不一定都是正确的结果,每次生成可执行文件都需要至少三个步骤,可见这是多么低下的效率。这时候Makefile就可以派上用场了。我们可以将这些编译规则写入一个名为Makefile的文件中,写法如下:
test:a.o b.o //test的生成需要依赖于a.o和b.o文件
gcc -o test a.o b.o //生成test的命令
a.o:a.c //a.o的生成需要依赖于a.c文件
gcc -c -o a.o a.c
b.o:b.c //b.o的生成需要依赖于b.c文件
gcc -c -o b.o b.c
改进
写完之后我们发现,对于当前的这个程序,我们确实可以用这个Makefile进行编译,但如果我现在需要增加c.c文件呢?我是不是需要在Makefile里边对新的文件添加编译规则了;如果我增加了许多源文件呢?显然这种方式并不是最好的方式,通用性还是不够,接下来我们需要改进它。使它的通用性很强,这样Makefile编写一次就不再需要大改,而是小修小补。
先贴出改进的Makefile,如下所示:
test:a.o b.o c.o
gcc -o test $^
%.o:%.c
gcc -c -o $@ $<
我的c.c如下:
#include<stdio.h>
void func_c(void)
{
printf("This is a Test Demo -- File C.\n");
}
a.c此时修改如下:
#include<stdio.h>
int main()
{
func_b();
func_c();
return 0;
}
其中:
$^
表示所有的依赖文件$@
表示目标文件$<
表示第一个依赖
这样就是一个通用性比较强的Makefile了,如果我增加文件,只需写出文件编译过后的.o文件作为test的依赖即可。但这种改进的Makefile并不是最简洁的形式,但对于新人理解使用Makefile已经足够。
我在使用Makefile过程中,除了最终的可执行文件,还会生成许多的中间文件,所以我们可以使用一些清除操作,在Makefile 文件中编写如下代码:
test:a.o b.o c.o
gcc -o test $^
%.o:%.c
gcc -c -o $@ $<
clean:
rm *.o test
.PHONY: clean
rm
是linux 的一个系统操作指令,意思是执行删除文件、文件夹等操作。*.o
代表所有的以.o为结尾的文件。为什么要写.PHONY
,它的意思是.PHONY后边为一个假想的目标,因为我们在clean这些文件的时候需要执行make clean
命令,如果不写这句话,有时候会出问题,比如这时候我文件名中刚好有一个叫clean名字的文件,那么执行这句话就会有问题,其会编译clean文件而不是清除中间文件和test。
Makefile其他常用语法
即时变量、延时变量
:=
即时变量,例如:A:= xxx ,代表A的值可以立即确定,在A定义的时候就确定。=
延时变量,例如:B=xxx,代表B的值在其使用到的时候才确定。?=
延时变量,若是第一次定义才有效,若在之前定义,则无效(即代表忽略此句)。+=
附加,它是即时变量还是延时变量取决于之前的语句。
Makefile函数
$(wildcard pattern)
#意思是在pattern中定义你需要寻找的文件格式,wildcard取出当前路径中存在的 相关文件。
例如:$(wildcard *.c)
– 取出当前路径下.c为后缀名的文件,并用分号隔开。$(subst FROM,TO,TEXT)
#意思是将TEXT中的东西从FROM变成TO,FROM为我要替换的文件或形式,TO为我要替换成的形式。
例如:$(subst a,the,There is a cat)
– 将There is a cat中的a替换成the。$(foreach var,list,text)
、$(filter pattern...,text)
#意思是从text中取出适合pattern格式的值。
例如:
A = a b c d/
B = $(filter %/, $(A)) #可以从A的a b c d/中取出d/
filter的另一种函数形式为:
$(filter-out pattern..., text)
#意思是从text中取出不符合pattern类型的值。例如上边的语句改为:
B = $(filter-out %/, $(A))
则代表取出不符合%/
格式的数据,结果为a b c
更多参考
就写到这里吧,这只是一个关于Makefile的简单实用的例子,其中Makefile的规则和语法还有很多,感兴趣的小伙伴可以去GNU make的官网下载阅读,我也把链接粘贴在这里,希望能跟各位小伙伴多多交流。
链接:GNU Make PDF下载