虽然现在很多时候都不用手写 Makefile 了,例如 Autotoolscmakesconsqmake 等,都是很好的构建系统。但能够读懂 Makefile 在很多时候是非常必要的,因此本文列举一些基本的 Makefile 要点,方便笔者在需要的时候参考。

Makefile 是由一系列规则(Rule)组成的,一条规则包含三个部分:工作目标(target)、必要条件(prerequisites,或称依存对象 dependents)和要执行的命令(command)。

target: prereq 1 prereq2
    commands            # 命令一定要以 tab 字符开始,在 vim 下可用 set list 查看

工作目标是一个必须构建的文件或进行的事情;必要条件工作目标被创建或执行之前,必须事先存在的文件;而命令则是将必要条件创建为工作目标需要执行的 shell 命令。

当 make 要处理某项规则时,它会找出必要条件工作目标中指定的文件,如果必要条件中的文件有关联的规则,make 会尝试处理这些规则,之后才是工作目标。如果必要条件中文件有更改的文件,则 make 会执行命令以便重新构建工作目标命令会被传递给 shell 并在 subshell 中执行。

由于有些规则工作目标可以是另一个规则必要条件,因此这些工作目标必要条件形成了一个依赖关系图(dependency graph),建立并处理这个关系图以更新指定的工作目标就是 make 所要做的事情。

假想(Phony)工作目标

.PHONY 用来告诉 make,它的必要条件对应的工作目标不是一个真正的文件。常见的假想工作目标有:

工作目标            功能
all             执行编译应用程序的所有工作
install         从已编译的二进制文件进行应用程序的安装
clean           将产生自源代码的二进制文件删除
distclean       删除编译过程中所产生的任何文件
TAGS            建立可供编辑器使用能够的编辑表(ctags和etags)
info            从 Textinfo 源代码来创建 GNU info 文件
check           执行与应用程序相关的测试

自动变量(Automatic Variables)

make 中使用 $(variable-name)(现在更常用一点) 或 ${variable-name} 标识变量,单字符的变量不需要加括号,通常 makefile 文件中会定义许多变量,不过其中有许多特殊变量是 make 自动定义的。当规则相符时, make 会设定自动变量,通过它们,你可以取用工作目标以及必要条件中的元素。

$@              工作目标的文件名
$%              档案文件成员结构中的文件名元素
$<              第一个必要条件的文件名
$?              时间戳在工作目标时间戳之后的所有必要条件,并以空格隔开这些必要条件。即上次执行之后变更过的必要条件
$^              以空格隔开的所有必要条件列表(重复的文件名会被删除)
$+              同 $^,但包含所有的重复文件名
$*              工作目标的主文件名。一个文件名称由两部分组成:主文件名(stem)和扩展名(suffix)

变量类型

make 的变量有两种类型:简单扩展(simply expanded)变量和递归扩展(recursively expanded)变量。前者使用 := 来定义,而后者使用 = 定义。递归变量的扩展动作会被延迟到该变量被使用的时候才进行。

函数

具体见到了直接查手册。

命令修饰符

一个命令可以通过若干前缀加以修饰。

  • @ 修饰符告诉 make 不要输出命令本身
  • - 用来指示 make 忽略被修饰行的结束状态,当命令出错的时候,不会终止运行
  • + 用来要求 make 执行这条命令,即使用户是以 –just-print(-n) 选项来执行 make