当在 shell 提示符下输入“make”命令以后。make读取当前目录下的 Makefile 文件,并将 Makefile 文件中的第一个目标作为其执行的“终极目标” ,开始处理第一个规则(终极目标所在的规则) 。在我们的例子中,第一个规则就是目标“edit”所在的规则。规则描述了“edit”的依赖关系,并定义了链接.o文件生成目标“edit”的命令; make在执行这个规则所定义的命令之前,首先处理目标“edit”的所有的依赖文件(例子中的那些.o文件)的更新规则(以这些.o文件为目标的规则)。对这些.o文件为目标的规则处理有下列三种情况:
1. 目标.o文件不存在,使用其描述规则创建它;
2. 目标.o文件存在,目标.o文件所依赖的.c源文件、 .h文件中的任何一个比目标.o文件“更新”(在上一次 make之后被修改)。则根据规则重新编译生成它;
3. 目标.o文件存在,目标.o文件比它的任何一个依赖文件(的.c源文件、.h文件)“更新”(它的依赖文件在上一次 make之后没有被修改),则什么也不做。 这些.o 文件所在的规则之所以会被执行,是因为这些.o 文件出现在“终极目标”的依赖列表中。在 Makefile 中一个规则的目标如果不是“终极目标”所依赖的(或者“终极目标”的依赖文件所依赖的) ,那么这个规则将不会被执行,除非明确指定执行这个规则(可以通过make的命令行指定重建目标,那么这个目标所在的规则就会被执行,例如 “make clean”) 。在编译或者重新编译生成一个.o文件时,make同样会去寻找它的依赖文件的重建规则(是这样一个规则:这个依赖文件在规则中作为目标出现),在这里就是.c和.h文件的重建规则。在上例的Makefile中没有哪个规则的目标是.c或者.h 文件,所以没有重建.c 和.h 文件的规则。不过 C 言语源程序文件可以使用工具Bison或者 Yacc 来生成(具体用法可参考相应的手册) 。
完成了对.o文件的创建(第一次编译)或者更新之后,make程序将处理终极目标“edit”所在的规则,分为以下三种情况:
1. 目标文件“edit”不存在,则执行规则以创建目标“edit”。
2. 目标文件“edit”存在,其依赖文件中有一个或者多个文件比它“更新” ,则根据规则重新链接生成“edit”。
3. 目标文件“edit”存在,它比它的任何一个依赖文件都“更新”,则什么也不做。
上例中,如果更改了源文件“insert.c”后执行make,“insert.o”将被更新,之后终极目标“edit”将会被重生成;如果我们修改了头文件“command.h”之后运行“make”,那么“kbd.o”、“command.o”和“files.o”将会被重新编译,之后同样终极目标“edit”也将被重新生成。
以上我们通过一个简单的例子,介绍了 Makefile 中目标和依赖的关系。我们简单总结一下:对于一个 Makefile 文件,“make”首先解析终极目标所在的规则(上节例子中的第一个规则),根据其依赖文件(例子中第一个规则的8个.o文件)依次(按照依赖文件列表从左到右的顺序)寻找创建这些依赖文件的规则。首先为第一个依赖文件(main.o)寻找创建规则,如果第一个依赖文件依赖于其它文件(main.c、defs.h),则同样为这个依赖文件寻找创建规则(创建 main.c和 defs.h的规则,通常源文件和头文件已经存在,也不存在重建它们的规则)……,直到为所有的依赖文件找到合适的创建规则。之后 make 从最后一个规则(上例目标为 main.o 的规则)回退开始执行,最终完成终极目标的第一个依赖文件的创建和更新。之后对第二个、第三个、第四个……终极目标的依赖文件执行同样的过程(上例的的顺序是“main.o”、“kbd.o”、“command.o”……)。
创建或者更新每一个规则依赖文件的过程都是这样的一个过程(类似于 C 语言中的递归过程) 。对于任意一个规则执行的过程都是按照依赖文件列表顺序,对于规则中的每一个依赖文件,使用同样方式(按照同样的过程)去重建它,在完成对所有依赖文件的重建之后,最后一步才是重建此规则的目标。
更新(或者创建)终极目标的过程中,如果任何一个规则执行出现错误 make就立即报错并退出。整个过程make只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make不做任何错误检查。
因此,需要正确的编译一个工程。需要在提供给 make 程序的 Makefile 中来保证其依赖关系的正确性、和执行命令的正确性。