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
:
- Call
enable_language
with the value set toASM_NASM
. - Depending on whether you are building a shared library or an executable
binary, call
add_library
oradd_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.