Makefile核心思想
在编译源码的时候,总会遇到这样的情况:
1、需要运行一串命令,把一个或多个源文件转换成一个目标文件,但这些源文件也可能是通过其他源文件生成的。
2、当部分文件发生改变时,只需要运行必要(最少)的命令,生成最新的目标文件。
例如(Masm汇编举例),我们需要编译一个hello.exe文件,先将asm源码编译成obj文件,然后通过link将多个obj文件链接成exe文件。
masm main.asm,main.obj;(编译main.asm,生成main.obj文件)
masm fun.asm,fun.obj;(编译fun.asm,生成fun.obj文件)
link main.obj fun.obj,hello.exe;(链接main.obj和fun.obj,生成hello.exe文件)
编译多个源码文件,并将这些文件最终编译成需要的库文件或可执行文件将是一件重复且无聊的工作。如果没有其他工具辅助,你需要编写长长的命令(当然,这可以通过脚本进行简化),并且你还需要记住哪些源码修改过,否则你需要重新编译并未修改过的源码,费时且做无用功。
Make工具配合makefile文件,可以大大简化这项工作(只需要运行make即可),并且帮助你判断哪些源码需要重新编译。make工具通过载入makefile文件,按照makefile中描述的规则,将需要生成的目标文件和生成目标文件需要的源文件对应起来,并且指定通过源文件生成目标文件的命令,makefile核心语法如下:
target : source ...
command
...
...
上面的语法代表一条依赖规则,target为目标文件,source为源文件或依赖文件(可以多个,中间空格分隔),command为把source文件生成target文件的命令(可多个命令)。
核心思想就是:通过检查目标文件和源文件的修改时间来决定是否执行command,如果源文件(任何一个)比目标的修改时间更接近当前时间,就需要执行对应的command命令。
注意make并不是按照makefile中所有规则的书写先后顺序执行,而是按照依赖关系树递归执行(从根部开始执行),因为当make检查源文件的修改时间时,会存在2种情况:
1、源文件也存在依赖规则,即源文件也是某条依赖规则的目标文件,那么make程序会检查该文件的依赖关系,并以此类推,直至源文件不存在依赖关系为止,并且所有的源文件都要递归检查。相当于创建一个依赖关系树,makefile种的第一条规则是为了生成最终的目标文件,后续的规则是对最终稿目标文件的源文件的依赖关系进行补充完善。
2、源文件不存在依赖规则,则直接检查该文件是否存在,如不存在则报错。
之前的例子可以这样编写makefile文件(可以看到依赖关系是树状结构,先写最终目标,然后写分支依赖):
hello.exe : maim.obj fun.obj
link maim.obj fun.obj,hello.exe;
main.obj : main.asm
masm main.asm,main.obj;
fun.obj : fun.asm
masm fun.asm,fun.obj;
执行顺序如下,makefile的最终目标是hello.exe,它依赖maim.obj和fun.obj;maim.obj也存在依赖,其依赖main.asm,main.asm无任何依赖,则检查main.obj和main.asm的修改时间,决定是否执行command(masm main.asm,main.obj;)。fun.obj也按此流程进行递归检查。随后检查hello.exe与maim.obj和fun.obj的修改时间,决定是否执行command(link maim.obj fun.obj,hello.exe;)。如按照makefile中的书写的规则的先后顺序执行,第一次编译后又修改fun.asm源码,如果按书写的顺序执行,第二次执行make命令,第一条依赖规则就无需执行,maim.obj和fun.obj都存在且hello.exe的时间比它两都更新,第二条相同。只有第三条依赖规则需要执行(因fun.asm修改过)。但生成新的fun.obj文件后,本应执行第一条依赖规则,这种情况就无法执行,除非第三次执行make程序。
我接触的make程序主要有Microsoft系和Linux系两大阵营,主要思想差不多,区别在高级功能(如宏、变量等)。Microsoft的nmake和Linux(Linux的make和Borland的make)核心思想都一致,但Microsoft在dos时代的编译器(Masm5.0及之前,Microsoft C 5.0之前)的make程序却有所不同,其并不生成依赖树,而是简单的按makefile的书写顺序执行,则上述例子的makefile文件需修改如下:
main.obj : main.asm
masm main.asm,main.obj;
fun.obj : fun.asm
masm fun.asm,fun.obj;
hello.exe : maim.obj fun.obj #soruce文件之间也可用逗号分割
link maim.obj fun.obj,hello.exe;
Makefile的高级用法,请参考陈皓的《跟我一起写 Makefile》吧。