CMake

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 等编译参数。有关所支持参数的列表,参阅工具链参数部分。

用法

Gradle

使用 externalNativeBuild 时,系统会自动使用 CMake 工具链文件。查看 Android Studio 的向您的项目添加 C 和 C++ 代码指南了解详情。

命令行

在 Gradle 之外使用 CMake 进行编译时,工具链文件本身及其参数必须传递给 CMake。例如:

$ cmake \
        -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
        -DANDROID_ABI=$ABI \
        -DANDROID_NATIVE_API_LEVEL=$MINSDKVERSION \
        $OTHER_ARGS
    

工具链参数

以下参数可以传递给 CMake 工具链文件。如果使用 Gradle 进行编译,请按照 ExternalNativeBuild docs 中所述向 android.defaultConfig.externalNativeBuild.cmake.arguments 添加参数。如果通过命令行进行编译,则将参数传递给 CMake 并加上 -D。例如,要强制 armeabi-v7a 始终使用 Neon 支持编译,请传递 -DANDROID_ARM_NEON=TRUE

ANDROID_ABI

目标 ABI。如需了解支持的 ABI 的信息,请参阅 Android ABI

Gradle

Gradle 会自动提供此参数。请勿在您的 build.gradle 文件中明确设置此参数。要控制 ABI Gradle 的目标,请按照 Android ABI 中所述使用 abiFilters

命令行

CMake 针对每个版本的单个目标进行编译。要以多个 Android ABI,您必须为每个 ABI 编译一次。建议对每个 ABI 使用不同的编译目录,以避免版本之间发生冲突。

注意
armeabi-v7a
armeabi-v7a with NEON -DANDROID_ABI=armeabi-v7a -DANDROID_ARM_NEON=ON 相同。
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

指定是否为 armeabi-v7a 生成 ARM 或 Thumb 指令。对其他 ABI 没有影响。如需了解详情,请参阅 Android ABI 文档。

注意
arm
Thumb 默认行为。

ANDROID_ARM_NEON

为 armeabi-v7a 启用或停用 NEON。对其他 ABI 没有影响。对于 API 级别(minSdkVersionANDROID_PLATFORM)23 或更新版本,默认为 true,否则为 false。如需了解详情,请参阅 Neon 文档。

注意
TRUE API 级别 23 或更新版本的默认值。
FALSE API 级别 22 或较旧版本的默认值。

ANDROID_LD

选择要使用的链接器。lld 目前处于 NDK 实验阶段,可通过此参数启用。

注意
lld 启用 lld。
默认 对于给定的 ABI 使用默认链接器。

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++ 标准库。
系统 系统 STL

了解 CMake 编译命令

在调试 CMake 编译问题时,了解 Gradle 在为 Android 交叉编译时使用的具体编译参数会很有帮助。

Android Gradle 插件会将用于为每个 ABI 和编译类型对执行 CMake 编译的编译参数保存至 cmake_build_command.txt。这些文件位于以下目录中:

<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
    

以下代码段会显示一个 CMake 参数示例,用于编译面向 armeabi-v7a 架构的 hello-jni 示例的可调试版本。

Executable : ${HOME}/Android/Sdk/cmake/3.6.3155560/bin/cmake
    arguments :
    -H${HOME}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/src/main/cpp
    -B${HOME}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/.cxx/cmake/arm7Debug/armeabi-v7a
    -GAndroid Gradle - Ninja
    -DANDROID_ABI=armeabi-v7a
    -DANDROID_NDK=${HOME}/Android/Sdk/ndk-bundle
    -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${HOME}/Dev/github-projects/googlesamples/android-ndk/hello-jni/app/build/intermediates/cmake/arm7/debug/obj/armeabi-v7a
    -DCMAKE_BUILD_TYPE=Debug
    -DCMAKE_MAKE_PROGRAM=${HOME}/Android/Sdk/cmake/3.6.3155560/bin/ninja
    -DCMAKE_TOOLCHAIN_FILE=${HOME}/Android/Sdk/ndk-bundle/build/cmake/android.toolchain.cmake
    -DANDROID_NATIVE_API_LEVEL=23
    -DANDROID_TOOLCHAIN=clang
    jvmArgs :
    

使用预编译库

有关在 CMake 中使用预编译库的说明,请参阅 CMake 手册中关于 IMPORTED 目标的 add_library 文档。

CMake 中的 YASM 支持

NDK 为编译 YASM 汇编代码提供 CMake 支持,以便在 x86 和 x86-64 架构上运行。YASM 是 x86 和 x86-64 架构的开源汇编程序,它基于 NASM 汇编程序。

要使用 CMake 编译汇编代码,请在项目的 CMakeLists.txt 中进行以下更改:

  1. 调用 enable_language,并将值设置为 ASM_NASM
  2. 根据您是要编译共享库还是可执行的二进制文件,调用 add_libraryadd_executable{.external}。在参数中传递源文件列表,该列表包含用于 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 问题跟踪器报告这些问题。