文章目录
- [一、 为什么需要逻辑函数?](#一、 为什么需要逻辑函数?)
- [二、 三大逻辑金刚](#二、 三大逻辑金刚)
- [三、 编译哨兵:环境断言与输出](#三、 编译哨兵:环境断言与输出)
- [四、 💡 安卓工程师的进阶组合拳](#四、 💡 安卓工程师的进阶组合拳)
- [五、 💡 总结给 CSDN 读者的技术笔记](#五、 💡 总结给 CSDN 读者的技术笔记)
一、 为什么需要逻辑函数?
在第七章我们学了 ifeq 指令,但它是"块级开关"。如果你想在变量定义内部 或者函数参数里做判断,指令就放不进去了。这时,我们需要的是能返回值的"逻辑函数"。
二、 三大逻辑金刚
- 条件分支:
$(if <condition>,<then-part>,<else-part>)- 逻辑 :如果
<condition>展开后非空 (哪怕是一个空格),则为真,执行then-part;否则执行else-part。 - 安卓实战:根据是否开启混淆(PROGUARD)来决定混淆文件的路径。
- 逻辑 :如果
bash
# 如果定义了 ENABLE_PROGUARD,就使用专用配置,否则用默认配置
CONFIG_FILE := $(if $(ENABLE_PROGUARD),proguard_full.flags,proguard_basic.flags)
2. 短路逻辑:$(and <cond1>,<cond2>,...)
- 逻辑:从左到右扫描,只要遇到一个为空,立刻返回空。全都不为空,则返回最后一个条件的值。
- 场景 :只有当
ARCH=arm64且VARIANT=user时才编译安全模块。
3. 补位逻辑:$(or <cond1>,<cond2>,...)
- 逻辑:返回第一个非空的条件值。
- 场景 :设置默认值。
SRC_DIR := $(or $(CUSTOM_DIR),$(DEFAULT_DIR),./src)。
三、 编译哨兵:环境断言与输出
在 AOSP 这种复杂的环境中,你绝对不希望编译跑了半小时才发现某个环境变量没设。
1. 致命错误:$(error <text>)
- 行为 :立即停止
make执行并报错。 - 实战:强制环境检查。
bash
ifndef TARGET_PRODUCT
$(error [FATAL] 尚未检测到 TARGET_PRODUCT。请先执行 source build/envsetup.sh 并 lunch)
endif
2. 友好警告:$(warning <text>)
- 行为:输出信息,显示当前文件名和行号,但不停止编译。
- 实战:提醒开发者某些可选优化未开启。
3. 调试雷达:$(info <text>)
- 行为:仅输出文本,不带行号。
- 专家技巧 :它是调试
eval(下一章)的最佳拍档。在动态生成代码前,先用info看看拼接出来的字符串对不对。
四、 💡 安卓工程师的进阶组合拳
当 foreach 遇见 if,你的 Makefile 就开始有"人工智能"的味道了:
bash
# 需求:从文件列表中挑选出所有的 .so 库,并给它们加上前缀
ALL_FILES := main.c utils.h libart.so libgui.so
TARGET_LIBS := $(foreach f,$(ALL_FILES),$(if $(filter %.so,$(f)),$(addprefix my_,$(f)),))
# 结果:my_libart.so my_libgui.so
五、 💡 总结给 CSDN 读者的技术笔记
逻辑函数的生存法则:
- 非空即为真 :
$(if )的判断标准很简单,字符串里只要有一个字符(哪怕是空格)就是真。 - 报错越早越好 :善用
$(error)把风险拦截在编译初始化阶段。 - 调试用 info:不要对着报错盲猜,把变量打印出来是解决问题的最短路径。
【本篇自测】
$(if $(DEBUG),-g)和ifeq ($(DEBUG),true) ... endif在使用场景上有什么区别?- 如果执行
$(or , , )(三个参数都为空),返回值是什么?