CMake

您可借助 Android Studio 2.2 及更高版本,使用 NDK 和 CMake 将 C 及 C++ 代码编译到原生库中。之后,Android Studio 会使用 IDE 的集成构建系统 Gradle 将您的库封装到 APK。

如果您初次使用 Android Studio 的 CMake,请前往向您的项目添加 C 和 C++ 代码,学习以下方面的基础知识:向您的项目添加原生源文件、创建 CMake 构建脚本,以及将您的 CMake 项目添加为 Gradle 依赖项。本页提供一些其他信息,可用于自定义 CMake 构建。

在 Gradle 中使用 CMake 变量

将 Gradle 关联到您的 CMake 项目后,您可配置特定 NDK 变量,以改变 CMake 构建您原生库的方式。要将参数从模块级 build.gradle 文件传送到 CMake,请使用以下 DSL:

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake build script.
    externalNativeBuild {
      cmake {
        ...
        // Use the following syntax when passing arguments to variables:
        // arguments "-DVAR_NAME=ARGUMENT".
        arguments "-DANDROID_ARM_NEON=TRUE",
        // If you're passing multiple arguments to a variable, pass them together:
        // arguments "-DVAR_NAME=ARG_1 ARG_2"
        // The following line passes 'rtti' and 'exceptions' to 'ANDROID_CPP_FEATURES'.
                  "-DANDROID_CPP_FEATURES=rtti exceptions"
      }
    }
  }
  buildTypes {...}

  // Use this block to link Gradle to your CMake build script.
  externalNativeBuild {
    cmake {...}
  }
}

下表介绍在将 CMake 与 NDK 搭配使用时,您可以配置的部分变量。

变量名称 参数 说明
ANDROID_PLATFORM

如需平台名称和对应 Android 系统映像的完整列表,请参阅 Android NDK 原生 API

指定目标 Android 平台的名称。例如,android-18 指定 Android 4.3(API 级别 18)。

请勿直接更改此标记,而应设置您模块级 build.gradle 文件defaultConfigproductFlavors 块的 minSdkVersion 属性。这样就能确保只有安装在 Android 版本足够高的设备上的应用才能使用您的库。之后,CMake 工具链将按照以下逻辑为您正在构建的 ABI 选择最佳平台版本:

  1. 如果现有 ABI 平台版本等于 minSdkVersion,则 CMake 会使用该版本。
  2. 否则,如果现有 ABI 平台版本低于 minSdkVersion,则 CMake 会使用其中版本最高的平台。这种选择较为合理,因为通常情况下,平台版本缺失意味着自上一个可用版本以来,原生平台 API 未作任何改变。
  3. 否则,CMake 会使用下一个高于 minSdkVersion 的可用平台版本。
ANDROID_STL

如需获取完整的选项列表,请参阅帮助程序运行时

默认情况下,CMake 使用 c++_static

指定 CMake 应使用的 STL。

ANDROID_PIE
  • ONANDROID_PLATFORM = android-16 及更高级别时的默认值)
  • OFFANDROID_PLATFORM = android-15 及更低级别时的默认值)

指定是否使用位置独立的可执行文件 (PIE)。Android 动态链接器在 Android 4.1(API 级别 16)及更高级别上支持 PIE。

ANDROID_CPP_FEATURES

此变量默认为空。不过,下面列举几个您可传递的参数:

  • rtti(表示您的代码使用 RTTI)
  • exceptions(表示您的代码使用 C++ 异常)

指定 CMake 编译您的原生库时需使用的特定 C++ 功能,如 RTTI(运行时类型信息)和 C++ 异常。

ANDROID_ALLOW_UNDEFINED_SYMBOLS
  • TRUE
  • FALSE(默认)

指定 CMake 在构建您的原生库时,如果遇到未定义的引用,是否会引发未定义的符号错误。要停用这些类型的错误,请将此变量设置为 TRUE

ANDROID_ARM_MODE
  • arm
  • thumb(默认)

指定在 armthumb 模式中,是否生成 ARM 目标二进制文件。在 thumb 模式中,每个指令都是 16 位宽,并且与 thumb/ 目录中的 STL 库链接。传递 arm 会告知 CMake 在 32 位 arm 模式下生成您库的对象文件。

ANDROID_ARM_NEON
  • TRUE
  • FALSE

指定 CMake 是否应构建支持 NEON 的原生库。API 级别为 23 或更高级别时,默认值为 true,否则为 false。

ANDROID_DISABLE_FORMAT_STRING_CHECKS
  • TRUE
  • FALSE(默认)

指定是否在编译源代码时保护格式字符串。启用保护后,如果在 printf 样式函数中使用非常量格式字符串,则编译器会引发错误。

了解 CMake 构建命令

了解在对 Android 进行交叉编译时,Android Studio 所使用的具体构建参数,将有助于调试 CMake 构建问题。

