makefile的编写
什么是make?
利用make,一步就可以完成大量的源文件编译和链接,从而不必再一条条输入gcc命令。
什么是makefile?
makefile是一个文本文件,里面记录了哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。makefile的好处是“自动编译”,只需要一个make命令,整个工程自动编译,极大提高了效率。
编写一个makefile需要提前知道:1.编译、链接的概念; 2.gcc、g++的相关命令参数 -g -o -c
编译、链接的概念
编译:对于C、C++,源文件需要编译成object file 目标文件,unix下会生成.o文件,这个动作叫compile 编译。一般来说,每一个源文件都对应着一个中间目标文件,.o文件或者.OBJ文件。
编译时,编译器需要的是语法正确,函数、变量声明的正确。对于后者,你需要告诉编译器头文件的位置(头文件中应只是声明而不是定义,定义应放在c\c++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。
链接:将大量的object file合成为一个可执行文件的动作叫作link 链接。
链接时,主要是链接函数和全局变量。所以我们可以通过.o /.obj文件来链接我们的应用程序。链接不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数的时候,由于函数的Object File太多,而在链接时需要明确指出中间目标文件名,这很不方便。所以我们给这些Object Files打成包,Windows下叫作链接库文件(Library File),也就是.lib文件。在Linux下就是Archive File,也就是.a文件。
gcc的相关选项 -g -o -c
目标 : 需要的条件 #注意冒号两边的空格
命令 #注意前头用TAB缩进
- 目标可以是一个或者多个Object File,也可以是可执行文件
- 需要的条件就是生成目标所需要的文件或者目标
- 命令就是生成目标所需要执行的脚本
一句话就是,makefile的规则规定的编译的依赖关系,目标文件依赖于条件,生成规则用命令来描述。在编译时,如果需要的条件比目标新,那么就会执行生成命令来更新目标。
举例说明:假设我们写下如下的三个文件,add.h 用于声明 add 函数,add.c 提供两个整数相加的函数体,而 main.c中调用 add 函数:
/* filename:add.h */ extern int add(int i, int j); /* filename:add.c */ int add(int i, int j) { return i + j; } /* filename:main.c */ #include "add.h" main() { int a, b; a = 2; b = 3; printf("the sum of a+b is %d", add(a + b)); }
现在,将上述三个文件写入makefile:
test : main.o add.o gcc main.o add.o -o test main.o : main.c add.h gcc -c main.c \ //这里的'\'只是换行,命令还是gcc -c main.c -o main.o
-o main.o add.o : add.c add.h gcc -c add.c -o add.o
上述 makefile 利用 add.c 和 add.h 文件执行 gcc -c add.c -o add.o 命令产生 add.o 目标代码,利用main.c 和 add.h 文件执行 gcc -c main.c -o main.o 命令产生 main.o 目标代码,最后利用 main.o 和 add.o文件(两个模块的目标代码)执行 gcc main.o add.o -o test 命令产生可执行文件 test。
clean:
rm *.o
要是觉得除了目标文件test外,生成了过多的中间目标文件,那么在make编译后,执行make clean可以删除所有中间目标文件,需要注意的是,执行make clean后,当前目录下的所有*.o文件都被删除了,包括不在makefile中出现过的.o文件。同时,clean不会自动执行,因为clean后面没有条件,clean也不是一个目标文件,它不过是一个动作,自然就不会自动执行所定义的命令。
make是如何工作的?
在默认情况,也就是我们只输入make命令的情况下,有:
- make在当前目录下找到makefile文件或Makefile文件
- 如果找到,它会找第一个目标,在上述例子中即是test,并把test作为最终的目标文件
- 如果test不存在或者test的条件文件比test的要更新,那么将执行后面的命令来生成test文件
- 如果test所依赖的某个.o文件不存在,则make将查找该.o文件的依赖,找到后再用相关的规则生产该.o文件
- .c / .h文件必须存在,由make将它们编译生成.o文件,然后再用.o文件生成最终的目标文件test
makefile中使用变量
我们可在 makefile 中加入变量,另外。环境变量在 make 过程中也被解释成 make 的变量。这些变量
是大小写敏感的,一般使用大写字母。Make 变量可以做很多事情,例如:
- 存储一个文件名列表
- 存储一个可执行文件名
- 存储编译器选项。
可能这么说很迷糊,举个例子,如果有个makefile的代码如下:
test : main.o kbd.o command.o display.o / insert.o search.o files.o cc -o edit main.o kbd.o command.o display.o / //cc是gcc的软连接 insert.o search.o files.o
条件文件有八个中间目标文件,而且7个.o文件的字符串被重复了两次,如果你要再增加一个.o文件作为test的依赖,那么就可能会发现遗漏。所以为了方便makefile的维护,我们用一个变量代表字符串,也可以理解为宏。
于是可以将增加一个utils.o文件的情况改写成下列代码:
OBJS = main.o kbd.o command.o display.o / insert.o search.o files.o utils.o //增加utils.o
CC = gcc
CFLAGS = -Wall -g //-c比较特殊,一般不把它加入变量;注意这里不加 - o ,因为这个选项后面必须要加可执行文件名
test : $(OBJS) //变量前加一个说明符$ $(CC) $(OBJS) -o test main.o : main.c defs.h $(CC) $(CFLAGS) -c main.c kbd.o : kbd.c defs.h command.h $(CC) $(CFLAGS) -c kbd.c command.o : command.c defs.h command.h $(CC) $(CFLAGS) -c command.c display.o : display.c defs.h buffer.h $(CC) $(CFLAGS) -c display.c insert.o : insert.c defs.h buffer.h $(CC) $(CFLAGS) -c insert.c search.o : search.c defs.h buffer.h $(CC) $(CFLAGS) -c search.c files.o : files.c defs.h buffer.h command.h $(CC) $(CFLAGS) -c files.c utils.o : utils.c defs.h //增加生成utils.o的规则 $(CC) $(CFLAGS) -c utils.c clean : rm $(objects)
只修改了一处,十分的方便
让make自动推导
make很强大,它可以自动推到依赖的文件和依赖文件后面的命令,于是我们就没必要在每个.o文件后都写上类似的命令,而是让make自动识别,自动推导。
比如,make发现一个.o文件,它就会自动地把.c文件加在依赖关系中,比如说make找到一个main.o,那么就推导出main.c是main.o的依赖文件。并且gcc -c main.c 也会被推导出来。
通过make的这个特性,我们简化上面一个例子的makefile:
objects = main.o kbd.o command.o display.o / insert.o search.o files.o utils.o test : $(objects) gcc -o test $(objects) main.o : defs.h kbd.o : defs.h command.h command.o : defs.h command.h display.o : defs.h buffer.h insert.o : defs.h buffer.h search.o : defs.h buffer.h files.o : defs.h buffer.h command.h utils.o : defs.h clean : rm $(objects)
MakeFile学习之路还很长,博文部分内容借鉴大牛陈浩,他的makefile专栏http://blog.csdn.net/haoel/article/details/2887