Wenn Sie CMake oder ndk-build nicht verwenden, aber den C/C++-Build des Android-Gradle-Plug-ins (AGP) und Android Studio vollständig integrieren möchten, können Sie ein benutzerdefiniertes C/C++-Build-System erstellen. Erstellen Sie dazu ein Shell-Script, das Build-Informationen im Build-Dateiformat Ninja schreibt.
Android Studio und AGP wurden experimentelle Unterstützung für benutzerdefinierte C/C++-Build-Systeme hinzugefügt. Diese Funktion ist ab Android Studio Dolphin | 2021.3.1 Canary 4 verfügbar.
Übersicht
Ein gängiges Muster für C/C++-Projekte, insbesondere solche, die auf mehrere Plattformen abzielen, besteht darin, Projekte für jede dieser Plattformen aus einer zugrunde liegenden Darstellung zu generieren.
Ein auffälliges Beispiel für dieses Muster ist CMake. CMake kann Projekte für Android, iOS und andere Plattformen aus einer einzigen zugrunde liegenden Darstellung generieren, die in der Datei CMakeLists.txt
gespeichert ist.
CMake wird zwar direkt von AGP unterstützt, es gibt jedoch andere Projektgeneratoren, die nicht direkt unterstützt werden:
Diese Arten von Projektgeneratoren unterstützen entweder Ninja als Back-End-Darstellung des C/C++-Builds oder können so angepasst werden, dass Ninja als Back-End-Darstellung generiert wird.
Bei korrekter Konfiguration bietet ein AGP-Projekt mit einem integrierten C/C++-Projektsystemgenerator folgende Möglichkeiten:
Mit der Befehlszeile und Android Studio entwickeln
Sie können Quellen mit vollständiger Sprachunterstützung in Android Studio bearbeiten, z. B. Go-to-Definition.
Verwenden Sie Android Studio-Debugger, um Fehler in nativen und gemischten Prozessen zu beheben.
Build für die Verwendung eines benutzerdefinierten C/C++-Build-Konfigurationsskripts ändern
In diesem Abschnitt werden die Schritte zur Verwendung eines benutzerdefinierten C/C++-Build-Konfigurationsskripts von AGP beschrieben.
Schritt 1: Datei build.gradle
auf Modulebene ändern, um auf ein Konfigurationsskript zu verweisen
Um die Ninja-Unterstützung in AGP zu aktivieren, konfigurieren Sie experimentalProperties
in der Datei build.gradle
auf Modulebene:
android {
defaultConfig {
externalNativeBuild {
experimentalProperties["ninja.abiFilters"] = [ "x86", "arm64-v8a" ]
experimentalProperties["ninja.path"] = "source-file-list.txt"
experimentalProperties["ninja.configure"] = "configure-ninja"
experimentalProperties["ninja.arguments"] = [
"\${ndk.moduleMakeFile}",
"--variant=\${ndk.variantName}",
"--abi=Android-\${ndk.abi}",
"--configuration-dir=\${ndk.configurationDir}",
"--ndk-version=\${ndk.moduleNdkVersion}",
"--min-sdk-version=\${ndk.minSdkVersion}"
]
}
}
Die Attribute werden von AGP so interpretiert:
ninja.abiFilters
ist eine Liste von ABIs, die erstellt werden sollen. Gültige Werte sind:x86
,x86-64
,armeabi-v7a
undarm64-v8a
.ninja.path
ist ein Pfad zu einer C/C++-Projektdatei. Sie haben ein beliebiges Format für diese Datei. Änderungen an dieser Datei lösen eine Eingabeaufforderung zur Gradle-Synchronisierung in Android Studio aus.ninja.configure
ist ein Pfad zu einer Skriptdatei, die von Gradle ausgeführt wird, wenn das C/C++-Projekt konfiguriert werden muss. Ein Projekt wird beim ersten Build, während einer Gradle-Synchronisierung in Android Studio oder wenn sich eine der Konfigurationsskripteingaben ändert.ninja.arguments
ist eine Liste von Argumenten, die an das von „ninja.configure“ definierte Skript übergeben werden. Elemente in dieser Liste können auf eine Reihe von Makros verweisen, deren Werte vom aktuellen Konfigurationskontext in AGP abhängen:${ndk.moduleMakeFile}
ist der vollständige Pfad zur Dateininja.configure
. In dem Beispiel wäre das alsoC:\path\to\configure-ninja.bat
.${ndk.variantName}
ist der Name der aktuellen AGP-Variante, die gerade erstellt wird. Beispiel: „Debug“ oder „Release“.${ndk.abi}
ist der Name der aktuellen AGP-ABIs, die gerade erstellt wird. Zum Beispiel:x86
oderarm64-v8a
.
${ndk.buildRoot}
ist der Name eines von AGP generierten Ordners, in den das Skript seine Ausgabe schreibt. Weitere Informationen dazu finden Sie in Schritt 2: Konfigurationsskript erstellen.${ndk.ndkVersion}
ist die Version des zu verwendenden NDK. Das ist in der Regel der Wert, der in der Dateibuild.gradle
an android.ndkVersion übergeben wird, oder ein Standardwert, wenn keiner vorhanden ist.${ndk.minPlatform}
ist die von AGP angeforderte Mindestzielplattform für Android.
ninja.targets
ist eine Liste der spezifischen Ninja-Ziele, die erstellt werden sollen.
Schritt 2: Konfigurationsskript erstellen
Die Mindestverantwortung des Konfigurationsskripts (im vorherigen Beispiel configure-ninja.bat
) besteht darin, eine build.ninja
-Datei zu generieren, die, wenn sie mit Ninja erstellt wird, alle nativen Ausgaben des Projekts kompiliert und verknüpft. In der Regel sind dies .o
(Objekt), .a
(Archiv) und .so
(Freigegebene Objekte).
Das Konfigurationsskript kann die build.ninja
-Datei je nach Ihren Anforderungen an zwei verschiedene Stellen schreiben.
Wenn AGP einen Speicherort auswählen darf, schreibt das Konfigurationsskript
build.ninja
an den im${ndk.buildRoot}
-Makro festgelegten Speicherort.Wenn das Konfigurationsskript den Speicherort der Datei
build.ninja
auswählen muss, schreibt es auch eine Datei namensbuild.ninja.txt
an den im${ndk.buildRoot}
-Makro festgelegten Speicherort. Diese Datei enthält den vollständigen Pfad zur Dateibuild.ninja
, die vom Konfigurationsskript geschrieben wurde.
Struktur der Datei build.ninja
Im Allgemeinen funktionieren die meisten Strukturen, die einen Android-C/C++-Build korrekt darstellen. Die wichtigsten Elemente, die für AGP und Android Studio benötigt werden, sind:
Die Liste der C/C++-Quelldateien zusammen mit den Flags, die Clang zum Kompilieren benötigt.
Die Liste der Ausgabebibliotheken. Dabei handelt es sich in der Regel um
.so
-Dateien (freigegebene Objekte), es kann sich aber auch um.a
-Dateien (Archive) oder ausführbare Dateien (ohne Erweiterung) handeln.
Beispiele zum Generieren einer build.ninja
-Datei finden Sie in der Ausgabe von CMake, wenn der build.ninja
-Generator verwendet wird.
Hier ist ein Beispiel für eine minimale build.ninja
-Vorlage.
rule COMPILE
command = /path/to/ndk/clang -c $in -o $out {other flags}
rule LINK
command = /path/to/ndk/clang $in -o $out {other flags}
build source.o : COMPILE source.cpp
build lib.so : LINK source.o
Best Practices
Zusätzlich zu den Anforderungen (Liste der Quelldateien und Ausgabebibliotheken) finden Sie hier einige empfohlene Best Practices.
Benannte Ausgaben mit phony
-Regeln deklarieren
Wenn möglich, empfiehlt es sich, in der build.ninja
-Struktur phony
-Regeln zu verwenden, um Build-Ausgaben in menschenlesbaren Namen zu benennen. Wenn Sie beispielsweise eine Ausgabe mit dem Namen c:/path/to/lib.so
haben, können Sie ihr einen visuell lesbaren Namen geben:
build curl: phony /path/to/lib.so
Der Vorteil dabei ist, dass Sie diesen Namen als Build-Ziel in der Datei build.gradle
angeben können. Beispiel:
android {
defaultConfig {
externalNativeBuild {
...
experimentalProperties["ninja.targets"] = [ "curl" ]
Ziel "Alle" angeben
Wenn Sie ein all
-Ziel angeben, werden dies der Standardsatz von Bibliotheken, die von AGP erstellt werden, wenn in der build.gradle
-Datei keine Ziele explizit angegeben sind.
rule COMPILE
command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
command = /path/to/ndk/clang $in -o $out {other flags}
build foo.o : COMPILE foo.cpp
build bar.o : COMPILE bar.cpp
build libfoo.so : LINK foo.o
build libbar.so : LINK bar.o
build all: phony libfoo.so libbar.so
Alternative Build-Methode angeben (optional)
Ein komplexerer Anwendungsfall ist das Einbinden eines vorhandenen Build-Systems, das nicht auf Ninja basiert. In diesem Fall müssen Sie trotzdem alle Quellen mit ihren Flags zusammen mit den Ausgabebibliotheken darstellen, damit Android Studio die richtigen Sprachdienstfunktionen wie automatische Vervollständigung und Go-to-Definition anzeigen kann. Sie möchten jedoch, dass AGP während des eigentlichen Builds auf das zugrunde liegende Build-System zurückgreift.
Um dies zu erreichen, können Sie eine Ninja-Build-Ausgabe mit der spezifischen Erweiterung .passthrough
verwenden.
Ein konkreteres Beispiel: Nehmen wir an, Sie möchten einen MSBuild verpacken. Ihr Konfigurationsskript würde wie gewohnt den build.ninja
generieren, fügt jedoch auch ein Passthrough-Ziel hinzu, das definiert, wie AGP MSBuild aufruft.
rule COMPILE
command = /path/to/ndk/clang $in -o $out {other flags}
rule LINK
command = /path/to/ndk/clang $in -o $out {other flags}
rule MBSUILD_CURL
command = /path/to/msbuild {flags to build curl with MSBuild}
build source.o : COMPILE source.cpp
build lib.so : LINK source.o
build curl : phony lib.so
build curl.passthrough : MBSUILD_CURL
Feedback geben
Diese Funktion befindet sich noch in der Testphase. Daher würden wir uns über Feedback freuen. Sie können über die folgenden Kanäle Feedback geben:
Hier kannst du allgemeines Feedback zu diesem Programmfehler hinzufügen.
Wenn Sie einen Fehler melden möchten, öffnen Sie Android Studio und klicken Sie auf Hilfe > Feedback geben. Verweisen Sie auf „Benutzerdefinierte C/C++-Build-Systeme“, um auf den Fehler aufmerksam zu machen.
Wenn Sie einen Fehler melden möchten, obwohl Android Studio nicht installiert ist, verwenden Sie diese Vorlage.