CMake

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 :

  1. Appelez enable_language en définissant la valeur sur ASM_NASM.
  2. Selon que vous créez une bibliothèque partagée ou un binaire exécutable, appelez add_library ou add_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.