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 das ExternalNativeBuild des Android-Gradle-Plug-ins oder durch direktes Aufrufen 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 Cross-Compilation 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 eine neue Implementierung der Toolchain-Datei getestet, die Verhaltensunterschiede zwischen der Verwendung der Toolchain-Datei des NDK und der integrierten CMake-Unterstützung verringern sollte. Das hat viel Arbeit erfordert (die noch nicht abgeschlossen ist), aber das Verhalten nicht verbessert. Daher verfolgen wir diesen Ansatz nicht mehr.
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, dieses Flag aus Ihrem Build zu entfernen. Die neue Toolchain-Datei hat nie die gleiche Funktionalität wie die alte Toolchain-Datei erreicht. Daher sind wahrscheinlich Verhaltensregressionen aufgetreten.
Wir raten zwar von der Verwendung der neuen Toolchain-Datei ab, es gibt aber derzeit keine Pläne, sie aus dem NDK zu entfernen. Dadurch würden Builds unterbrochen, die auf den Verhaltensunterschieden zwischen den neuen und den Legacy-Toolchain-Dateien basieren. Leider würde auch das Umbenennen der Option, um deutlich zu machen, dass „Legacy“ tatsächlich empfohlen wird, die Nutzer dieser Option beeinträchtigen. Wenn Sie die neue Toolchain-Datei 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. Stattdessen müssen Sie migrieren.
Nutzung
Gradle
Die Verwendung der CMake-Toolchain-Datei erfolgt automatisch, wenn Sie externalNativeBuild verwenden. Weitere Informationen finden Sie im Android Studio-Leitfaden C- und C++-Code zum Projekt hinzufügen.
Befehlszeile
Wenn Sie mit 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 CMake-Toolchain-Datei übergeben werden. Wenn Sie mit Gradle erstellen, fügen Sie android.defaultConfig.externalNativeBuild.cmake.arguments Argumente hinzu, wie in der Dokumentation zu ExternalNativeBuild beschrieben. Wenn Sie den Build über die Befehlszeile ausführen, übergeben Sie Argumente an CMake mit -D. Wenn Sie beispielsweise erzwingen möchten, dass armeabi-v7a nicht mit Neon-Unterstützung erstellt wird, übergeben Sie -DANDROID_ARM_NEON=FALSE.
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 erstellt jeweils nur ein Ziel pro Build. Wenn Sie auf mehr als ein Android-ABI ausrichten möchten, müssen Sie einmal pro ABI einen Build erstellen. Es wird empfohlen, für jede ABI unterschiedliche Build-Verzeichnisse zu verwenden, um Kollisionen 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 |
|---|---|
| scharf schalten | |
| Daumen | Standardverhalten. |
ANDROID_NATIVE_API_LEVEL
Alias für ANDROID_PLATFORM.
ANDROID_PLATFORM
Gibt das Mindest-API-Level an, das von der Anwendung oder Bibliothek unterstützt wird. Dieser Wert entspricht dem minSdkVersion der Anwendung.
Gradle
Wenn Sie das Android-Gradle-Plug-in verwenden, wird dieser Wert automatisch auf die minSdkVersion der Anwendung festgelegt und sollte nicht manuell festgelegt werden.
Befehlszeile
Wenn Sie CMake direkt aufrufen, wird dieser Wert standardmäßig auf die niedrigste API-Ebene festgelegt, die vom verwendeten NDK unterstützt wird. Mit NDK r20 ist dieser Wert beispielsweise standardmäßig auf API-Level 16 festgelegt.
Für diesen Parameter sind mehrere Formate zulässig:
android-$API_LEVEL$API_LEVELandroid-$API_LETTER
Im $API_LETTER-Format können Sie android-N angeben, ohne die Nummer der entsprechenden Version ermitteln zu müssen. Bei einigen Releases wurde das API-Level erhöht, ohne dass sich der Buchstabe geändert hat. Diese APIs können durch Anhängen des Suffixes -MR1 angegeben werden. API-Level 25 ist beispielsweise android-N-MR1.
ANDROID_STL
Gibt an, welche STL für diese Anwendung verwendet werden soll. Weitere Informationen finden Sie unter Unterstützung für C++-Bibliotheken. Standardmäßig wird c++_static verwendet.
| Wert | Hinweise |
|---|---|
| c++_shared | Die gemeinsam genutzte Bibliotheksvariante 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 für Ihren Build bestimmte Flags an den Compiler oder Linker übergeben müssen, lesen Sie in der CMake-Dokumentation nach, wie Sie set_target_compile_options und die zugehörigen Optionen verwenden. Im Abschnitt „Siehe auch“ unten auf der Seite finden Sie einige hilfreiche Hinweise.
Im Allgemeinen empfiehlt es sich, Compiler-Flags mit dem kleinstmöglichen verfügbaren Bereich anzuwenden. Flags, die Sie auf alle Ihre Ziele anwenden möchten (z. B. -Werror), sind umständlich, da sie pro Modul wiederholt werden müssen. Sie sollten jedoch nur selten global angewendet werden (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 Teilmenge von Compiler-Flags können sie auch in der Datei „build.gradle“ mit cppFlags oder ähnlichen Attributen festgelegt werden. Das sollten Sie nicht tun. Flags, die von Gradle an CMake übergeben werden, haben ein überraschendes Vorrangverhalten. In einigen Fällen werden Flags überschrieben, die implizit von der Implementierung übergeben werden und für das Erstellen von Android-Code erforderlich sind. Das Verhalten von CMake sollte immer direkt in CMake behandelt werden. Wenn Sie Compiler-Flags pro AGP-buildType steuern müssen, lesen Sie den Abschnitt Mit AGP-Build-Typen in CMake arbeiten.
Mit AGP-Build-Typen in CMake arbeiten
Wenn Sie das CMake-Verhalten an einen benutzerdefinierten Gradle-buildType anpassen müssen, verwenden Sie diesen Build-Typ, um ein zusätzliches CMake-Flag (kein Compiler-Flag) zu übergeben, das von Ihren CMake-Build-Skripts gelesen werden kann. Wenn Sie beispielsweise die Build-Varianten „free“ und „premium“ haben, die von Ihrer build.gradle.kts-Datei gesteuert werden, und Sie diese Daten an CMake übergeben müssen:
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 Ihrer CMakeLists.txt Folgendes hinzu:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
Sie können den Namen der Variablen selbst festlegen. Achten Sie jedoch darauf, dass Sie nichts mit dem Präfix ANDROID_, APP_ oder CMAKE_ verwenden, um Konflikte oder Verwechslungen mit vorhandenen Flags zu vermeiden.
Ein Beispiel finden Sie im NDK-Beispiel für Bereinigungsfunktionen.
CMake-Build-Befehl verstehen
Beim Debuggen 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 es zum Ausführen eines CMake-Builds für jedes ABI- und Build-Typ-Paar verwendet, in 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 debugfähigen Releaseversion des hello-jni-Beispiels für die armeabi-v7a-Architektur.
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 vorab erstellte Bibliothek, die Sie importieren müssen, als AAR verteilt wird, folgen Sie der Dokumentation zu Abhängigkeiten in Android Studio, um sie zu importieren und zu verwenden. Wenn Sie AGP nicht verwenden, können Sie https://google.github.io/prefab/example-workflow.html folgen. Es ist jedoch wahrscheinlich viel einfacher, zu AGP zu migrieren.
Eine Anleitung zur Verwendung vorgefertigter 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, Drittanbietercode als Teil Ihres CMake-Projekts zu erstellen. Welche Option am besten geeignet ist, hängt von Ihrer Situation ab. Oft ist es am besten, das gar nicht zu tun. Erstellen Sie stattdessen eine AAR für die Bibliothek und verwenden Sie diese in Ihrer Anwendung. Sie müssen dieses AAR nicht unbedingt veröffentlichen. Sie kann intern in Ihrem Gradle-Projekt sein.
Wenn das nicht möglich ist:
- Kopieren Sie die Drittanbieterquelle in Ihr Repository und verwenden Sie add_subdirectory, um sie zu erstellen. Das funktioniert nur, wenn die andere Bibliothek auch mit CMake erstellt wird.
- Definieren Sie ein ExternalProject.
- Erstellen Sie die Bibliothek separat von Ihrem Projekt und folgen Sie der Anleitung unter Vorkompilierte Bibliotheken verwenden, um sie als vorkompilierte Bibliothek zu importieren.
YASM-Unterstützung in CMake
Das NDK bietet CMake-Unterstützung für das Erstellen von in YASM geschriebenen Assembly-Code, der 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 mit CMake Assembly-Code erstellen möchten, nehmen Sie die folgenden Änderungen in der CMakeLists.txt Ihres Projekts vor:
- Rufen Sie
enable_languagemit dem WertASM_NASMauf. - Je nachdem, ob Sie eine gemeinsam genutzte Bibliothek oder eine ausführbare Binärdatei erstellen, rufen Sie
add_libraryoderadd_executableauf. Ü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 bestehen.
Das folgende Snippet zeigt, wie Sie CMakeLists.txt konfigurieren können, um ein YASM-Programm als freigegebene Bibliothek zu erstellen.
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 für das Erstellen eines YASM-Programms als ausführbare Datei finden Sie im yasm-Test im NDK-Git-Repository.
Probleme melden
Wenn Sie Probleme mit dem NDK oder der zugehörigen CMake-Toolchain-Datei haben, melden Sie sie über den android-ndk/ndk-Issue-Tracker auf GitHub. Bei Problemen mit Gradle oder dem Android Gradle-Plug-in melden Sie stattdessen einen Studio-Fehler.