文章目录
- [一、 通配符:批量处理的艺术](#一、 通配符:批量处理的艺术)
- [二、 文件搜索:VPATH 与 vpath](#二、 文件搜索:VPATH 与 vpath)
- [三、 伪目标(.PHONY):绝对的"安卓必修课"](#三、 伪目标(.PHONY):绝对的“安卓必修课”)
- [四、 多目标与多规则](#四、 多目标与多规则)
- [五、 💡 安卓工程师的记忆卡片](#五、 💡 安卓工程师的记忆卡片)
一、 通配符:批量处理的艺术
在 Makefile 中,如果你想表示"所有的 .c 文件",你会用到通配符。
- *(星号) :与 Shell 里的用法一致。
- 例子:rm -f *.o 表示删除所有以 .o 结尾的文件。
- 陷阱: 如果你在变量定义中使用 objects = *.o,而此时目录下还没有生成任何 .o,那么 objects 的值就是字符串 *.o,而不是文件列表。
- % (百分号) : 这是 Makefile 的精髓 。它用于"匹配"。
- 它不像 * 那样简单展开,而是在规则中定义"模式"。例如 %.o : %.c 表示"任何一个 .o 目标都依赖于同名的 .c 文件"。
二、 文件搜索:VPATH 与 vpath
在 Android 源码中,头文件和源文件通常分开放(比如 src/ 和 include/)。如果 Makefile 找不到文件,就会报错。
- VPATH 变量:这是一个全局搜索路径。
bash
VPATH = src:../headers
make 如果在当前目录找不到依赖文件,就会去 src 和 .../headers 目录下挨个找。
- vpath 关键字(小写): 更精准的分类搜索。
bash
vpath %.h ../headers
vpath %.c src
这告诉 make:所有的 .h 去 .../headers 找,所有的 .c 去 src 找。
三、 伪目标(.PHONY):绝对的"安卓必修课"
这是本章最重要的概念。
场景: 假设你的 Makefile 里有一个 clean 目标,但不巧,你的文件夹里真的有一个文件叫 clean
- 结果: 当你输入 make clean 时,make 发现 clean 文件已经存在,且没有依赖,它会认为"目标已最新",从而拒绝执行删除命令!
解决方案 : 使用 .PHONY 显式声明。
bash
.PHONY: clean
clean:
rm -rf $(OBJS)
- 意义: 告诉 make,clean 不是一个真正的"文件",而是一个"动作"。无论目录下有没有同名文件,请务必执行它。
四、 多目标与多规则
- 多目标: 一个命令生成多个文件。
- 静态模式: 让你能更灵活地指定哪些目标应用哪些规则(我们将在之后的自动化篇深入剖析)。
五、 💡 安卓工程师的记忆卡片
- Android 源码中的 .PHONY :
在 AOSP 源码中,你会看到大量的伪目标,比如 make bootimage、make systemimage。这些显然不是为了生成一个叫 bootimage 的单个文件,而是触发一整套复杂的打包脚本。 - 目录搜索的工程实践 :
虽然 VPATH 很好用,但在极大型项目中(如 Android),过度依赖全局 VPATH 可能会导致链接到错误版本的库。现代 Android 开发更倾向于在 Android.mk 中直接使用 $(LOCAL_PATH) 绝对路径或明确的相对路径。 - 续行符 \ :
当 vpath 路径太长或者 OBJS 列表太长时,记得在行末使用 \。
1)\ 后面不能有任何字符(空格和制表符也不能有)
2)解析时,\ 和换行符被删除,但下一行的前导空白字符(空格和制表符)被保留,一同拼接到第一行的末尾
3)echo (OBJS) 或 (info $(OBJS)) 输出时,make 本身会将多个空白压缩为一个空格显示(但实际值中仍有多个空白)。
【本篇思考题】
- 在 Android 源码中,如果你在当前目录下执行 make clean,结果发现没有任何文件被删除,但目录下确实有 .o 文件。请问最可能的原因是什么?
- %.o : %.c 和 *.o : *.c 这两种写法,哪一个是正确的模式匹配规则?