CMake

The Android NDK supports using CMake to compile C and C++ code for your application. This page discusses how to use CMake with the NDK via the Android Gradle Plugin's ExternalNativeBuild or when invoking CMake directly.

The CMake toolchain file

The NDK supports CMake via a toolchain file. Toolchain files are CMake files that customize the behavior of the toolchain for cross-compiling. The toolchain file used for the NDK is located in the NDK at <NDK>/build/cmake/android.toolchain.cmake.

Build parameters such as ABI, minSdkVersion, etc. are given on the command line when invoking cmake. For a list of supported arguments, see the Toolchain arguments section.

The "new" toolchain file

Earlier NDKs experimented with a new implementation of the toolchain file that would reduce behavior differences between using the NDK's toolchain file and using the built-in CMake support. This ended up requiring a significant amount of work (which has not been completed), but did not actually improve behavior, so we're no longer pursuing this.

The "new" toolchain file has behavior regressions compared to the "legacy" toolchain file. The default behavior is the recommended workflow. If you're using -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF, we recommend removing that flag from your build. The new toolchain file never reached parity with the legacy toolchain file, so there are likely behavior regressions.

Though we recommend against using the new toolchain file, there are currently no plans to remove it from the NDK. Doing so would break builds that rely on the behavior differences between the new and legacy toolchain files, and unfortunately renaming the option to make it clear that "legacy" is actually recommended would also break users of that option. If you're happily using the new toolchain file you do not need to migrate, but know that any bugs filed against new toolchain file behavior will probably not be fixed, and instead you'll need to migrate.

Usage

Gradle

Use of the CMake toolchain file is automatic when using externalNativeBuild. See Android Studio's Add C and C++ code to your project guide for more information.

Command Line

When building with CMake outside of Gradle, the toolchain file itself and its arguments must be passed to CMake. For example:

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

Toolchain arguments

The following arguments can be passed to the CMake toolchain file. If building with Gradle, add arguments to android.defaultConfig.externalNativeBuild.cmake.arguments as described in the ExternalNativeBuild docs. If building from the command line, pass arguments to CMake with -D. For example, to force armeabi-v7a to not build with Neon support, pass -DANDROID_ARM_NEON=FALSE.

ANDROID_ABI

The target ABI. For information on supported ABIs, see Android ABIs.

Gradle

Gradle provides this argument automatically. Do not explicitly set this argument in your build.gradle file. To control what ABIs Gradle targets, use abiFilters as described in Android ABIs.

Command Line

CMake builds for a single target per build. To target more than one Android ABI, you must build once per ABI. It is recommended to use different build directories for each ABI to avoid collisions between builds.

Value Notes
armeabi-v7a
armeabi-v7a with NEON Same as armeabi-v7a.
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

Specifies whether to generate arm or thumb instructions for armeabi-v7a. Has no effect for other ABIs. For more information, see the Android ABIs documentation.

Value Notes
arm
thumb Default behavior.

ANDROID_NATIVE_API_LEVEL

Alias for ANDROID_PLATFORM.

ANDROID_PLATFORM

Specifies the minimum API level supported by the application or library. This value corresponds to the application's minSdkVersion.

Gradle

When using the Android Gradle Plugin, this value is automatically set to match the application's minSdkVersion and should not be set manually.

Command Line

When invoking CMake directly, this value defaults to the lowest API level supported by the NDK in use. For example, with NDK r20 this value defaults to API level 16.

Multiple formats are accepted for this parameter:

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

The $API_LETTER format allows you to specify android-N without the need to determine the number associated with that release. Note that some releases received an API increase without a letter increase. These APIs can be specified by appending the -MR1 suffix. For example, API level 25 is android-N-MR1.

ANDROID_STL

Specifies which STL to use for this application. For more information, see C++ library support. By default, c++_static will be used.

Value Notes
c++_shared The shared library variant of libc++.
c++_static The static library variant of libc++.
none No C++ standard library support.
system The system STL

Manage compiler flags

