CMake

Das Android NDK unterstützt die Verwendung von CMake zum Kompilieren von C- und C++-Code für Ihre Anwendung. Auf dieser Seite wird beschrieben, wie Sie CMake mit dem NDK über die ExternalNativeBuild des Android Gradle-Plug-ins oder beim direkten Aufruf von CMake verwenden.

Die CMake-Toolchain-Datei

Das NDK unterstützt CMake über eine Toolchain-Datei. Toolchain-Dateien sind CMake-Dateien, mit denen das Verhalten der Toolchain für die plattformübergreifende Kompilierung angepasst wird. Die für das NDK verwendete Toolchain-Datei befindet sich im NDK unter <NDK>/build/cmake/android.toolchain.cmake.

Build-Parameter wie ABI, minSdkVersion usw. werden in der Befehlszeile angegeben, wenn cmake aufgerufen wird. Eine Liste der unterstützten Argumente finden Sie im Abschnitt Toolchain-Argumente.

Die „neue“ Toolchain-Datei

In früheren NDKs wurde mit einer neuen Implementierung der Toolchain-Datei experimentiert, um Verhaltensunterschiede zwischen der Verwendung der Toolchain-Datei des NDKs und der Verwendung der integrierten CMake-Unterstützung zu verringern. Das erforderte einen erheblichen Arbeitsaufwand (der noch nicht abgeschlossen ist), führte aber nicht zu einer Verbesserung des Verhaltens. Wir verfolgen diesen Ansatz daher nicht weiter.

Die neue toolchain-Datei weist im Vergleich zur alten toolchain-Datei Verhaltensregressionen auf. Das Standardverhalten ist der empfohlene Workflow. Wenn Sie -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF verwenden, empfehlen wir Ihnen, dieses Flag aus Ihrem Build zu entfernen. Die neue toolchain-Datei hat nie die Parität mit der alten toolchain-Datei erreicht. Daher sind wahrscheinlich Verhaltensregressionen aufgetreten.

Wir empfehlen zwar, die neue toolchain-Datei nicht zu verwenden, es ist aber derzeit nicht geplant, sie aus dem NDK zu entfernen. Dadurch würden Builds unterbrochen, die auf den Verhaltensunterschieden zwischen den neuen und Legacy-Toolchain-Dateien basieren, und leider würde die Umbenennung der Option, um deutlich zu machen, dass „Legacy“ tatsächlich empfohlen wird, auch Nutzer dieser Option beeinträchtigen würden. Wenn Sie die neue toolchain-Datei bereits verwenden, müssen Sie nicht migrieren. Beachten Sie jedoch, dass Fehler, die im Zusammenhang mit dem Verhalten der neuen toolchain-Datei gemeldet werden, wahrscheinlich nicht behoben werden. Sie müssen dann migrieren.

Nutzung

Gradle

Die Verwendung der CMake-Toolchain-Datei erfolgt bei Verwendung von externalNativeBuild automatisch. Weitere Informationen finden Sie im Android Studio-Leitfaden C- und C++-Code zum Projekt hinzufügen.

Befehlszeile

Wenn Sie CMake außerhalb von Gradle erstellen, müssen die Toolchain-Datei selbst und ihre Argumente an CMake übergeben werden. Beispiel:

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

Toolchain-Argumente

Die folgenden Argumente können an die Toolchain-Datei von CMake übergeben. Wenn Sie mit Gradle erstellen, fügen Sie android.defaultConfig.externalNativeBuild.cmake.arguments Argumente hinzu, wie in der ExternalNativeBuild-Dokumentation beschrieben. Wenn Sie über die Befehlszeile erstellen, übergeben Sie CMake mit -D Argumente. Wenn Sie beispielsweise erzwingen möchten, dass armeabi-v7a nicht mit Neon-Unterstützung erstellt wird, geben Sie -DANDROID_ARM_NEON=FALSE an.

ANDROID_ABI

Die Ziel-ABI. Informationen zu unterstützten ABIs finden Sie unter Android-ABIs.

Gradle

Gradle stellt dieses Argument automatisch bereit. Legen Sie dieses Argument nicht explizit in der Datei build.gradle fest. Wenn Sie festlegen möchten, auf welche ABIs Gradle ausgerichtet ist, verwenden Sie abiFilters wie unter Android-ABIs beschrieben.

Befehlszeile

CMake führt Builds für ein einzelnes Ziel pro Build aus. Wenn Sie Ihre App auf mehrere Android-ABIs ausrichten möchten, müssen Sie sie einmal pro ABI erstellen. Es wird empfohlen, für jede ABI ein anderes Build-Verzeichnis zu verwenden, um Konflikte zwischen Builds zu vermeiden.

