Make是一个很通用的用于编译我们程序的工具,否则对于较大的工程,进行编译可能就是一件很麻烦的事情。今天对makefile进行了一点自己的总结,肯定不是那种从原理上能把makefile讲得面面俱到,很详细的那种,但是应该比较实用,希望对初学者能够有所帮助。想要有深入的了解,网上也有很多详细的资料。
说Makefile,首先说说一说编译与链接。
编译:一般来说,无论是C、C++首先要把源文件编译成中间代码文件,在Windows下也就是.obj文件,UNIX下是.o 文件,即 Object File,这个动作叫做编译(compile)。编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接:把大量的Object File合成执行文件,这个动作叫作链接(link)。链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。文件打个包,在Windows下这种链接器并不管函数所在的源文件,只管函数的中间目标文件(ObjectFile),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标包叫“库文件”(LibraryFile),也就是.lib 文件,在UNIX下,是Archive File,也就是.a 文件。
编译&链接:源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的ObjectFile中找寻函数的实现,如果找不到,那到就会报链接错误码(LinkerError),在VC下,这种错误一般是:Link2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的ObjectFile.
make命令执行时,需要一个Makefile文件,以告诉make命令需要怎么样的去编译和链接程序。
满足下列条件Makefile将会被执行:
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3 )如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的 C 文件,并链接目标程序Makefile的规则:
target... : prerequisites ...
command
...
...
target就是一个目标文件,可以是ObjectFile,也可以是执行文件。还可以是一个标签(Label),例如:clean、install。
prerequisites就是要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)需要一个TAB键
举个例子:
main:main.o object1.o object2.o
gcc -o main main.o object1.o object2.o
main.o:main.c object1.h object2.h
gcc -c main.c
object1.o:object1.c object1.h
gcc -c object1.c
object2.o:object2.c object2.h
gcc -c object2.c
有用的变量:
$@--目标文件;
$^--所有的依赖文件;
$<--第一个依赖的条件。
上面的程序可以简化为:
main:main.o object1.o object2.o
gcc -o $@ $^
main.o:main.c object1.h object2.h
gcc -c $<
object1.o:object1.c object1.h
gcc -c $<
object2.o:object2.c object2.h
gcc -c $<
.c.o
被定义为老式的"双后缀规则",在一些大型程序中是很常见的。 双后缀规则定义了一对后缀:目标文件的后缀和依赖目标(源文件)的后缀。如 ". c.o " 相当于 "%o : %c" 。 后缀规则中所定义的后缀应该是 make 所认识的,例如: ".c" 和 ".o" 都是 make 所知道。因而,如果你定义了一个规则是 ". c.o " 那么其就是双后缀规则,意义就是 ".c" 是源文件的后缀, ".o" 是目标文件的后缀。后缀规则不允许任何的依赖文件,如果有依赖文件的话,那就不是后缀规则,那些后缀统统被认为是文件名要让make知道一些特定的后缀,我们可以使用伪目标“.SUFFIXES”来定义或是删除:.SUFFIXES:.c .o .h # 定义自己的后缀
这样我们前面的程序可以进一步简化:
main:main.o object1.o object2.o
gcc -o $@ $^
.c.o:
gcc -c $<
也可以表示为:
.PHONY:clean
其中.PHONY表示clean是一个“为目标”。
还要注意以下几点:
1.Make只管文件的依赖关系,至于命令中的编译错误,make不管。依赖的文件找不到,make会直接退出
2.为了Makefile的易维护性,在Makefile中我们可以使用变量,makefile中的变量也就是一个字符串,理解成C语言中的宏更好
3.如果make找到一个whatever.o那么whatever.c就会是whatever.o的依赖文件。
4. 通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“@”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。
当make执行时,会输出“正在编译XXX模块 ......”字串,但不会输出命令,如果没有“@”,那么 ,make 将输出:
echo 正在编译XXX模块......
正在编译XXX模块......
6. make 参数“ -s” 或“ -- slient ” 则是全面禁止命令的显示。