If you need to pass specific flags to the compiler or linker for your build, refer to the CMake documentation for set_target_compile_options and the related family of options. The "see also" section at the bottom of that page has some helpful clues.

In general, the best practice is to apply compiler flags as the narrowest available scope. Flags that you want to apply to all of your targets (such as -Werror) are inconvenient to repeat per-module, but still should rarely be applied globally (CMAKE_CXX_FLAGS), as those may have undesired effects on third-party dependencies in your project. For such cases, the flags can be applied at directory-scope (add_compile_options).

For a narrow subset of compiler flags, they can also be set in your build.gradle file using cppFlags or similar properties. You should not do this. Flags passed to CMake from Gradle will have surprising precedence behaviors, in some cases overriding flags passed implicitly by the implementation which are required for building Android code. Always prefer handling CMake behavior directly in CMake. If you need to control compiler flags per AGP buildType, see Work with AGP build types in CMake.

Work with AGP build types in CMake

If you need to tailor CMake behavior to a custom Gradle buildType, use that build type to pass an additional CMake flag (not a compiler flag) that your CMake build scripts can read. For example, if you have "free" and "premium" build variants that are controlled by your build.gradle.kts and you need to pass that data to CMake:

android {
    buildTypes {
        free {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
                }
            }
        }
        premium {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
                }
            }
        }
    }
}

Then, in your CMakeLists.txt:

if (DPRODUCT_VARIANT_PREMIUM)
  # Do stuff for the premium build.
else()
  # Do stuff for the free build.
endif()

The name of the variable is up to you, but make sure you avoid anything with an ANDROID_, APP_, or CMAKE_ prefix to avoid collision or confusion with existing flags.

See the Sanitizers NDK sample for an example.

Understand the CMake build command

When debugging CMake build issues, it's helpful to know the specific build arguments that Gradle uses when cross-compiling for Android.

The Android Gradle Plugin saves the build arguments it uses for executing a CMake build for each ABI and build type pair to the build_command.txt. These files are found in the following directory:

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

The following snippet shows an example of the CMake arguments to build a debuggable release of the hello-jni sample targeting the armeabi-v7a architecture.

                    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

Use prebuilt libraries

If the prebuilt library you need to import is distributed as an AAR, follow Studio's dependency docs to import and use those. If you are not using AGP you can follow https://google.github.io/prefab/example-workflow.html, but it is likely much easier to migrate to AGP.

For libraries that are not distributed as an AAR, instructions on using prebuilt libraries with CMake, see the add_library documentation regarding IMPORTED targets in the CMake manual.

Building third-party code

There are a handful of ways to build third-party code as part of your CMake project, and which option works best will depend on your situation. The best option will often be to not do this at all. Instead, build an AAR for the library and consume that in your application. You do not necessarily need to publish that AAR. It can be internal to your Gradle project.

If that's not an option:

  • Vendor (i.e. copy) the third-party source into your repository and use add_subdirectory to build it. This only works if the other library is also built with CMake.
  • Define an ExternalProject.
  • Build the library separately from your project and follow Use prebuilt libraries to import it as a prebuilt.

YASM support in CMake

The NDK provides CMake support for building assembly code written in YASM to run on x86 and x86-64 architectures. YASM is an open-source assembler for x86 and x86-64 architectures, based on the NASM assembler.

To build assembly code with CMake, make the following changes in your project's CMakeLists.txt:

  1. Call enable_language with the value set to ASM_NASM.
  2. Depending on whether you are building a shared library or an executable binary, call add_library or add_executable. In the arguments, pass in a list of source files consisting of the .asm files for the assembly program in YASM and the .c files for the associated C libraries or functions.

The following snippet shows how you might configure your CMakeLists.txt to build a YASM program as a shared library.

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)

For an example of how to build a YASM program as an executable, see the yasm test in the NDK git repository.

Report problems

If you run into any issues with the NDK or its CMake toolchain file, report them via the android-ndk/ndk issue tracker on GitHub. For Gradle or Android Gradle Plugin issues, report a Studio bug instead.