什么是Makefile

在学习Makefile之前,我们需要了解C语言是如何生成可执行文件的过程。源文件从创建到生成可执行文件需要经历四个阶段:

  1. 预处理(Pre-processing)
  2. 编译(Compilation)
  3. 汇编(Assemble)
  4. 链接(Linking)

关于这一块的内容,请参考我之前的文章《C编译过程》。

Makefile是控制make程序操作的文本文件。make程序通常用于源文件生成可执行文件的管理,此外,make还可以用于当文件(依赖文件)被修改后,重新生成可执行文件(或目标)。Makefile描述目标和依赖文件之间的关系,在一个或多个依赖文件发生更改时指定更新目标所需的命令。make确定文件是否被修改的唯一方法是比较目标文件和依赖文件的修改时间。

注:这里有两个专业术语,目标(target)和依赖文件(prerequisites)。
目标(target):通常是程序中间或者最后需要生成的文件名。可以是.o文件、也可以是最后的可执行程序的文件名。另外,目标也可以是一个make执行的动作的名称,如目标“clean”,成这样的目标是“伪目标”。

依赖文件(prerequisites):生成规则目标所需要的文件名列表。通常一个目标依赖于一个或者多个文件。

Makefile在某些方面有些独特,可能会与普通脚本造成混淆。

首先,Makefile在同一个文件中包含两种完全不同的编程语言。文件的大部分内容是make程序可以理解的语言编写的:Makefile提供了变量赋值和扩展,某些预处理功能(包含其他文件,条件解析等)以及目标和依赖文件的定义。此外,每个目标都有一个与之关联的配方(recipe),该配方指定应该调用哪些命令以使目标处于最新状态。

注:recipe有菜谱,处方,配方的意思。有些资料翻译成菜谱,我个人更喜欢翻译成配方。

该配方以shell脚本的形式编写(默认为POSIX sh)。make程序不解析这个脚本:它运行一个shell,并将该脚本传递给要运行的shell。要理解makefile,我们必须要记住关键的一个点就是:配方(recipe)不是由make解析的,而是由单独的shell进程处理的。

第二,makefile不是像脚本那样的过程性语言:当make解析makefile时,它在内部构造一个有向图,其中目标是图的节点,依赖关系是边。只有在所有makefile文件都被完全解析并且图构造完成之后,才可以选择一个节点(目标)并尝试将其更新。为了确保目标是最新的,它必须首先确保该目标的每个依赖文件都是最新的,依此类推。

Make版本

以下是几个常见的make程序的基本介绍。

名称 别名 初版 版本 发布日期
POSIX make 1992 IEEE Std 1003.1-2008,
2016 Edition
2016-09-30
NetBSD make bmake 1988 20160926 2016-09-26
GNU make gmake 1988 4.2.1 2016-06-10
SunPro make dmake 2006 2015-07-13
MSVS nmake 2003 2015p3 2016-06-27
下一节