【Makefile 专家之路 | 基础篇】03. 规矩方圆:书写规则详解(通配符、文件搜索与伪目标)

文章目录

  • [一、 通配符:批量处理的艺术](#一、 通配符:批量处理的艺术)
  • [二、 文件搜索:VPATH 与 vpath](#二、 文件搜索:VPATH 与 vpath)
  • [三、 伪目标(.PHONY):绝对的"安卓必修课"](#三、 伪目标(.PHONY):绝对的“安卓必修课”)
  • [四、 多目标与多规则](#四、 多目标与多规则)
  • [五、 💡 安卓工程师的记忆卡片](#五、 💡 安卓工程师的记忆卡片)

一、 通配符:批量处理的艺术

在 Makefile 中,如果你想表示"所有的 .c 文件",你会用到通配符。

  1. *(星号) :与 Shell 里的用法一致。
    • 例子:rm -f *.o 表示删除所有以 .o 结尾的文件。
    • 陷阱: 如果你在变量定义中使用 objects = *.o,而此时目录下还没有生成任何 .o,那么 objects 的值就是字符串 *.o,而不是文件列表。
  2. % (百分号)这是 Makefile 的精髓 。它用于"匹配"。
    • 它不像 * 那样简单展开,而是在规则中定义"模式"。例如 %.o : %.c 表示"任何一个 .o 目标都依赖于同名的 .c 文件"。

二、 文件搜索:VPATH 与 vpath

在 Android 源码中,头文件和源文件通常分开放(比如 src/ 和 include/)。如果 Makefile 找不到文件,就会报错。

  1. VPATH 变量:这是一个全局搜索路径。
bash 复制代码
VPATH = src:../headers

make 如果在当前目录找不到依赖文件,就会去 src 和 .../headers 目录下挨个找。

  1. 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 不是一个真正的"文件",而是一个"动作"。无论目录下有没有同名文件,请务必执行它。

四、 多目标与多规则

  1. 多目标: 一个命令生成多个文件。
  2. 静态模式: 让你能更灵活地指定哪些目标应用哪些规则(我们将在之后的自动化篇深入剖析)。

五、 💡 安卓工程师的记忆卡片

  1. Android 源码中的 .PHONY
    在 AOSP 源码中,你会看到大量的伪目标,比如 make bootimage、make systemimage。这些显然不是为了生成一个叫 bootimage 的单个文件,而是触发一整套复杂的打包脚本。
  2. 目录搜索的工程实践
    虽然 VPATH 很好用,但在极大型项目中(如 Android),过度依赖全局 VPATH 可能会导致链接到错误版本的库。现代 Android 开发更倾向于在 Android.mk 中直接使用 $(LOCAL_PATH) 绝对路径或明确的相对路径。
  3. 续行符 \
    当 vpath 路径太长或者 OBJS 列表太长时,记得在行末使用 \。
    1)\ 后面不能有任何字符(空格和制表符也不能有)
    2)解析时,\ 和换行符被删除,但下一行的前导空白字符(空格和制表符)被保留,一同拼接到第一行的末尾
    3)echo (OBJS) 或 (info $(OBJS)) 输出时,make 本身会将多个空白压缩为一个空格显示(但实际值中仍有多个空白)。

【本篇思考题】

  1. 在 Android 源码中,如果你在当前目录下执行 make clean,结果发现没有任何文件被删除,但目录下确实有 .o 文件。请问最可能的原因是什么?
  2. %.o : %.c 和 *.o : *.c 这两种写法,哪一个是正确的模式匹配规则?
相关推荐
扛枪的书生31 分钟前
Linux 网络管理器用法速查
linux
顺风尿一寸3 小时前
Java Socket 内核之旅:从 SocketChannel.read() 到 tcp_recvmsg 与 epoll 的完整调用链路
linux
XIAOHEZIcode10 小时前
Ubuntu 终端美化全栈指南:Bash 到 Kitty 踩坑实录
linux·ubuntu·命令行
唐青枫12 小时前
别再只会用 cron:Linux systemd Timer 定时任务实战详解
linux
AlfredZhao2 天前
生产环境里,为什么不建议把普通端口直接暴露到公网?
linux·https·443·80
戴为沐3 天前
Linux内存扩容指南
linux
zylyehuo4 天前
Linux 彻底且安全地删除文件
linux
用户805533698034 天前
主线 U-Boot 上 RK3506:和闭源 rkbin 拔河的三个隐性契约
linux·嵌入式
用户034095297914 天前
linux fcitx 5 雾凇拼音 设置在中文输入法下仍然输入英文标点
linux
Web3探索者6 天前
可视化服务器管理和传统命令行区别是什么?新手教程:Linux 运维到底该用图形界面还是 SSH 命令行?
linux·ssh