CMake

Android NDK は、CMake を使用した C および C++ のアプリコードのコンパイルをサポートしています。このページでは NDK で CMake を使用する方法として、Android Gradle プラグインの ExternalNativeBuild を介す場合と、CMake を直接呼び出す場合を説明します。

CMake ツールチェーン ファイル

NDK は CMake をツールチェーン ファイルを使ってサポートします。ツールチェーン ファイルはクロスコンパイルでのツールチェーンの動作をカスタマイズする CMake ファイルです。NDK で使用されるツールチェーン ファイルは NDK 内の <NDK>/build/cmake/android.toolchain.cmake にあります。

ABI、minSdkVersion などのビルド パラメータは、cmake を呼び出すときにコマンドラインで指定します。サポートされる引数のリストについては、ツールチェーンの引数のセクションをご覧ください。

「新しい」ツールチェーン ファイル

以前の NDK では、NDK のツールチェーン ファイルと組み込みの CMake サポートの使用とで動作の違いを減らすための、ツールチェーン ファイルの新しい実装をテストしました。かなりの作業が必要になりましたが(未完了)、実際には動作が改善されなかったので、これ以上の作業は行いません。

「新しい」ツールチェーン ファイルには、「以前の」ツールチェーン ファイルと比較して動作が低下しています。デフォルトの動作が推奨されるワークフローです。-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF を使用している場合は、ビルドからそのフラグを削除することをおすすめします。新しい toolchain ファイルは以前の toolchain ファイルと完全に一致しないため、動作が低下する可能性があります。

新しいツールチェーン ファイルの使用はおすすめしませんが、現在のところ NDK から削除する予定はありません。これを行うと、新しい toolchain ファイルとレガシー toolchain ファイルの動作の違いに依存するビルドが破損します。また、オプションの名前を変更して「レガシー」が実際に推奨されることを明確にすると、そのオプションを使用するユーザーも破損します。新しいツールチェーン ファイルを使用しても問題ない場合は移行する必要はありませんが、新しいツールチェーン ファイルの動作に対して報告されたバグはおそらく修正されず、移行する必要があります。

使用方法

Gradle

externalNativeBuild を使用したとき、CMake ツールチェーン ファイルが自動的に使用されます。詳細については、Android Studio のプロジェクトへの C / C++ コードの追加に関するガイドをご覧ください。

コマンドライン

CMake を使って Gradle の外部でビルドする場合、ツールチェーン ファイルそのものとその引数を 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 を使用します。

コマンドライン

CMake はビルドごとに 1 つのターゲットに対してビルドします。複数の Android ABI をターゲットにするには、ABI ごとにビルドする必要があります。ビルド間の競合を回避するには、ABI ごとに異なるビルド ディレクトリを使用することをおすすめします。

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 番号が増えても文字が変わらなかったリリースもあるのでご注意ください。その場合の API は、-MR1 サフィックスを追加して指定できます。たとえば、API レベル 25 は android-N-MR1 です。

ANDROID_STL

このアプリに使用する STL を指定します。詳細については、C++ ライブラリ サポートをご覧ください。デフォルトでは c++_static が使用されます。

c++_shared libc++ の共有ライブラリ バリアント。
c++_static libc++ の静的ライブラリ バリアント。
none C++ 標準ライブラリのサポートなし。
system system STL

コンパイラ フラグを管理する

ビルドのコンパイラまたはリンカーに特定のフラグを渡す必要がある場合は、CMake のドキュメントで set_target_compile_options と関連するオプション ファミリーをご覧ください。そのページの下部にある「関連情報」セクションに、役立つヒントが記載されています。

一般に、使用可能なスコープで最も狭い範囲としてコンパイラ フラグを適用することをおすすめします。すべてのターゲットに適用するフラグ(-Werror など)は、モジュールごとに繰り返すのは不便ですが、プロジェクトのサードパーティ依存関係に望ましくない影響を与える可能性があるため、グローバルに適用することはほとんどありません(CMAKE_CXX_FLAGS)。このような場合、フラグはディレクトリ スコープ(add_compile_options)で適用できます。

コンパイラ フラグの狭いサブセットの場合は、cppFlags などのプロパティを使用して build.gradle ファイルで設定することもできます。この操作は行わないでください。Gradle から CMake に渡されるフラグは、意外な優先動作を行います。場合によっては、Android コードのビルドに必要な、実装によって暗黙的に渡されたフラグをオーバーライドすることがあります。CMake の動作は常に CMake で直接処理します。AGP buildType ごとにコンパイラ フラグを制御する必要がある場合は、CMake で AGP ビルドタイプを操作するをご覧ください。

CMake で AGP ビルドタイプを使用する

CMake の動作をカスタムの Gradle buildType に合わせて調整する必要がある場合は、そのビルドタイプを使用して、CMake ビルド スクリプトが読み取れる追加の CMake フラグ(コンパイラ フラグではなく)を渡します。たとえば、build.gradle.kts によって制御される「無料」と「プレミアム」のビルド バリアントがあり、そのデータを 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>/

次のスニペットは、armeabi-v7a アーキテクチャをターゲットとする hello-jni のサンプルのデバッグ可能なリリースをビルドする CMake 引数の例を示します。

                    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 は必ずしも公開する必要はありません。Gradle プロジェクトで内部的に使用できます。

上記の方法が適していない場合:

  • サードパーティのソースをリポジトリに供給(コピー)し、add_subdirectory を使用してビルドします。この方法は、もう一方のライブラリも CMake でビルドされる場合にのみ機能します。
  • ExternalProject を定義します。
  • プロジェクトとは別にライブラリをビルドし、ビルド済みライブラリを使用するの手順に沿って、ビルド済みとしてインポートします。

CMake での YASM サポート

NDK は、YASM で記述され、x86 と x86-64 アーキテクチャで実行されるアセンブリ コードをビルドする CMake サポートを提供します。YASM は、NASM アセンブラをベースとする x86 と x86-64 アーキテクチャ用のオープンソース アセンブラです。

CMake でアセンブリ コードをビルドするには、プロジェクトの CMakeLists.txt を次のように変更します。

  1. ASM_NASM に設定する値を指定して enable_language を呼び出します。
  2. 共有ライブラリをビルドするのか、実行可能なバイナリをビルドするのかに応じて、add_library または add_executable を呼び出します。YASM のアセンブリ プログラムの .asm ファイルと、関連する C ライブラリまたは関数の .c ファイルから構成されるソースファイルのリストを引数に渡します。

次のスニペットは、YASM プログラムを共有ライブラリとしてビルドする CMakeLists.txt の設定方法の例を示します。

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 test をご覧ください。

問題を報告する

NDK または CMake のツールチェーン ファイルで問題が発生した場合は、GitHub の android-ndk/ndk 公開バグトラッカーで報告してください。Gradle または Android Gradle プラグインの問題については、代わりに Studio のバグを報告してください。