MNN Android 编译命令零基础解析笔记
我今天尝试在 Windows 上用 Android NDK + Ninja 编译 MNN,生成支持 LLM 的 Android 库。刚开始,我几乎完全不懂命令的意思,只是看教程复制粘贴。这里我把命令拆开,做了每个单词的英文来源、构词法和零基础解释,希望自己以后回头看能懂。
我用的命令是:
bash
cmake .. -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=arm64-v8a \
-DANDROID_PLATFORM=android-33 \
-DMNN_LOW_MEMORY=true \
-DMNN_CPU_WEIGHT_DEQUANT_GEMM=true \
-DMNN_BUILD_LLM=true \
-DMNN_SUPPORT_TRANSFORMER_FUSE=true \
-DMNN_ARM82=true \
-DMNN_USE_LOGCAT=true \
-DMNN_OPENCL=true \
-DLLM_SUPPORT_VISION=true \
-DMNN_BUILD_OPENCV=true \
-DBUILD_TEST=OFF \
-DMNN_BUILD_TEST=OFF
我来一个个解释。
1. cmake ..
-
英文来源:
cmake
→ "C Make",意思是"为 C/C++ 生成构建系统"(Makefile)..
→ "上一级目录",告诉 cmake 去上一级目录找 CMakeLists.txt 文件
-
零基础理解:
- 我们的项目里有一个
CMakeLists.txt
,就像"食谱",告诉电脑怎么做饭(编译程序) cmake ..
就是让 cmake 去上一级目录找到这个食谱
- 我们的项目里有一个
-
比喻:
- 想象你在厨房里(build 目录),你告诉助手(cmake):"去客厅的书架上拿菜谱(...)",然后开始准备原料(生成 Makefile/Ninja 文件)。
2. -G Ninja
-
英文来源:
-G
→ "Generator",意思是生成器Ninja
→ Ninja 是一个编译工具,比 Make 快
-
零基础理解:
- cmake 支持生成不同类型的构建文件,比如 Makefile 或 Ninja
-G Ninja
就是告诉 cmake:"我用 Ninja 来编译项目"
-
比喻:
- 就像你选择开车还是骑自行车去超市,
-G Ninja
就是选择"骑 Ninja 电动车",速度快。
- 就像你选择开车还是骑自行车去超市,
3. -DCMAKE_TOOLCHAIN_FILE=...
-
英文来源:
-
D
→ define(定义一个变量) -
CMAKE_TOOLCHAIN_FILE
→ "CMake 工具链文件"toolchain
→ 工具链,指编译、链接等一整套工具file
→ 文件
-
-
零基础理解:
- NDK 里有很多工具(编译器、链接器),要告诉 cmake 用哪一套工具
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake
就是指向 NDK 提供的"工具链文件",告诉 cmake 用 Android 编译器
NDK 是什么,为什么这里会提到 NDK
1️⃣ NDK 是什么
-
英文全称 :Native Development Kit
- Native → 本地的(这里指直接用 C/C++ 写的程序)
- Development → 开发
- Kit → 套件、工具包
-
零基础理解:
- Android 平常用 Java/Kotlin 写程序,但有些程序要更快或者用 C/C++ 写,比如 MNN 这种神经网络库
- NDK 就是 Android 官方提供的一套 C/C++ 编译工具,用来把你写的 C/C++ 代码变成 Android 手机能运行的程序
2️⃣ 为什么 CMake 要提到 NDK
-
CMake 是一个"总指挥",它需要知道你用哪套工具来编译程序
-
工具链(toolchain):
- 就是一整套工具:编译器、链接器、汇编器等
- NDK 自带 Android 专用的工具链
-
-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake
CMAKE_TOOLCHAIN_FILE
→ 告诉 CMake "你的工具链文件在哪"$ANDROID_NDK/.../android.toolchain.cmake
→ 指向 NDK 提供的专门文件,让 CMake 知道用 Android 的编译器
-
比喻:
- 你要做饭(编译程序),但是厨具太多,不知道用哪套
- 工具链文件就是一张说明书:告诉你用锅、用刀、用火,按 Android 手机的要求做菜
3️⃣ 总结
- NDK = Native Development Kit = Android 官方 C/C++ 工具包
- CMake 需要知道"你用哪套工具链",所以用
-DCMAKE_TOOLCHAIN_FILE
指向 NDK 提供的文件 - 这就是为什么命令里出现了 NDK
4. -DANDROID_ABI=arm64-v8a
-
英文来源:
ABI
→ Application Binary Interface,应用程序二进制接口arm64-v8a
→ ARM 64位架构(手机芯片型号)
-
零基础理解:
- 告诉 cmake 我编译的是 64 位 ARM 手机
- 如果手机是 32 位,或者 x86 模拟器,这里要改
5. -DANDROID_PLATFORM=android-33
-
英文来源:
ANDROID_PLATFORM
→ 安卓平台版本android-33
→ Android API 33(对应 Android 13)
-
零基础理解:
- 告诉 cmake 你要编译的安卓系统版本
- 不同版本可能有不同的系统库
-
比喻:
- 就像做菜:要做给 3 岁的小朋友还是 10 岁的小朋友吃,调料量不一样。
6. -DMNN_LOW_MEMORY=true
-
英文来源:
LOW_MEMORY
→ 低内存true
→ 开启
-
零基础理解:
- 告诉 MNN 库尽量节省内存
7. -DMNN_CPU_WEIGHT_DEQUANT_GEMM=true
-
英文来源:
-
CPU_WEIGHT_DEQUANT_GEMM
CPU
→ 中央处理器WEIGHT
→ 模型权重DEQUANT
→ 去量化GEMM
→ General Matrix Multiply(矩阵乘法)
-
-
零基础理解:
- 这是让 CPU 在执行神经网络矩阵运算时用去量化方式,提高效率
-
比喻:
- 就像做饭前把食材切小块,更容易炒熟。
8. -DMNN_BUILD_LLM=true
-
英文来源:
BUILD
→ 编译LLM
→ Large Language Model(大型语言模型)
-
零基础理解:
- 告诉 MNN 要编译 LLM 相关模块
-
比喻:
- 就像选择菜单:"今晚要做大餐(LLM 模块)"。
9. -DMNN_SUPPORT_TRANSFORMER_FUSE=true
-
英文来源:
SUPPORT
→ 支持TRANSFORMER
→ Transformer 模型FUSE
→ 融合
-
零基础理解:
- 开启 Transformer 模型优化融合
-
比喻:
- 就像做菜时把两个步骤合并成一个步骤,更快完成。
10. -DMNN_ARM82=true
-
英文来源:
ARM82
→ ARM CPU 的一个指令集扩展
-
零基础理解:
- 让 MNN 优化 ARMv8.2 架构的指令
-
比喻:
- 就像厨房升级了更高档的炉子,可以快炒。
11. -DMNN_USE_LOGCAT=true
-
英文来源:
LOGCAT
→ Android 的日志系统
-
零基础理解:
- 让程序在 Android 上打印日志,方便调试
12. -DMNN_OPENCL=true
-
英文来源:
OPENCL
→ Open Computing Language,GPU 加速
-
零基础理解:
- 告诉 MNN 使用 GPU 来加速计算
为什么 -DMNN_OPENCL=true
跟 GPU 有关系?
1. 英文来源
-
OPENCL → Open Computing Language
- Open → 开放的
- Computing → 计算
- Language → 语言
-
所以完整意思就是"开放计算语言",是一种可以用来控制GPU进行计算的语言
13. -DLLM_SUPPORT_VISION=true
-
英文来源:
VISION
→ 视觉SUPPORT
→ 支持
-
零基础理解:
- 开启 LLM 的视觉模块,需要处理图像
-
比喻:
- 就像做大餐不仅要炒菜,还要拍美图。
14. -DMNN_BUILD_OPENCV=true
-
英文来源:
OPENCV
→ Open Source Computer Vision Library(开源计算机视觉库)
-
零基础理解:
- 告诉 MNN 编译自带的 OpenCV 功能,比如
imread
(读图)
- 告诉 MNN 编译自带的 OpenCV 功能,比如
-
比喻:
- 就像准备相机拍菜,拍之前得先准备好相机。
15. -DBUILD_TEST=OFF
和 -DMNN_BUILD_TEST=OFF
-
英文来源:
BUILD_TEST
→ 是否编译测试程序OFF
→ 关闭
-
零基础理解:
- 不编测试程序,只编正式库
-
比喻:
- 就像做饭时不做试吃菜,只做正餐。
总结
-
每个
-D
是"定义一个变量",cmake 根据变量决定编译哪些模块 -
每个模块名称都有英文原意,很多是缩写
-
-G Ninja
是选择 Ninja 构建系统 -
-DCMAKE_TOOLCHAIN_FILE
是指定 NDK 工具链 -
-DANDROID_ABI=arm64-v8a
指定手机芯片架构 -
-DMNN_BUILD_LLM=true
要编 LLM -
-DMNN_OPENCL=true
是 GPU 加速 -
-DMNN_BUILD_OPENCV=true
让 MNN 自带的 OpenCV 功能编进去
配置完后,CMake 没报错,看起来一切正常:
-- Configuring done
-- Generating done
-- Build files have been written to: G:/down/MNN-master/MNN-master/build_64
二、开始编译
我直接运行:
bash
ninja install
然后就报错了:
undefined symbol: MNN::CV::imread
referenced by omni.cpp:557
我只知道 imread
是读图像的意思,其他完全没概念。我尝试理解:
- 编译器说找不到
MNN::CV::imread
这个东西 - 它好像在
omni.cpp
里被用到了 - 我的 CMake 配置里
-DMNN_BUILD_OPENCV=true
,理论上应该有这个函数啊
总之,我看到就是链接失败。
三、查找原因
我尝试去看 OpenCV 模块有没有生成:
bash
D:/sdk/ndk/27.2.12479018/toolchains/llvm/prebuilt/windows-x86_64/bin/llvm-nm.exe -gC OFF/arm64-v8a/libMNNOpenCV.so | grep "imread"
报错:
No such file or directory
我心里一紧:原来 OpenCV 模块根本没生成。
然后尝试单独编 OpenCV 模块:
bash
ninja MNNOpenCV
结果:
ninja: no work to do.
我完全懵了,好像它又说已经编好了,但实际上那个库文件根本不存在。