Android NDK prend en charge l'utilisation de CMake pour compiler du code C et C++ pour votre application. Cette page explique comment utiliser CMake avec le NDK via le ExternalNativeBuild
du plug-in Android Gradle ou lorsque vous appelez directement CMake.
Fichier de chaîne d'outils CMake
Le NDK est compatible avec CMake grâce à un fichier de chaîne d'outils. Les fichiers de chaîne d'outils sont des fichiers CMake qui personnalisent le comportement de la chaîne d'outils pour la compilation croisée. Le fichier de chaîne d'outils utilisé pour le NDK se trouve dans celui-ci, à l'emplacement <NDK>/build/cmake/android.toolchain.cmake
.
Les paramètres de compilation comme ABI, minSdkVersion
, etc. sont fournis dans la ligne de commande lors de l'appel de cmake
. Pour obtenir la liste des arguments acceptés, consultez la section Arguments de la chaîne d'outils.
Le "nouveau" fichier de chaîne d'outils
Les NDK précédents ont testé une nouvelle implémentation du fichier de chaîne d'outils qui réduirait les différences de comportement entre l'utilisation du fichier de chaîne d'outils du NDK et en utilisant la prise en charge intégrée de CMake. Cela a nécessité un montant important de travail (qui n’a pas été achevé), mais n’a pas réellement amélioré le comportement, donc nous n'allons plus poursuivre dans cette voie.
Le "nouveau" le fichier de chaîne d'outils présente des régressions de comportement par rapport à l'ancien
fichier de chaîne d'outils. Le comportement par défaut est le workflow recommandé. Si vous utilisez
à l'aide de -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF
, nous vous recommandons de supprimer cet indicateur
de votre build. Le nouveau fichier de chaîne d'outils n'a jamais atteint la parité avec l'ancienne
fichier de chaîne d'outils. Il y a donc probablement des régressions de comportement.
Bien que nous vous recommandions de ne pas utiliser le nouveau fichier de chaîne d'outils, il n'existe actuellement prévoit de le supprimer du NDK. Cela endommagerait les builds qui dépendent du les différences de comportement entre les nouveaux fichiers et les anciens fichiers de chaîne d'outils ; nous renommons malheureusement l'option pour indiquer clairement que le terme "ancien" est en fait la recommandation empêcherait aussi les utilisateurs de bénéficier de cette option. Si vous utilisez la classe un nouveau fichier de chaîne d'outils que vous n'avez pas besoin de migrer, mais sachez que tous les bugs signalés par rapport au nouveau comportement des fichiers de chaîne d'outils ne sera probablement pas corrigé, et que vous devez migrer.
Utilisation
Gradle
L'utilisation du fichier de chaîne d'outils CMake est automatique lorsque vous utilisez externalNativeBuild
. Pour en savoir plus, consultez le guide Ajouter du code C et C++ à votre projet d'Android Studio.
Ligne de commande
Lorsque vous compilez votre projet avec CMake en dehors de Gradle, le fichier de chaîne d'outils et ses arguments doivent être transmis à CMake. Exemple :
$ cmake \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$MINSDKVERSION \
$OTHER_ARGS
Arguments de la chaîne d'outils
Les arguments suivants peuvent être transmis au fichier de chaîne d'outils CMake. Si vous compilez votre projet avec Gradle, ajoutez des arguments à android.defaultConfig.externalNativeBuild.cmake.arguments
, comme décrit dans la documentation sur ExternalNativeBuild. Si vous compilez votre projet à partir de la ligne de commande, transmettez les arguments à CMake avec -D
. Par exemple, pour forcer armeabi-v7a à ne pas compiler avec Neon,
support, transmettez -DANDROID_ARM_NEON=FALSE
.
ANDROID_ABI
ABI cible. Pour en savoir plus sur les ABI compatibles, consultez ABI Android.
Gradle
Gradle fournit automatiquement cet argument. Ne le définissez pas de manière explicite dans votre fichier build.gradle
. Pour contrôler les ABI ciblées par Gradle, utilisez abiFilters
comme décrit dans ABI Android.
Ligne de commande
La compilation CMake prévoit une seule cible par build. Pour cibler plusieurs ABI Android, vous devez effectuer une compilation pour chaque ABI. Il est recommandé d'utiliser des répertoires de compilation différents pour chaque ABI afin d'éviter les conflits entre les builds.
Valeur | Remarques |
---|---|
armeabi-v7a |
|
armeabi-v7a with NEON |
Équivalent à armeabi-v7a . |
arm64-v8a |
|
x86 |
|
x86_64 |
ANDROID_ARM_MODE
Indique s'il faut générer des instructions arm ou thumb pour armeabi-v7a. N'a aucun effet pour les autres ABI. Pour en savoir plus, consultez la documentation sur les ABI Android.
Valeur | Remarques |
---|---|
arm | |
thumb | Comportement par défaut |
ANDROID_NATIVE_API_LEVEL
Alias pour ANDROID_PLATFORM
ANDROID_PLATFORM
Spécifie le niveau d'API minimal accepté par l'application ou la bibliothèque. Cette valeur correspond à la minSdkVersion
de l'application.
Gradle
Lorsque vous utilisez le plug-in Android Gradle, cette valeur est définie automatiquement pour correspondre à la minSdkVersion
de l'application et ne doit pas être définie manuellement.
Ligne de commande
Lorsque vous appelez directement CMake, cette valeur est définie par défaut sur le niveau d'API le plus bas accepté par le NDK utilisé. Par exemple, avec NDK r20, cette valeur est définie par défaut sur le niveau d'API 16.
Ce paramètre accepte plusieurs formats :
android-$API_LEVEL
$API_LEVEL
android-$API_LETTER
Le format $API_LETTER
vous permet de spécifier android-N
sans avoir à déterminer le numéro associé à cette version. Notez que certaines versions passent à un niveau d'API supérieur sans incrémentation de la lettre. Vous pouvez spécifier ces API en ajoutant le suffixe -MR1
. Par exemple, le niveau d'API 25 correspond à android-N-MR1
.
ANDROID_STL
Spécifie la STL à utiliser pour cette application. Pour en savoir plus, consultez Compatibilité avec les bibliothèques C++. La bibliothèque utilisée par défaut est c++_static
.
Valeur | Remarques |
---|---|
c++_shared | Variante de bibliothèque partagée de libc++ |
c++_static | Variante de bibliothèque statique de libc++ |
aucune | Incompatible avec les bibliothèques standards C++. |
système | STL système |
Gérer les indicateurs de compilation
Si vous devez transmettre des indicateurs spécifiques au compilateur ou à l'éditeur de liens pour votre build, reportez-vous à la documentation CMake pour en savoir plus sur set_target_compile_options et sur famille d'options connexe. La section "Voir aussi" en bas de cette page contient quelques indices utiles.
En général, il est recommandé d'appliquer les indicateurs de compilation
le niveau d'accès disponible. Les indicateurs que vous souhaitez appliquer à toutes vos cibles (comme
-Werror
) ne sont pas pratiques à répéter par module, mais doivent rarement l'être
appliquées à l'échelle mondiale (CMAKE_CXX_FLAGS
), car elles peuvent avoir des effets indésirables
des dépendances tierces dans votre projet. Dans ce cas, les indicateurs peuvent être
appliquée au niveau du répertoire (add_compile_options
).
Pour un sous-ensemble restreint d'indicateurs de compilation, vous pouvez également les définir dans votre fichier build.gradle
à l'aide de cppFlags
ou de propriétés similaires. Nous vous déconseillons de le faire. Indicateurs
transmis à CMake depuis Gradle auront des comportements de priorité surprenants, dans certains cas
qui remplacent les options transmises implicitement par l'implémentation
nécessaire pour compiler du code Android. Préférez toujours gérer le comportement CMake
directement dans CMake. Si vous devez contrôler des indicateurs de compilation par buildType
AGP,
consultez Utiliser des types de compilation AGP dans CMake.
Utiliser les types de compilation AGP dans CMake
Si vous devez adapter le comportement de CMake à un buildType
Gradle personnalisé, utilisez cet outil
type de compilation pour transmettre un indicateur CMake supplémentaire (pas un indicateur de compilation) que votre
Les scripts de compilation CMake sont lisibles. Par exemple, si vous avez le mot "sans frais" et "premium"
des variantes de compilation contrôlées par votre build.gradle.kts et que vous devez transmettre
ces données à CMake:
android {
buildTypes {
free {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
}
}
}
premium {
externalNativeBuild {
cmake {
arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
}
}
}
}
}
Puis, dans votre fichier CMakeLists.txt:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
C'est vous qui choisissez le nom de la variable, mais veillez à éviter tout élément comportant
Préfixe ANDROID_
, APP_
ou CMAKE_
pour éviter les conflits ou la confusion avec
options existantes.
Consultez l'exemple de NDK Sanitizers pour voir un exemple.
Comprendre la commande de compilation CMake
Lors du débogage des problèmes de compilation CMake, il est utile de connaître les arguments de compilation spécifiques utilisés par Gradle lors de la compilation croisée pour Android.
Le plug-in Android Gradle enregistre les arguments utilisés pour exécuter une compilation CMake pour chaque association entre une ABI et un type de compilation dans le fichier build_command.txt
. Ces fichiers se trouvent dans le répertoire suivant :
<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/
L'extrait de code suivant illustre l'utilisation d'arguments CMake pour créer une version débogable de l'exemple hello-jni
ciblant l'architecture 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
Utiliser des bibliothèques prédéfinies
Si la bibliothèque prédéfinie que vous devez importer est distribuée en tant que fichiers AAR, suivez la documentation sur les dépendances de Studio pour les importer et les utiliser. Si vous n'utilisez pas AGP, vous pouvez suivre les instructions de la page https://google.github.io/prefab/example-workflow.html, mais il sera probablement beaucoup plus facile de migrer vers AGP.
Pour les bibliothèques qui ne sont pas distribuées en tant que fichiers AAR, consultez les instructions sur l'utilisation de bibliothèques prédéfinies avec CMake dans la documentation add_library
sur les cibles IMPORTED
dans le manuel CMake.
Développer un code tiers
Il existe plusieurs façons de créer du code tiers dans le cadre de votre projet CMake. L'option la plus adaptée dépend de votre situation. Souvent, la meilleure solution est de ne pas procéder ainsi. À la place, créez un fichier AAR pour la bibliothèque et utilisez-le dans votre application. Vous n'avez pas nécessairement besoin de publier ce fichier AAR. Il peut être interne à votre projet Gradle.
Si ce n'est pas possible :
- Procurez-vous (c'est-à-dire copiez) la source tierce dans votre dépôt et utilisez add_subdirectory pour le créer. Cette méthode ne fonctionne que si l'autre bibliothèque est également créée avec CMake.
- Définissez un ExternalProject.
- Créez la bibliothèque séparément de votre projet et suivez la section Utiliser des bibliothèques prédéfinies pour l'importer en tant que telle.
Compatibilité avec YASM dans CMake
Le NDK assure la compatibilité de CMake avec la création de code assembleur écrit en YASM pour l'exécution sur des architectures x86 et x86-64. YASM est un assembleur Open Source destiné aux architectures x86 et x86-64. Il est basé sur l'assembleur NASM.
Pour créer du code assembleur avec CMake, apportez les modifications suivantes au fichier CMakeLists.txt
de votre projet :
- Appelez
enable_language
en définissant la valeur surASM_NASM
. - Selon que vous créez une bibliothèque partagée ou un binaire exécutable, appelez
add_library
ouadd_executable
. Dans les arguments, transmettez la liste des fichiers sources comprenant les fichiers.asm
pour l'assembleur en YASM, et les fichiers.c
pour les bibliothèques ou fonctions C associées.
L'extrait de code suivant montre comment configurer CMakeLists.txt
pour créer un programme YASM en tant que bibliothèque partagée.
cmake_minimum_required(VERSION 3.6.0)
enable_language(ASM_NASM)
add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)
Pour obtenir un exemple de création d'un programme YASM en tant qu'exécutable, consultez le test yasm dans le dépôt Git NDK.
Signaler des problèmes
Si vous rencontrez des problèmes avec le NDK ou son fichier de chaîne d'outils CMake, signalez-les via l'outil de suivi des problèmes android-ndk/ndk sur GitHub. Pour les problèmes liés à Gradle ou au plug-in Android Gradle, signalez un bug Studio.