Configurer CMake

Un script de compilation CMake est un fichier en texte brut qui inclut des commandes permettant à CMake de créer vos bibliothèques C/C++. Ce fichier doit être nommé CMakeLists.txt. Si vos sources natives ne comportent pas déjà un script de compilation CMake, vous devez en créer un vous-même et inclure les commandes CMake appropriées. Pour savoir comment installer CMake, consultez la page Installer et configurer le NDK et CMake.

Cette section décrit quelques commandes de base à inclure dans votre script de compilation afin d'indiquer à CMake quelles sources utiliser lors de la création de votre bibliothèque native. Pour en savoir plus, consultez la documentation officielle sur les commandes CMake.

Après avoir configuré un nouveau script de compilation CMake, vous devez configurer Gradle pour inclure votre projet CMake en tant que dépendance de compilation, afin que Gradle compile et package votre bibliothèque native avec l'APK de votre application.

Remarque : Si votre projet utilise ndk-build, vous n'avez pas besoin de créer de script de compilation CMake. Il vous suffit de configurer Gradle pour inclure votre projet de bibliothèque native existant en fournissant un chemin d'accès à votre fichier Android.mk.

Créer un script de compilation CMake

Pour créer un fichier en texte brut utilisable comme script CBuild, procédez comme suit :

  1. Ouvrez le volet Projet à gauche de l'IDE, puis sélectionnez la vue Projet dans le menu déroulant.
  2. Effectuez un clic droit sur le répertoire racine de your-module, puis sélectionnez Nouveau > Fichier.

    Remarque : Vous pouvez créer le script de compilation à l'emplacement de votre choix. Toutefois, lorsque vous le configurez, les chemins d'accès à vos bibliothèques et fichiers sources natifs sont relatifs à l'emplacement du script de compilation.

  3. Nommez le fichier "CMakeLists.txt", puis cliquez sur OK.

Vous pouvez maintenant configurer votre script de compilation en ajoutant des commandes CMake. Pour indiquer à CMake de créer une bibliothèque native à partir du code source natif, ajoutez les commandes cmake_minimum_required() et add_library() à votre script de compilation :

# Sets the minimum version of CMake required to build your native library.
# This ensures that a certain set of CMake features is available to
# your build.

cmake_minimum_required(VERSION 3.4.1)

# Specifies a library name, specifies whether the library is STATIC or
# SHARED, and provides relative paths to the source code. You can
# define multiple libraries by adding multiple add_library() commands,
# and CMake builds them for you. When you build your app, Gradle
# automatically packages shared libraries with your APK.

