0 Preface/Foreword
1 构建规则
构建规则中的三要素:
- 目标:target
- 依赖:dependancies / prerequisites
- 命令:commands
三要素的关系可以简单归纳为:为了生成目标🎯,需要哪些依赖文件,并通过执行什么命令来完成。
1.1 三要素的定义和角色
1.1.1 目标
目标相关描述如下:
- 定义:要生成的最终产物或中间产物。
- 形式:通常是一个文件名(可以是可执行文件main、目标文件main.o),也可以是一个动作标签(比如:clean、install,称为伪目标.PHONY)
- 角色:目标是构建过程的输出。
1.1.2 依赖
依赖相关描述如下:
- 定义:生成目标所必须的文件或另一个目标。
- 形式:源文件(*.c, *.cpp)、头文件(*.h)、库文件或者其他已生成的目标文件(*.o)。
- 角色:作为构建过程的输入。如果依赖文件比目标文件新(依赖被修改过),或者目标文件不存在,Make就会认为目标“过期”,需要重新构建。
1.1.3 命令
命令相关描述如下:
- 定义:将依赖转换为目标的具体操作步骤。
- 形式:shell命令,比如gcc -c main.c、 rf -f *.o .
- 关键语法要求:命令行的开头必须使用Tab键缩进,不能使用空格,否则Make会报错。
1.2 三要素之间的逻辑关系
Makefile的规则本质上是描述一个自动化构建流程,逻辑如下:
- 触发条件:Make工具检查目标文件是否存在,以及依赖文件的最后修改时间是否晚于目标文件。
- 执行判断:
- 目标不存在;
- 任意一个依赖文件比目标文件都更新(文件修改时间);
- 目标是伪目标(Phony Target)
- 结果:命令执行完成后,生成或更新目标文件。
1.3 注意事项
注意事项如下:
- Tab键强制要求:不能用空格替代,否则会造成make工具读取makefile文件时候,解析失败。
- 依赖的完整性:假设main.c中包含了utils.h,那么main.o的依赖需要写成main.o : main.c utils.h。否则,修改头文件时make不会重新编译对应的源文件,导致构建错误。
- 伪目标(.PHONY):例如clean这样的目标并不会对应实际文件,如果不申明为伪目标(.PHONY: clean),并且路径下有一个名为clean的文件,make会认为目标已是最新而拒绝执行删除命令。所以,建议始终使用.PHONY声明非文件类目标。
1.3.1 伪目标
通过.PHONY进行设定的目标。
1.4 自动变量
为了简化Makefile的编写,Make提供了自动变量:
- $@:代表当前规则的目标🎯。
- $^:代表当前规则的所有依赖。
- $<:代表当前规则的第一个依赖。
1.5 构建三要素组合规则
作为一个构建规则中的三要素,不同场景会出现不同的组合方式,不是所有场景三要素都必须存在,具体情况如下面表格所示:
| 组合情况 | 是否合法 | 典型应用 | 注意事项 |
| 目标+依赖+命令 | 是✅️ | 标准构建规则 | NULL |
| 目标+命令 | 是✅️ | 伪目标(all, clean, install) | 若非伪目标文件存在,命令不会执行 |
| 目标+依赖 | 是✅️ | 依赖聚合、阻止隐含规则(impicit rule) | 仅触发依赖检查,不执行shell命令 |
| 目标 | 是✅️ | 占位符、配合隐含规则(placeholder、coordinate) | 若无隐含规则且文件不存在,会报错 |