Wert Hinweise
armeabi-v7a
armeabi-v7a with NEON Gleich wie bei armeabi-v7a.
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

Gibt an, ob ARM- oder Thumb-Anweisungen für armeabi-v7a generiert werden sollen. Hat keine Auswirkungen auf andere ABIs. Weitere Informationen finden Sie in der Dokumentation zu Android-ABIs.

Wert Hinweise
Arm
Daumen Standardverhalten.

ANDROID_NATIVE_API_LEVEL

Alias für ANDROID_PLATFORM.

ANDROID_PLATFORM

Gibt das von der Anwendung oder Bibliothek unterstützte minimale API-Level an. Dieser Wert entspricht dem minSdkVersion der Anwendung.

Gradle

Wenn Sie das Android-Gradle-Plug-in verwenden, wird dieser Wert automatisch auf die minSdkVersion der App festgelegt und sollte nicht manuell festgelegt werden.

Befehlszeile

Wenn CMake direkt aufgerufen wird, ist dieser Wert standardmäßig die niedrigste API-Ebene, die vom verwendeten NDK unterstützt wird. Bei NDK r20 ist dieser Wert beispielsweise standardmäßig API-Ebene 16.

Für diesen Parameter sind mehrere Formate zulässig:

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

Mit dem Format $API_LETTER kannst du android-N angeben, ohne die mit diesem Release verknüpfte Nummer ermitteln zu müssen. Beachten Sie, dass einige Releases eine API-Erhöhung ohne Buchstabenerhöhung erhalten haben. Diese APIs können durch Anhängen des Suffixes -MR1 angegeben werden. API-Ebene 25 ist beispielsweise android-N-MR1.

ANDROID_STL

Gibt an, welche STL für diese Anwendung verwendet werden soll. Weitere Informationen finden Sie unter C++-Bibliotheksunterstützung. Standardmäßig wird c++_static verwendet.

Wert Hinweise
c++_shared Die Variante der gemeinsam genutzten Bibliothek von libc++.
c++_static Die statische Bibliotheksvariante von libc++.
Keine Keine Unterstützung der C++-Standardbibliothek.
Infotainmentsystem Die System-STL

Compiler-Flags verwalten

Wenn Sie dem Compiler oder Linker für Ihren Build bestimmte Flags übergeben müssen, lesen Sie die CMake-Dokumentation zu set_target_compile_options und die zugehörigen Optionen. Der Abschnitt „Siehe auch“ am unteren Rand dieser Seite enthält einige hilfreiche Anhaltspunkte.

Im Allgemeinen empfiehlt es sich, Compiler-Flags auf den engsten verfügbaren Bereich anzuwenden. Flags, die Sie auf alle Ziele anwenden möchten (z. B. -Werror), müssen nicht für jedes Modul wiederholt werden. Sie sollten sie aber selten global anwenden (CMAKE_CXX_FLAGS), da dies unerwünschte Auswirkungen auf Drittanbieterabhängigkeiten in Ihrem Projekt haben kann. In solchen Fällen können die Flags auf Verzeichnisebene (add_compile_options) angewendet werden.

Für eine kleine Gruppe von Compiler-Flags können diese auch in der Datei „build.gradle“ mit cppFlags oder ähnlichen Eigenschaften festgelegt werden. Das sollten Sie nicht tun. Die von Gradle an CMake übergebenen Flags haben überraschende Prioritäten. In einigen Fällen überschreiben die Flags die Flags, die implizit von der Implementierung übergeben werden und zum Erstellen von Android-Code erforderlich sind. Das CMake-Verhalten sollte immer direkt in CMake verwaltet werden. Wenn Sie Compiler-Flags pro AGP buildType steuern müssen, lesen Sie den Hilfeartikel Mit AGP-Buildtypen in CMake arbeiten.

Mit AGP-Buildtypen in CMake arbeiten

Wenn Sie das CMake-Verhalten an eine benutzerdefinierte Gradle-buildType anpassen möchten, verwenden Sie diesen Buildtyp, um ein zusätzliches CMake-Flag (kein Compiler-Flag) zu übergeben, das von Ihren CMake-Build-Scripts gelesen werden kann. Beispiel: Sie haben „free“- und „premium“-Build-Varianten, die von Ihrer build.gradle.kts gesteuert werden, und müssen diese Daten an CMake übergeben:

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

Fügen Sie dann in CMakeLists.txt Folgendes hinzu:

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

