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 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 Cross-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 beim Aufrufen von cmake in der Befehlszeile angegeben. 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, die die Verhaltensunterschiede zwischen der Verwendung der Toolchain-Datei des NDK und der Verwendung der integrierten CMake-Unterstützung verringern sollte. Dies erforderte einen erheblichen Arbeitsaufwand (der noch nicht abgeschlossen ist), verbesserte das Verhalten jedoch nicht, sodass wir dies nicht weiter verfolgen.
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 Parität mit der alten Toolchain-Datei erreicht, sodass es wahrscheinlich zu Verhaltensregressionen kommt.
Wir raten zwar von der Verwendung der neuen Toolchain-Datei ab, es ist jedoch derzeit nicht geplant, sie aus dem NDK zu entfernen. Andernfalls würden Builds fehlschlagen, die auf den Verhaltensunterschieden zwischen den neuen und den Legacy-Toolchain-Dateien basieren. Leider würde das Umbenennen der Option, um deutlich zu machen, dass „Legacy“ tatsächlich empfohlen wird, auch Nutzer dieser Option beeinträchtigen. Wenn Sie die neue Toolchain-Datei verwenden und damit zufrieden sind, 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 CMake-Toolchain-Datei wird automatisch verwendet, wenn Sie externalNativeBuild verwenden. Weitere Informationen finden Sie im Android Studio-Leitfaden C- und C++-Code zu Ihrem 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 über die Befehlszeile erstellen, übergeben Sie Argumente mit -D an CMake. 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 pro Build ein einzelnes Ziel. Wenn Sie mehr als eine Android-Binärschnittstelle als Ziel festlegen möchten, müssen Sie für jede Binärschnittstelle einen Build 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 |
|---|---|
| scharf schalten | |
| thumb | Standardverhalten. |
ANDROID_NATIVE_API_LEVEL
Alias für ANDROID_PLATFORM.
ANDROID_PLATFORM
Gibt das von der Anwendung oder Bibliothek unterstützte Mindest-API-Level an. Dieser Wert entspricht der minSdkVersion der Anwendung.
Gradle
Wenn Sie das Android Gradle-Plug-in verwenden, wird dieser Wert automatisch so festgelegt, dass er mit der minSdkVersion der Anwendung übereinstimmt. Er sollte nicht manuell festgelegt werden.
Befehlszeile
Wenn CMake direkt aufgerufen wird, wird dieser Wert standardmäßig auf das niedrigste API-Level festgelegt, das vom verwendeten NDK unterstützt wird. Bei 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
Mit dem Format $API_LETTER können Sie 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 eine Erhöhung des Buchstabens erhalten haben. 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 Variante der gemeinsam genutzten Bibliothek von libc++. |
| c++_static | Die statische Bibliotheksvariante von libc++. |
| Keine | Keine Unterstützung für die C++-Standardbibliothek. |
| Infotainmentsystem | Die System-STL |
Compiler-Flags verwalten
Wenn Sie bestimmte Flags für Ihren Build an den Compiler oder Linker übergeben müssen, lesen Sie die CMake-Dokumentation zu set_target_compile_options und den zugehörigen Optionen. Der Abschnitt „Siehe auch“ unten auf dieser Seite enthält einige hilfreiche Hinweise.
Im Allgemeinen empfiehlt es sich, Compiler-Flags auf den kleinstmöglichen verfügbaren Bereich anzuwenden. Flags, die Sie auf alle Ihre Ziele anwenden möchten (z. B. -Werror), sind umständlich pro Modul zu wiederholen, sollten aber dennoch selten global (CMAKE_CXX_FLAGS) angewendet werden, 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 Prioritätsverhalten. In einigen Fällen werden Flags überschrieben, die implizit von der Implementierung übergeben werden und für das Erstellen von Android-Code erforderlich sind. Es ist immer besser, das CMake-Verhalten direkt in CMake zu verarbeiten. Wenn Sie Compiler-Flags pro AGP-buildType steuern müssen, lesen Sie den Hilfeartikel 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 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-Datei Folgendes hinzu:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
Der Name der Variablen ist Ihnen überlassen. Vermeiden Sie jedoch Präfixe wie ANDROID_, APP_ oder CMAKE_, um Konflikte oder Verwechslungen mit vorhandenen Flags zu vermeiden.
Ein Beispiel findest du im NDK-Beispiel für Sanitizer.
CMake-Build-Befehl verstehen
Beim Debuggen von CMake-Build-Problemen ist es hilfreich, die spezifischen Build-Argumente zu kennen, die Gradle beim Cross-Kompilieren 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 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, mit denen ein debugfähiger Release des hello-jni-Beispiels für die armeabi-v7a-Architektur erstellt wird.
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 vordefinierte Bibliothek, die Sie importieren möchten, als AAR verteilt wird, folgen Sie der Dokumentation zu Abhängigkeiten in Android Studio, um sie zu importieren und zu verwenden. Wenn Sie das AGP nicht verwenden, können Sie der Anleitung unter https://google.github.io/prefab/example-workflow.html folgen. Es ist jedoch wahrscheinlich viel einfacher, zum AGP zu migrieren.
Eine Anleitung zur Verwendung vordefinierter 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. Die beste Option ist oft, dies überhaupt nicht zu tun. Erstellen Sie stattdessen ein AAR für die Bibliothek und verwenden Sie es 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 ebenfalls mit CMake erstellt wurde.
- Definieren Sie ein ExternalProject.
- Erstellen Sie die Bibliothek separat von Ihrem Projekt und folgen Sie der Anleitung unter Vordefinierte Bibliotheken verwenden, um sie als vordefinierte Bibliothek zu importieren.
YASM-Unterstützung in CMake
Das NDK bietet CMake-Unterstützung für die Erstellung von Assemblercode, der in YASM geschrieben wurde, um auf x86- und x86-64-Architekturen ausgeführt zu werden. YASM ist ein Open-Source-Assembler für x86- und x86-64-Architekturen, der auf dem NASM-Assembler basiert.
Wenn Sie Assemblercode mit CMake erstellen möchten, nehmen Sie die folgenden Änderungen in der Datei CMakeLists.txt Ihres Projekts vor:
- Rufen Sie
enable_languageauf und legen Sie den Wert aufASM_NASMfest. - 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 besteht.
Das folgende Snippet zeigt, wie Sie Ihre CMakeLists.txt konfigurieren können, um ein YASM-Programm als gemeinsam genutzte 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 die Erstellung eines YASM-Programms als ausführbare Datei finden Sie im yasm-Test im NDK-Git-Repository.
Probleme melden
Wenn Probleme mit dem NDK oder der CMake-Toolchain-Datei auftreten, melde 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.