Android Studio 会将其用于执行 CMake 构建的构建参数保存于 cmake_build_command.txt 文件。Android Studio 会针对您应用指向的每个应用二进制界面 (ABI),以及这些 ABI 的每个构建类型(即发行调试),为每个具体配置生成 cmake_build_command.txt 文件副本。Android Studio 随后会将其生成的文件放置于以下目录:

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

提示:在 Android Studio 中,您可使用键盘快捷键 (shift+shift) 快速浏览这些文件,并在输入字段输入 cmake_build_command.txt

以下代码片段举例说明用于构建指向 armeabi-v7a 架构的可调式版 hello-jni 示例的 CMake 参数。

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

构建参数

下表突出显示用于 Android 的 CMake 关键构建参数。这些构建参数并非由开发者设置。相反,Android Plugin for Gradle 会根据您项目的 build.gradle 配置,设置这些参数。

构建参数 说明
-G <build-system>

CMake 生成的构建文件类型。

对于 Android Studio 中含原生代码的项目,将 <build-system> 设置为 Android Gradle - Ninja。此设置表示 CMake 使用 ninja 构建系统,为您的应用编译和链接 C/C++ 源文件。CMake 还会生成 android_gradle_build.json 文件,其中包含有关 CMake 构建的 Gradle 插件元数据,如编译器标记和目标名称。

此设置表示 CMake 同时使用 Gradleninja 构建系统,为您的应用编译和链接 C/C++ 源文件。Studio 只支持 ninja 构建系统这一种生成器。

-DANDROID_ABI <abi>

目标 ABI。

NDK 支持一组 ABI,如 ABI 管理中所述。此选项类似于 ndk-build 工具所使用的 APP_ABI 变量。

默认情况下,Gradle 会针对 NDK 支持的各种 ABI,将您的原生库构建为单独的 .so 文件,并将其全部封装入您的 APK 中。如您只想让 Gradle 构建特定 ABI 配置,请按照向您的项目添加 C 和 C++ 代码中的说明操作。

如果未指定目标 ABI,则 CMake 默认使用 armeabi-v7a

有效的目标名称为:

  • armeabi:带软件浮点运算并基于 ARMv5TE 的 CPU。
  • armeabi-v7a:带硬件 FPU 指令 (VFPv3_D16) 并基于 ARMv7 的设备。
  • armeabi-v7a with NEON:与 armeabi-v7a 相同,但启用 NEON 浮点指令。这相当于设置 -DANDROID_ABI=armeabi-v7a-DANDROID_ARM_NEON=ON
  • arm64-v8a:ARMv8 AArch64 指令集。
  • x86:IA-32 指令集。
  • x86_64 - 用于 x86-64 架构的指令集。
-DANDROID_NDK <path> 您主机上安装的 NDK 根目录的绝对路径。
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY <path> 构建 LIBRARY 目标文件之后,CMake 在您的主机上存放这些文件的位置。
-DCMAKE_BUILD_TYPE <type> 类似于 ndk-build 工具的构建类型。变量值为 ReleaseDebug。为简化调试,CMake 不会在构建过程中删除发布或调试版本。但是,Gradle 在将其封装到 APK 的过程中会删除二进制文件。
-DCMAKE_MAKE_PROGRAM <program-name> 启动原生构建系统的工具。Gradle 插件为与 Android SDK 捆绑的 CMake ninja 生成器设置此值。
-DCMAKE_TOOLCHAIN_FILE <path> CMake 用于对 Android 进行交叉编译的 android.toolchain.cmake 文件的路径。通常情况下,此文件位于 $NDK/build/cmake/ 目录,而 $NDK 是您主机上 NDK 的安装目录。如需了解关于工具链文件的更多信息,请参阅 Cross Compiling for Android
-DANDROID_NATIVE_API_LEVEL <level> CMake 针对其进行编译的 Android API 级别。
-DANDROID_TOOLCHAIN <type> CMake 使用的编译器工具链。默认为 clang

CMake 中的 YASM 支持

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

该程序可用于将汇编语言程序或例程与 C 代码关联,以便从您的汇编代码访问 C 库或函数。您还能在编译完的 C 代码中添加简短的汇编例程,以充分利用汇编代码提供的更出色的机器性能。

要使用 CMake 构建汇编代码,请在您项目的 CMakeLists.txt 中作出以下变更:

  1. 调用 enable_language,且值设置为 ASM_NASM
  2. 根据您是构建共享库还是可执行二进制文件来决定调用 add_libraryadd_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 程序构建为可执行文件,请参阅 git 代码库中的 yasm 代码。

报告问题

如您遇到 CMake 开放源代码版本以外的问题,请通过 GitHub 上的 android-ndk/ndk 问题追踪器报告。