什么是makefile?或许很多Windows的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个功作,但是作为一个好的和专业的程序员,makefile还是要懂的。这就好像现在有许多的所见即所得的网页制作工具,但如果你想成为一个专业网页开发人员,你还是要了解HTML的标识的含义的,当然还有其它的比如CSS,JavaScript等等等等。
makefile关系到整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——自动化编译,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
不同产商的make各不相同,也有不同的语法,但其本质都是在“文件依赖性”上做文章。
关于程序的编译和连接
在此,先说说关于程序编译的一些规范和方法,一般来说,无论是C、C++,还是pas,首先要把源文件编译成中间的代码文件,在Windows下也就是.obj文件,UNIX下是.o文件,即Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫做链接(link)。
编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数的时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是.lib文件,在UNIX下,是Archive File,也就是.a文件。
总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。在VC下,这种错误一般是:Link 2001错误,意思就是说,链接器未能找到函数的实现。你需要指定函数的Object File。
言归正传。
Makefile介绍
make命令执行时,需要一个Makefile文件,以告诉make命令要怎么样的去编译和链接程序。
Makefile规则:
规则包含两个部分,一个是依赖关系,一个是生成目标的方法。
target … : prerequisites…
command
…
…
目标:先决条件
命令
target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。
prerequisites 就是,要生成那个target所需要的文件或目标。
command 也就是make需要执行的命令。(任意的Shell命令)
这是一个文件的依赖文件,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
在Makefile中,规则的顺序是很重要的,因为,Makefile中只应该有一个最终目标,其它的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么。一般来说,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终的目标。如果第一条规则中的目标有很多个,那么,第一个目标会成为最终的目标。make所完成的也就是这个目标。
一个示例:一个工程只包含两个文件,一个汇编源码文件*.asm,一个资源文件.rc。注:这是我学习Win32汇编程序如果定义菜单的例子。
NAME = Menu
OBJS = $(NAME).obj
RES = $(NAME).res
LINK_FLAG = /subsystem:windows
ML_FLAG = /c /coff
$(NAME).exe: $(OBJS) $(RES)
Link $(LINK_FLAG) $(OBJS) $(RES)
.asm.obj:
ml $(ML_FLAG) $<
.rc.res:
rc $<
clean:
del *.obj
del *.res
make是如何工作的
在默认的方式下,也就是我们只键入make命令。那么,
1,make会在当前目录下找名称为“Makefile”或“makefile”的文件。
2,如果找到,它会找文件中的每一个目标文件(target),并把这个文件作为最终的目标文件。比如是XXX,一般它是一个可执行文件。
3,如果这个XXX文件不存在,或是XXX所依赖的后面的文件YYY的修改时间要比XXX这个文件新,那么,他就会执行后面所定义的命令来生成XXX这个文件。
4,如果XXX所依赖的YYY文件存在,那么make会在当前文件中找目标为YYY文件的依赖性,如果找到则再根据那一个规则生成YYY文件。
5,当然,你的C文件和H文件都是存在的,于是make会生成YYY文件,然后再用YYY文件生成make的终极任务,也就是执行文件XXX了。
这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。make只管文件的依赖性,即,如果在我找到了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。
通过上述分析,我们知道,像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显式让make执行。即命令——“make clean”,以此来清除所有的目标文件,以便重编译。
于是在我们编程中,如果这个工程已经被编译过了,当我们修改了其中一个源文件,比如file.c,那么根据我们的依赖性,我们的目标file.obj会被重编译(也就是在这个依赖性关系后面所定义的命令),于是file.obj的文件也是最新的啦,于file.obj的文件修改时间要比最终的可执行文件XXX要新,所以XXX也会被重新链接了。
makefile中使用变量
为了makefile的易维护,在makefile中我们可以使用变量。
makefile的变量也就是一个字符串,理解成C语言的宏可能会更好一些。
比如,我们声明一个变量,叫OBJS.
在makefile一开始就这样定义:
OBJS = 1.obj 2.obj 3.obj
于是,我们就可以很方便地在我们的makefile中以“$(OBJS)”的方式来使用这个变量了。
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。
1、显式规则。显式规则说明了,如果生成一个或多个目标文件。这是由Makefile的书写者明显指出,要生的文件,文件的依赖文件,生成的命令。
2、隐晦规则。由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile,这是由make所支持的。
3、变量的定义。在Makefile中我们要定义一系列的变量,变量一般都是字符串,这个有点像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。
4、文件指示。其包括了三个部分,一个是在一个Makefile中引用另一个Makefile,就像C语言中的include一样;另一个是指根据某些尾部指定Makefile中的有效部分,就像C语言中的预编译#if一样;还有就是定义一个多行的命令。
5、注释。Makefile中只有行注释,和UNIX的Shell脚本一样,其注释是用“#”字符,这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符,可以用反斜杠进行转义,如:“\#”。
先就整理到这里,不敢再往下看了,内容太多了,先有个概念,以后遇到问题再看。