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:
- Rufen Sie
enable_language
mit dem WertASM_NASM
auf. - Je nachdem, ob Sie eine freigegebene Bibliothek oder ein ausführbares Binärprogramm erstellen, rufen Sie
add_library
oderadd_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.