【Makefile 专家之路 | 函数篇】11. 终极奥义:eval 函数——动态生成规则的“核武器”

文章目录

  • [一、 为什么要用 eval?](#一、 为什么要用 eval?)
  • [二、 eval 的"两段式"展开逻辑(脑细胞风暴预警)](#二、 eval 的“两段式”展开逻辑(脑细胞风暴预警))
  • [三、 安卓实战:动态生成多个模块的编译规则](#三、 安卓实战:动态生成多个模块的编译规则)
  • [四、 💡 安卓工程师的"避坑"金律](#四、 💡 安卓工程师的“避坑”金律)
  • [五、 💡 安卓工程师的记忆卡片](#五、 💡 安卓工程师的记忆卡片)

一、 为什么要用 eval?

在普通的 Makefile 中,规则是"死"的。比如你要编译 100 个类似的模块,难道要手写 100 遍
target: dependency 吗?

  • 普通函数 : 返回一个字符串(如 patsubst)。
  • eval 函数 : 把一个字符串变成 Makefile 的代码
  • 核心价值: 它是"产生代码的代码"。它允许你在程序运行期间,根据变量动态地"原地写出"新的规则、变量定义或指令。

二、 eval 的"两段式"展开逻辑(脑细胞风暴预警)

这是 eval 最难理解的地方:它会对内容进行两次展开。

  1. 第一次展开eval 内部的变量(如 $(VAR))会被替换成具体的值,形成一段临时的"纯文本"。
  2. 第二次展开make 解析器把这段"纯文本"当成正常的 Makefile 语法重新读一遍。
  3. 专家提醒 : 因为有两次展开,所以在 eval 内部定义规则时,经常需要用到双美元符号 $$。第一个 $ 用来躲过第一次展开,让第二个 $ 在第二次展开时生效。

三、 安卓实战:动态生成多个模块的编译规则

假设你要为 Android 系统编译三个不同的工具包(ToolA, ToolB, ToolC),它们的编译逻辑一模一样:

bash 复制代码
TOOLS := ToolA ToolB ToolC

# 定义一个生成规则的模板
define create-rule
$(1): $(1).c
	gcc -o $(1) $(1).c
	@echo "编译模块 $(1) 完成"
endef

# 使用 eval 和 foreach 批量"炸"出规则
$(foreach t,$(TOOLS),$(eval $(call create-rule,$(t))))

发生了什么?

  • foreach 循环三次。
  • 第一次循环,call 生成了字符串:ToolA: ToolA.c ...
  • eval 拿过这个字符串,直接拍在 Makefile 里。
  • 最终,你的 Makefile 就像瞬间多出了 3 段手写的规则一样。

四、 💡 安卓工程师的"避坑"金律

  1. 调试极其困难 : 因为 eval 生成的规则是动态的,在文件中看不见。

    • 技巧 : 调试时,把 eval 换成 info(如 $(info $(call create-rule,$(t)))),这样 make 会把即将生成的代码打印在屏幕上,方便你检查语法。
  2. $$ 的玄学 : 如果在 eval 定义的命令里引用变量,记得用 $$。

    • $(VAR) → \rightarrow → 在 eval 执行时就被换掉了。
    • \$\$(VAR) → \rightarrow → 只有在最终执行命令(Shell 运行)时才换掉。
  3. 不要滥用 : 虽然 eval 很酷,但它会显著增加 Makefile 的维护成本。只有在处理 AOSP 那种规模的重复逻辑时,它才是最优解。


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

  1. eval 是翻译官 : 它把字符串翻译成 make 能听懂的指令。
  2. 它是 Android 的基石 : Android 源码中的 BUILD_PREBUILTBUILD_EXECUTABLE 等宏,本质上都是复杂的 eval 模板。
  3. 两遍扫描 : 永远记住它会"扫描两次",这能帮你解释 90% 关于 eval 的报错。

【本篇自测】

  1. 为什么在 define 模板中,如果我们要写 target: $$(DEP),必须要用两个 $
  2. 如果你在 eval 内部写了一个语法错误,make 报错的行号会指向哪里?(这是一个很有趣的调试问题)
相关推荐
皮卡蛋炒饭.1 小时前
进程得控制
linux·运维·服务器
YMWM_2 小时前
Install pyrealsense2 on the jetson thor
linux·realsense2
BestOrNothing_20153 小时前
(3)Ubuntu 22.04 双系统安装全过程记录
linux·ubuntu22.04·双系统安装
寂柒4 小时前
Linux——基础IO
linux
杨云龙UP4 小时前
Oracle ASM磁盘组空间分配与冗余理解
linux·运维·数据库·sql·oracle
朽棘不雕4 小时前
Linux权限
linux
minji...5 小时前
Linux 库制作与原理(三)深入动静态链接原理
linux·运维·服务器·c++
bukeyiwanshui5 小时前
Linux实践
linux·运维·服务器
xlp666hub5 小时前
【Linux驱动实战】:字符设备之ioctl与mutex全解析
linux·面试