Android NDK supporta l'utilizzo di CMake per compilare il codice C e C++ per la tua applicazione. Questa pagina spiega come utilizzare CMake con l'NDK tramite ExternalNativeBuild del plug-in Android per Gradle o richiamando direttamente CMake.
Il file della toolchain CMake
L'NDK supporta CMake tramite un file della toolchain. I file della toolchain sono file CMake che personalizzano il comportamento della toolchain per la cross-compilazione. Il file della toolchain utilizzato per l'NDK si trova nell'NDK in <NDK>/build/cmake/android.toolchain.cmake.
I parametri di build come ABI, minSdkVersion e così via vengono forniti nella riga di comando quando viene richiamato cmake. Per un elenco degli argomenti supportati, consulta la sezione Argomenti della toolchain.
Il file della toolchain "nuovo"
Le versioni precedenti dell'NDK hanno sperimentato una nuova implementazione del file della toolchain che riduceva le differenze di comportamento tra l'utilizzo del file della toolchain dell'NDK e l'utilizzo del supporto CMake integrato. Questo ha richiesto una quantità significativa di lavoro (che non è stato completato), ma non ha effettivamente migliorato il comportamento, quindi non stiamo più portando avanti questa iniziativa.
Il file della toolchain "nuovo" presenta regressioni di comportamento rispetto al file della toolchain "legacy". Il comportamento predefinito è il workflow consigliato. Se utilizzi -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF, ti consigliamo di rimuovere questo flag dalla build. Il nuovo file della toolchain non ha mai raggiunto la parità con il file della toolchain precedente, pertanto è probabile che si verifichino regressioni del comportamento.
Sebbene sconsigliamo di utilizzare il nuovo file della toolchain, al momento non è prevista la sua rimozione dall'NDK. In questo modo, le build che si basano sulle differenze di comportamento tra i file della toolchain nuovi e legacy verrebbero interrotte e, sfortunatamente, anche la ridenominazione dell'opzione per chiarire che "legacy" è effettivamente consigliato interromperebbe gli utenti di questa opzione. Se utilizzi il nuovo file della toolchain senza problemi, non è necessario eseguire la migrazione, ma tieni presente che eventuali bug segnalati in relazione al comportamento del nuovo file della toolchain probabilmente non verranno corretti e dovrai eseguire la migrazione.
Utilizzo
Gradle
L'utilizzo del file della toolchain CMake è automatico quando utilizzi
externalNativeBuild. Per saperne di più, consulta la guida Aggiungi codice C e C++ al tuo progetto di Android Studio.
Riga di comando
Quando crei con CMake al di fuori di Gradle, il file della toolchain e i relativi argomenti devono essere passati a CMake. Ad esempio:
$ cmake \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$MINSDKVERSION \
$OTHER_ARGS
Argomenti della toolchain
Al file della toolchain CMake possono essere passati i seguenti argomenti. Se la build viene eseguita con Gradle, aggiungi gli argomenti a android.defaultConfig.externalNativeBuild.cmake.arguments come descritto nella documentazione di ExternalNativeBuild. Se la build viene eseguita dalla riga di comando, passa gli argomenti a CMake con -D. Ad esempio, per forzare armeabi-v7a a non eseguire la build con il supporto di Neon, passa -DANDROID_ARM_NEON=FALSE.
ANDROID_ABI
L'ABI di destinazione. Per informazioni sulle ABI supportate, consulta ABI Android.
Gradle
Gradle fornisce questo argomento automaticamente. Non impostare esplicitamente questo argomento nel file build.gradle. Per controllare le ABI target di Gradle,
utilizza abiFilters come descritto in ABI Android.
Riga di comando
CMake crea un singolo target per build. Per scegliere come target più di un'ABI Android, devi eseguire una build per ogni ABI. Per evitare collisioni tra le build, è consigliabile utilizzare directory di build diverse per ogni ABI.
| Valore | Note |
|---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
Uguale a armeabi-v7a. |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
Specifica se generare istruzioni ARM o Thumb per armeabi-v7a. Non ha alcun effetto per altre ABI. Per saperne di più, consulta la documentazione relativa alle ABI Android.
| Valore | Note |
|---|---|
| braccio | |
| pollice | Comportamento predefinito. |
ANDROID_NATIVE_API_LEVEL
Alias per ANDROID_PLATFORM.
ANDROID_PLATFORM
Specifica il livello API minimo supportato dall'applicazione o dalla libreria. Questo valore corrisponde al minSdkVersion dell'applicazione.
Gradle
Quando utilizzi il plug-in Android per Gradle, questo valore viene impostato automaticamente in modo da corrispondere al valore minSdkVersion dell'applicazione e non deve essere impostato manualmente.
Riga di comando
Quando richiami CMake direttamente, questo valore viene impostato per impostazione predefinita sul livello API più basso supportato dall'NDK in uso. Ad esempio, con NDK r20 questo valore viene impostato per impostazione predefinita sul livello API 16.
Per questo parametro sono accettati più formati:
android-$API_LEVEL$API_LEVELandroid-$API_LETTER
Il formato $API_LETTER ti consente di specificare android-N senza dover determinare il numero associato alla release. Tieni presente che alcune release hanno ricevuto un aumento dell'API senza un aumento della lettera. Queste API possono essere specificate aggiungendo il suffisso -MR1. Ad esempio, il livello API 25 è android-N-MR1.
ANDROID_STL
Specifica quale STL utilizzare per questa applicazione. Per saperne di più, consulta Supporto delle librerie C++. Per impostazione predefinita, verrà utilizzato c++_static.
| Valore | Note |
|---|---|
| c++_shared | La variante della libreria condivisa di libc++. |
| c++_static | La variante della libreria statica di libc++. |
| nessuno | Nessun supporto della libreria standard C++. |
| di infotainment | STL di sistema |
Gestisci i flag del compilatore
Se devi passare flag specifici al compilatore o al linker per la tua build, consulta la documentazione di CMake per set_target_compile_options e la relativa famiglia di opzioni. La sezione "Vedi anche" in fondo alla pagina contiene alcuni indizi utili.
In generale, la best practice consiste nell'applicare i flag del compilatore all'ambito più ristretto disponibile. I flag che vuoi applicare a tutte le tue destinazioni (come -Werror) sono scomodi da ripetere per modulo, ma raramente devono essere applicati a livello globale (CMAKE_CXX_FLAGS), in quanto potrebbero avere effetti indesiderati sulle dipendenze di terze parti nel tuo progetto. In questi casi, i flag possono essere applicati a livello di directory (add_compile_options).
Per un sottoinsieme limitato di flag del compilatore, questi possono essere impostati anche nel file build.gradle utilizzando cppFlags o proprietà simili. Non dovresti farlo. I flag passati a CMake da Gradle avranno comportamenti di precedenza sorprendenti, in alcuni casi eseguendo l'override dei flag passati implicitamente dall'implementazione, che sono necessari per la creazione del codice Android. È sempre preferibile gestire il comportamento di CMake direttamente in CMake. Se devi controllare i flag del compilatore per AGP buildType, consulta Utilizza i tipi di build AGP in CMake.
Utilizza i tipi di build AGP in CMake
Se devi adattare il comportamento di CMake a un buildType Gradle personalizzato, utilizza questo tipo di build per passare un flag CMake aggiuntivo (non un flag del compilatore) che gli script di build CMake possono leggere. Ad esempio, se hai varianti di compilazione "senza costi" e "premium" controllate dal tuo build.gradle.kts e devi passare questi dati a CMake:
android {
buildTypes {
free {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
}
}
}
premium {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
}
}
}
}
}
Poi, nel file CMakeLists.txt:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
Il nome della variabile è a tua discrezione, ma assicurati di evitare qualsiasi prefisso ANDROID_, APP_ o CMAKE_ per evitare conflitti o confusione con i flag esistenti.
Per un esempio, consulta l'esempio di NDK per i sanitizer.
Informazioni sul comando di build CMake
Quando esegui il debug dei problemi di build di CMake, è utile conoscere gli argomenti di build specifici utilizzati da Gradle durante la cross-compilazione per Android.
Il plug-in Android per Gradle salva gli argomenti di build che utilizza per eseguire una build CMake per ogni coppia di ABI e tipo di compilazione in build_command.txt. Questi file si trovano nella seguente directory:
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
Il seguente snippet mostra un esempio degli argomenti CMake per creare una release abilitata al debug dell'esempio hello-jni che ha come target l'architettura armeabi-v7a.
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
Utilizza librerie predefinite
Se la libreria predefinita che devi importare è distribuita come AAR, segui la documentazione sulle dipendenze di Studio per importarla e utilizzarla. Se non utilizzi AGP, puoi seguire la procedura descritta all'indirizzo https://google.github.io/prefab/example-workflow.html, ma è probabile che sia molto più semplice eseguire la migrazione ad AGP.
Per le librerie non distribuite come AAR, le istruzioni sull'utilizzo di librerie predefinite con CMake sono disponibili nella documentazione di add_library relativa alle destinazioni IMPORTED nel manuale di CMake.
Creazione di codice di terze parti
Esistono diversi modi per creare codice di terze parti nell'ambito del tuo progetto CMake e l'opzione migliore dipende dalla tua situazione. Spesso l'opzione migliore è non farlo affatto. Invece, crea un AAR per la libreria e utilizzalo nella tua applicazione. Non è necessario pubblicare l'AAR. Può essere interno al progetto Gradle.
Se non è possibile:
- Copia l'origine di terze parti nel tuo repository e utilizza add_subdirectory per crearla. Questo funziona solo se anche l'altra libreria è creata con CMake.
- Definisci un ExternalProject.
- Crea la libreria separatamente dal progetto e segui le istruzioni riportate in Utilizza librerie predefinite per importarla come predefinita.
Supporto di YASM in CMake
NDK fornisce il supporto di CMake per la creazione di codice assembly scritto in YASM da eseguire su architetture x86 e x86-64. YASM è un assemblatore open source per architetture x86 e x86-64, basato sull'assemblatore NASM.
Per creare il codice assembly con CMake, apporta le seguenti modifiche al file CMakeLists.txt del progetto:
- Chiama
enable_languagecon il valore impostato suASM_NASM. - A seconda che tu stia creando una libreria condivisa o un file binario eseguibile, chiama
add_libraryoadd_executable. Negli argomenti, passa un elenco di file di origine composto dai file.asmper il programma assembly in YASM e dai file.cper le librerie o le funzioni C associate.
Il seguente snippet mostra come configurare CMakeLists.txt per creare un programma YASM come libreria condivisa.
cmake_minimum_required(VERSION 3.6.0)
enable_language(ASM_NASM)
add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)
Per un esempio di come creare un programma YASM come eseguibile, consulta il test yasm nel repository Git dell'NDK.
Segnalare problemi
Se riscontri problemi con l'NDK o il relativo file della toolchain CMake, segnalali tramite l'Issue Tracker android-ndk/ndk su GitHub. Per problemi relativi a Gradle o al plug-in Android per Gradle, segnala un bug di Studio.