Android NDK 支持使用 CMake 编译应用的 C 和 C++ 代码。本页介绍了如何通过 Android Gradle 插件的 ExternalNativeBuild
或通过直接调用 CMake 将 CMake 与 NDK 搭配使用。
CMake 工具链文件
NDK 通过工具链文件支持 CMake。工具链文件是用于自定义交叉编译工具链行为的 CMake 文件。用于 NDK 的工具链文件位于 NDK 中以下位置:<NDK>/build/cmake/android.toolchain.cmake
。
在调用 cmake
时,命令行会提供 ABI 和 minSdkVersion
等构建参数。如需查看支持的参数的列表,请参阅工具链参数部分。
“新”工具链文件
早期的 NDK 试验了工具链文件的新实现, 会减少使用 NDK 的工具链文件与使用 NDK 时的行为差异 使用内置的 CMake 支持功能。这最终要求 (尚未完成),但实际上并无改进工作行为, 所以我们不再继续开发了
“新”与“旧版”相比,工具链文件存在行为回归问题
工具链文件。默认行为是推荐工作流程。如果您
使用 -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF
,建议移除该标志
。新工具链文件始终无法与旧版工具链文件相等
工具链文件,因此可能存在行为回归问题。
虽然我们建议不要使用新的工具链文件,但目前没有 并计划将其从 NDK 中移除。否则会破坏依赖于 新旧工具链文件之间的行为差异,以及 但遗憾的是,我们重命名了该选项 以便清楚地表明“旧版”实际上是 也会使用户无法选择该选项如果您乐意使用 新的工具链文件,但是知道提交的所有 bug 新的工具链文件行为可能无法得到修复,而是 您需要进行迁移
用法
Gradle
使用 externalNativeBuild
时,系统会自动使用 CMake 工具链文件。详情请参阅 Android Studio 的向您的项目添加 C 和 C++ 代码指南。
命令行
在 Gradle 之外使用 CMake 进行构建时,工具链文件本身及其参数必须传递给 CMake。例如:
$ cmake \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$MINSDKVERSION \
$OTHER_ARGS
工具链参数
以下参数可以传递给 CMake 工具链文件。如果使用 Gradle 进行构建,请按照 ExternalNativeBuild 文档中所述向 android.defaultConfig.externalNativeBuild.cmake.arguments
添加参数。如果通过命令行进行构建,请使用 -D
将参数传递给 CMake。例如,强制 armeabi-v7a 不要使用 Neon 进行构建
请传递 -DANDROID_ARM_NEON=FALSE
。
ANDROID_ABI
目标 ABI。如需了解支持的 ABI,请参阅 Android ABI。
Gradle
Gradle 会自动提供此参数。请勿在您的 build.gradle
文件中明确设置此参数。如需控制 ABI Gradle 的目标,请按照 Android ABI 中所述使用 abiFilters
。
命令行
对于每个 build,CMake 都只针对一个目标进行构建。如需以多个 Android ABI 为目标,您必须为每个 ABI 构建一次。建议对每个 ABI 使用不同的构建目录,以避免 build 之间发生冲突。
值 | 备注 |
---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
与 armeabi-v7a 相同。 |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
指定是为 armeabi-v7a 生成 arm 还是 thumb 指令。对其他 ABI 没有影响。如需了解详情,请参阅 Android ABI 文档。
值 | 备注 |
---|---|
arm | |
thumb | 默认行为。 |
ANDROID_NATIVE_API_LEVEL
ANDROID_PLATFORM 的别名。
ANDROID_PLATFORM
指定应用或库所支持的最低 API 级别。此值对应于应用的 minSdkVersion
。
Gradle
使用 Android Gradle 插件时,此值会自动设置为与应用的 minSdkVersion
相匹配,因此不应手动设置。
命令行
当直接调用 CMake 时,此值默认为所使用的 NDK 支持的最低 API 级别。例如,对于 NDK r20,此值默认为 API 级别 16。
此参数支持多种格式:
android-$API_LEVEL
$API_LEVEL
android-$API_LETTER
$API_LETTER
格式允许您指定 android-N
,而无需确定与该版本关联的编号。请注意,对于某些版本,API 只是编号增加了,但字母次序没有增加。您可以通过附加 -MR1
后缀来指定这些 API。例如,API 级别 25 为 android-N-MR1
。
ANDROID_STL
指定要为此应用使用的 STL。如需了解详情,请参阅 C++ 库支持。默认情况下将使用 c++_static
。
值 | 备注 |
---|---|
c++_shared | libc++ 的共享库变体。 |
c++_static | libc++ 的静态库变体。 |
无 | 不支持 C++ 标准库。 |
system | 系统 STL |
管理编译器标记
如果您需要将特定标记传递给构建的编译器或链接器, 请参阅 CMake 文档中的 set_target_compile_options 和 相关的选项系列“其他资源”该页面底部有 一些有用的线索
一般来说,最佳做法是将编译器标志作为最具体的
可用的范围。您希望应用于所有目标(例如
-Werror
)不方便在每个模块中重复,但仍应很少出现
(CMAKE_CXX_FLAGS
),因为这些政策可能会对
第三方依赖项对于此类情况,可以将标记
在目录范围 (add_compile_options
) 下应用。
对于一小部分编译器标志,也可以在 build.gradle 中设置它们
文件。cppFlags
您不应执行此操作。标志
从 Gradle 传递到 CMake 将有令人惊讶的优先行为,在某些
这些用例会覆盖
是构建 Android 代码所必需的。始终优先处理 CMake 行为
直接使用 CMake如果您需要根据 AGP buildType
控制编译器标志,
请参阅在 CMake 中使用 AGP build 类型。
在 CMake 中使用 AGP build 类型
如果您需要针对自定义 Gradle buildType
调整 CMake 行为,请使用
build 类型传递一个额外的 CMake 标志(不是编译器标志)
CMake 构建脚本可以读取。例如,如果您在“免费”时间段和“premium”
由 build.gradle.kts 控制的 build 变体,并且您需要通过
将该数据导出到 CMake:
android {
buildTypes {
free {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
}
}
}
premium {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
}
}
}
}
}
然后,在您的 CMakeLists.txt 中:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
变量的名称由您决定,但务必要避免使用任何带有
ANDROID_
、APP_
或 CMAKE_
前缀,以免与
现有标记。
如需查看示例,请参阅 Sanitizers NDK 示例。
了解 CMake 构建命令
在调试 CMake 构建问题时,了解 Gradle 在为 Android 交叉编译时使用的具体构建参数会很有帮助。
Android Gradle 插件会将用于为每对 ABI 和构建类型执行 CMake 构建的构建参数保存至 build_command.txt
。这些文件位于以下目录中:
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
以下代码段举例说明了如何使用 CMake 参数构建面向 armeabi-v7a
架构的 hello-jni
示例的可调试版本。
Executable : ${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/cmake
arguments :
-H${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/src/main/cpp
-DCMAKE_FIND_ROOT_PATH=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/prefab/armeabi-v7a/prefab
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk/22.1.7171670/build/cmake/android.toolchain.cmake
-DANDROID_ABI=armeabi-v7a
-DANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DANDROID_PLATFORM=android-23
-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a
-DCMAKE_ANDROID_NDK=${HOME}/Android/Sdk/ndk/22.1.7171670
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/build/intermediates/cmake/universalDebug/obj/armeabi-v7a
-DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.10.2.4988404/bin/ninja
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_SYSTEM_VERSION=23
-B${HOME}/Dev/github-projects/googlesamples/ndk-samples/hello-jni/app/.cxx/cmake/universalDebug/armeabi-v7a
-GNinja
jvmArgs :
Build command args: []
Version: 1
使用预构建库
如果您需要导入的预构建库是作为 AAR 分发的,请按照 Studio 的依赖项文档进行导入和使用。如果您未使用 AGP,可以按照 https://google.github.io/prefab/example-workflow.html 中的说明操作,但迁移到 AGP 可能要容易得多。
对于未作为 AAR 分发的库,如需了解有关如何将预构建库与 CMake 搭配使用的说明,请参阅 CMake 手册中关于 IMPORTED
目标的 add_library
文档。
构建第三方代码
将第三方代码作为 CMake 项目的一部分进行构建有多种方式,至于哪个方法最适合,则取决于您的具体情况。通常,最好的选择是别这样做。相反,请为相应的库构建 AAR,并在您的应用中使用该 AAR。倒不一定非要发布该 AAR。它可以作为 Gradle 项目的内部资源。
如果不行,请执行以下操作:
- 将第三方源代码提供(即复制)到您的代码库中,然后使用 add_subdirectory 构建相应源代码。只有在另一个库也使用 CMake 构建的情况下,这种方法才有效。
- 定义一个 ExternalProject。
- 独立于您的项目构建该库,然后按照使用预构建库中的说明将其导入为预构建库。
CMake 中的 YASM 支持
NDK 为构建 YASM 汇编代码提供 CMake 支持,以便在 x86 和 x86-64 架构上运行。YASM 是 x86 和 x86-64 架构的开源汇编程序,它基于 NASM 汇编程序。
如需使用 CMake 构建汇编代码,请在项目的 CMakeLists.txt
中进行以下更改:
- 调用
enable_language
,并将值设置为ASM_NASM
。 - 如果要构建共享库,则调用
add_library
;如果要构建可执行的二进制文件,则调用add_executable
。在参数中传入源文件列表,该列表包含用于 YASM 中的汇编程序的.asm
文件和用于相关 C 库或函数的.c
文件。
以下代码段展示了如何配置 CMakeLists.txt
来将 YASM 程序构建为共享库。
cmake_minimum_required(VERSION 3.6.0)
enable_language(ASM_NASM)
add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)
如需查看如何将 YASM 程序构建为可执行文件的示例,请参阅 NDK git 代码库中的 yasm 测试。
报告问题
如果您在 NDK 或其 CMake 工具链文件方面遇到任何问题,请通过 GitHub 上的 android-ndk/ndk 问题跟踪器报告这些问题。对于 Gradle 或 Android Gradle 插件问题,请改为报告 Studio bug。