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 expérimenté une nouvelle implémentation du fichier de chaîne d'outils qui réduisait les différences de comportement entre l'utilisation du fichier de chaîne d'outils du NDK et l'utilisation de la compatibilité CMake intégrée. Cela a nécessité un travail important (qui n'a pas été terminé), mais n'a pas réellement amélioré le comportement, nous avons donc arrêté d'aller plus loin.
Le "nouveau" fichier de chaîne d'outils présente des régressions de comportement par rapport à l'"ancienne" fichier de chaîne d'outils. Le comportement par défaut est le workflow recommandé. Si vous utilisez -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'ancien 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'est actuellement pas prévu de le supprimer du NDK. Cela endommagerait les builds qui dépendent des différences de comportement entre les nouveaux fichiers de chaîne d'outils et les anciens fichiers de chaîne d'outils. Malheureusement, si vous renommiez l'option pour indiquer clairement que l'ancien fichier est en fait recommandé, les utilisateurs ne bénéficieraient pas de cette option. Si vous utilisez le nouveau fichier de chaîne d'outils, vous n'avez pas besoin de le migrer. Sachez toutefois que les bugs signalés concernant le comportement du nouveau fichier de chaîne d'outils ne seront probablement pas corrigés. Vous devrez plutôt le 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, 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 options du compilateur
Si vous devez transmettre des indicateurs spécifiques au compilateur ou à l'éditeur de liens pour votre build, consultez la documentation CMake sur set_target_compile_options et la famille d'options associée. La section "Voir aussi" en bas de cette page contient des indices utiles.
En règle générale, il est recommandé d'appliquer les options du compilateur comme champ d'application le plus restreint disponible. Il n'est pas pratique de répéter les indicateurs que vous souhaitez appliquer à toutes vos cibles (par exemple, -Werror
) par module. Toutefois, ils doivent rarement être appliqués à l'échelle mondiale (CMAKE_CXX_FLAGS
), car ils peuvent avoir des effets indésirables sur les dépendances tierces de votre projet. Dans ce cas, les indicateurs peuvent être appliqués 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. Ne le faites pas. Les indicateurs transmis à CMake à partir de Gradle présenteront des comportements de priorité surprenants, dans certains cas en ignorant les indicateurs transmis implicitement par l'implémentation, qui sont nécessaires pour compiler le code Android. Préférez toujours gérer le comportement CMake directement dans CMake. Si vous devez contrôler les indicateurs du compilateur par buildType
AGP, consultez Utiliser les types de compilation AGP dans CMake.
Utiliser des types de compilation AGP dans CMake
Si vous devez adapter le comportement de CMake à un buildType
Gradle personnalisé, utilisez ce type de compilation pour transmettre un indicateur CMake supplémentaire (et non un indicateur de compilateur) que vos scripts de compilation CMake peuvent lire. Par exemple, si vous disposez de variantes de compilation "free" et "premium" 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")
}
}
}
}
}
Ensuite, dans votre fichier CMakeLists.txt:
if (DPRODUCT_VARIANT_PREMIUM)
# Do stuff for the premium build.
else()
# Do stuff for the free build.
endif()
Le nom de la variable vous appartient, mais veillez à éviter tout élément avec un préfixe ANDROID_
, APP_
ou CMAKE_
pour éviter tout conflit ou confusion avec les indicateurs existants.
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.