Den Namen der Variablen können Sie selbst bestimmen. Achten Sie aber darauf, nichts mit dem Präfix ANDROID_, APP_ oder CMAKE_ zu vermeiden, um Konflikte oder Verwechslungen mit vorhandenen Flags zu vermeiden.

Ein Beispiel finden Sie im NDK-Beispiel für Sanitizer.

CMake-Buildbefehl

Beim Beheben von CMake-Build-Problemen ist es hilfreich, die spezifischen Build-Argumente zu kennen, die Gradle beim Cross-Compiling für Android verwendet.

Das Android Gradle-Plug-in speichert die Build-Argumente, die für die Ausführung eines CMake-Builds für jedes Paar aus ABI und Build-Typ verwendet werden, in der build_command.txt. Diese Dateien befinden sich im folgenden Verzeichnis:

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

Das folgende Snippet zeigt ein Beispiel für die CMake-Argumente zum Erstellen einer debuggbaren Version des hello-jni-Beispiels, das auf die armeabi-v7a-Architektur ausgerichtet ist.

                    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

Vordefinierte Bibliotheken verwenden

Wenn die vorkonfigurierte Bibliothek, die Sie importieren müssen, als AAR-Datei bereitgestellt wird, folgen Sie der Anleitung in den Abhängigkeitsdokumenten von Studio, um sie zu importieren und zu verwenden. Wenn Sie AGP nicht verwenden, können Sie der Anleitung unter https://google.github.io/prefab/example-workflow.html folgen. Die Migration zu AGP ist jedoch wahrscheinlich viel einfacher.

Eine Anleitung zur Verwendung vorkonfigurierter Bibliotheken mit CMake für Bibliotheken, die nicht als AAR verteilt werden, finden Sie in der add_library-Dokumentation zu IMPORTED-Zielen im CMake-Handbuch.

Drittanbietercode erstellen

Es gibt verschiedene Möglichkeiten, Code von Drittanbietern als Teil Ihres CMake-Projekts zu erstellen. Welche Option am besten geeignet ist, hängt von Ihrer Situation ab. Die beste Option ist oft, dies gar nicht zu tun. Erstellen Sie stattdessen eine AAR-Datei für die Bibliothek und verwenden Sie diese in Ihrer Anwendung. Sie müssen diese AAR nicht unbedingt veröffentlichen. Sie kann sich innerhalb Ihres Gradle-Projekts befinden.

Wenn das nicht möglich ist, gehen Sie so vor:

  • Kopieren Sie die Drittanbieterquelle in Ihr Repository und verwenden Sie add_subdirectory, um sie zu erstellen. Dies funktioniert nur, wenn die andere Bibliothek ebenfalls mit CMake erstellt wird.
  • Definieren Sie ein ExternalProject.
  • Erstellen Sie die Bibliothek separat von Ihrem Projekt und folgen Sie der Anleitung unter Vorab erstellte Bibliotheken verwenden, um sie als vorkonfigurierte Bibliothek zu importieren.

YASM-Unterstützung in CMake

Das NDK bietet CMake-Unterstützung für das Erstellen von Assembly-Code, der in YASM geschrieben wurde und auf x86- und x86-64-Architekturen ausgeführt werden kann. YASM ist ein Open-Source-Assembler für x86- und x86-64-Architekturen, der auf dem NASM-Assembler basiert.

Wenn Sie Assemblycode mit CMake erstellen möchten, nehmen Sie die folgenden Änderungen an der CMakeLists.txt Ihres Projekts vor:

  1. Rufen Sie enable_language mit dem Wert ASM_NASM auf.
  2. Je nachdem, ob Sie eine freigegebene Bibliothek oder ein ausführbares Binärprogramm erstellen, rufen Sie add_library oder add_executable auf. Übergeben Sie in den Argumenten eine Liste von Quelldateien, die aus den .asm-Dateien für das Assembly-Programm in YASM und den .c-Dateien für die zugehörigen C-Bibliotheken oder -Funktionen besteht.

Das folgende Snippet zeigt, wie Sie CMakeLists.txt so konfigurieren können, dass ein YASM-Programm als freigegebene Bibliothek erstellt wird.

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

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

Ein Beispiel zum Erstellen eines YASM-Programms als ausführbarer Datei finden Sie im yasm-Test im NDK-Git-Repository.

Probleme melden

Wenn Sie Probleme mit dem NDK oder der CMake-Toolchain-Datei haben, melden Sie sie über den Issue-Tracker android-ndk/ndk auf GitHub. Bei Problemen mit Gradle oder dem Android Gradle-Plug-in melden Sie stattdessen einen Studio-Fehler.