add_library( # Specifies the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/native-lib.cpp )

Conseil : En procédant comme pour indiquer à CMake de créer une bibliothèque native à partir de fichiers sources, vous pouvez utiliser la commande add_executable() pour créer un exécutable à partir de ces mêmes fichiers sources. Toutefois, il n'est généralement pas nécessaire de créer des fichiers exécutables à partir de vos sources natives. Compiler des bibliothèques natives et les packager dans votre APK répond aux besoins de la plupart des projets.

Lorsque vous utilisez add_library() pour ajouter un fichier source ou une bibliothèque à votre script de compilation CMake, Android Studio affiche également les fichiers d'en-tête associés dans la vue Projet une fois votre projet synchronisé. Cependant, pour que CMake puisse localiser vos fichiers d'en-tête lors de la compilation, vous devez ajouter la commande include_directories() à votre script de compilation et spécifier le chemin d'accès à vos en-têtes :

add_library(...)

# Specifies a path to native header files.
include_directories(src/main/cpp/include/)

La convention de nommage CMake pour les fichiers de bibliothèque est la suivante :

liblibrary-name.so

Par exemple, si vous spécifiez "native-lib" comme nom de bibliothèque partagée dans le script de compilation, CMake crée un fichier nommé libnative-lib.so. Lorsque vous chargez cette bibliothèque dans votre code Java ou Kotlin, utilisez le nom que vous avez spécifié dans le script de compilation CMake :

Kotlin

companion object {
    init {
        System.loadLibrary("native-lib");
    }
}

Java

static {
    System.loadLibrary("native-lib");
}

Remarque : Si vous renommez ou supprimez une bibliothèque dans votre script de compilation CMake, vous devez nettoyer votre projet avant que Gradle applique les modifications ou supprime l'ancienne version de la bibliothèque de votre APK. Pour nettoyer votre projet, sélectionnez Compiler > Nettoyer le projet dans la barre de menu.

Android Studio ajoute automatiquement les fichiers et les en-têtes sources au groupe cpp dans le volet Projet. En utilisant plusieurs commandes add_library(), vous pouvez définir des bibliothèques supplémentaires pour indiquer à CMake de les compiler à partir d'autres fichiers sources.

Ajouter des API NDK

Le Android Native Development Kit (NDK) Android fournit un ensemble d'API et de bibliothèques natives qui peuvent vous être utiles. Vous pouvez utiliser ces API en incluant les bibliothèques NDK dans le fichier de script CMakeLists.txt de votre projet.

La plate-forme Android propose des bibliothèques NDK prédéfinies, que vous n'avez pas besoin de créer ni de packager dans votre APK. Comme les bibliothèques NDK font déjà partie du chemin de recherche de CMake, vous n'avez même pas besoin de spécifier leur emplacement dans votre installation locale du NDK. Il vous suffit de fournir à CMake le nom de la bibliothèque que vous souhaitez utiliser, et l'associer à votre propre bibliothèque native.

Ajoutez la commande find_library() à votre script de compilation CMake pour localiser une bibliothèque NDK et stocker son chemin d'accès en tant que variable. Vous utiliserez cette variable pour faire référence à la bibliothèque NDK dans d'autres parties du script de compilation. L'exemple suivant localise la bibliothèque Android Support dédiée aux journaux et stocke son chemin d'accès dans log-lib :

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

Pour que votre bibliothèque native puisse appeler des fonctions de la bibliothèque log, vous devez associer les bibliothèques à l'aide de la commande target_link_libraries() dans votre script de compilation CMake :

find_library(...)

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

Le NDK inclut également certaines bibliothèques en tant que code source que vous devez compiler et associer à votre bibliothèque native. Vous pouvez compiler le code source dans une bibliothèque native à l'aide de la commande add_library() dans votre script de compilation CMake. Pour fournir un chemin d'accès à votre bibliothèque NDK locale, vous pouvez utiliser la variable de chemin ANDROID_NDK, qu'Android Studio définit automatiquement pour vous.

La commande suivante indique à CMake de compiler android_native_app_glue.c (qui gère les événements de cycle de vie et les entrées tactiles NativeActivity) dans une bibliothèque statique et l'associe à native-lib :

add_library( app-glue
             STATIC
             ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

# You need to link static libraries against your shared native library.
target_link_libraries( native-lib app-glue ${log-lib} )

Ajouter d'autres bibliothèques prédéfinies

Le processus permettant d'ajouter une bibliothèque prédéfinie et celui servant à spécifier une autre bibliothèque native à compiler par CMake sont similaires. Toutefois, comme la bibliothèque est déjà créée, vous devez utiliser l'option IMPORTED pour indiquer à CMake que vous souhaitez seulement l'importer dans votre projet :

add_library( imported-lib
             SHARED
             IMPORTED )

Vous devez ensuite spécifier le chemin d'accès à la bibliothèque à l'aide de la commande set_target_properties(), comme indiqué ci-dessous.

Certaines bibliothèques fournissent des packages distincts pour des architectures de processeur spécifiques (les interfaces binaires d'applications (ABI)) et les organisent dans des répertoires distincts. Cette approche aide les bibliothèques à exploiter certaines architectures de processeur tout en vous permettant de n'utiliser que les versions de la bibliothèque qui vous intéressent. Pour ajouter plusieurs versions ABI d'une bibliothèque à votre script de compilation CMake, sans avoir à écrire plusieurs commandes pour chaque version de la bibliothèque, vous pouvez utiliser la variable de chemin ANDROID_ABI. Cette variable utilise une liste des ABI par défaut compatibles avec le NDK. Vous pouvez également configurer manuellement Gradle afin d'utiliser une liste d'ABI filtrée. Par exemple :

add_library(...)
set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

Pour que CMake localise vos fichiers d'en-tête lors de la compilation, vous devez utiliser la commande include_directories() et inclure le chemin d'accès à vos fichiers d'en-tête :

include_directories( imported-lib/include/ )

Remarque : Si vous souhaitez packager une bibliothèque prédéfinie qui n'est pas une dépendance au moment de la compilation (par exemple, lorsque vous ajoutez une bibliothèque prédéfinie qui est une dépendance de imported-lib), vous n'avez pas besoin d'exécuter les instructions suivantes pour associer la bibliothèque.

Pour associer la bibliothèque prédéfinie à votre propre bibliothèque native, ajoutez-la à la commande target_link_libraries() dans votre script de compilation CMake :

target_link_libraries( native-lib imported-lib app-glue ${log-lib} )

Pour packager la bibliothèque prédéfinie dans votre APK, vous devez configurer manuellement Gradle avec le bloc sourceSets, afin d'inclure le chemin d'accès à votre fichier .so. Après avoir compilé votre APK, vous pouvez vérifier quelles bibliothèques Gradle y sont incluses à l'aide de l'analyseur d'APK.

Inclure d'autres projets CMake

Si vous souhaitez créer plusieurs projets CMake et inclure leurs sorties dans votre projet Android, vous pouvez utiliser un fichier CMakeLists.txt comme script de compilation CMake de premier niveau (celui que vous associez à Gradle) et ajouter des projets CMake supplémentaires en tant que dépendances de ce script de compilation. Le script de compilation CMake de premier niveau ci-dessous utilise la commande add_subdirectory() pour spécifier un autre fichier CMakeLists.txt en tant que dépendance de compilation, puis renvoie un lien vers la sortie obtenue comme pour toute autre bibliothèque prédéfinie.

# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath )

# Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR})

# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
                  ${lib_src_DIR}

                  # Specifies the directory for the build outputs.
                  ${lib_build_DIR} )

# Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
                       ${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include )

# Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )

Appeler CMake à partir de la ligne de commande

Exécutez la commande suivante pour appeler CMake afin de générer un projet Ninja en dehors d'Android Studio :

cmake
-Hpath/to/cmakelists/folder
-Bpath/to/generated/ninja/project/debug/ABI
-DANDROID_ABI=ABI                               // For example, arm64-v8a
-DANDROID_PLATFORM=platform-version-string      // For example, android-16
-DANDROID_NDK=android-sdk/ndk/ndk-version
-DCMAKE_TOOLCHAIN_FILE=android-sdk/ndk/ndk-version/build/cmake/android.toolchain.cmake
-G Ninja

Cette commande génère le projet Ninja, qui peut être exécuté pour créer des bibliothèques exécutables Android (fichiers .so). CMAKE_TOOLCHAIN_FILE est nécessaire pour la prise en charge CMake par le NDK. Avec CMake 3.21 et les versions ultérieures, vous pouvez utiliser la prise en charge intégrée du NDK par CMake à la place. Cependant, vous devrez utiliser un autre groupe de variables, comme expliqué dans la documentation Compilation croisée pour Android de CMake.