【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 报错的行号会指向哪里?(这是一个很有趣的调试问题)
相关推荐
代码AC不AC2 分钟前
【Linux】线程同步
linux·线程·线程同步
糖果店的幽灵1 小时前
软件测试接口测试从入门到精通:curl命令行工具
linux·软件测试·接口测试·命令行·curl
毒爪的小新9 小时前
Linux 环境极速部署 vLLM:从零搭建生产级大模型推理服务
linux·人工智能·ai·语言模型·vllm
鹤落晴春9 小时前
RH124问答3:从命令行管理文件
linux·运维·服务器
凡人叶枫9 小时前
Effective C++ 条款30:透彻了解 inlining 的里里外外
linux·开发语言·c++·嵌入式开发·effective c++
Net_Walke10 小时前
【Linux系统】静态链接库与动态链接库
linux·嵌入式硬件
syc789012310 小时前
中文语境下AI编码工具实战对比:从迭代体验看日常开发选择
linux·人工智能·ubuntu
凡人叶枫10 小时前
Effective C++ 条款22:将成员变量声明为 private
linux·开发语言·c++
vsropy12 小时前
Ubuntu网络图标消失问题/有网络问号
linux·运维·ubuntu
coderwu13 小时前
Ubuntu 24.04 终端输入 openclaw config 提示未找到命令解决办法
linux·运维·ubuntu