Настройка CMake

Сценарий сборки CMake — это обычный текстовый файл, который необходимо назвать CMakeLists.txt и который включает команды, которые CMake использует для сборки библиотек C/C++. Если в ваших собственных источниках еще нет сценария сборки CMake, вам необходимо создать его самостоятельно и включить соответствующие команды CMake. Чтобы узнать, как установить CMake, см. раздел Установка и настройка NDK и CMake .

В этом разделе описаны некоторые основные команды, которые следует включить в сценарий сборки, чтобы указать CMake, какие источники использовать при создании собственной библиотеки. Чтобы узнать больше, прочтите официальную документацию по командам CMake .

После настройки нового сценария сборки CMake вам необходимо настроить Gradle для включения вашего проекта CMake в качестве зависимости сборки, чтобы Gradle собирал и упаковывал вашу собственную библиотеку с APK вашего приложения.

Примечание. Если в вашем проекте используется ndk-build, вам не нужно создавать сценарий сборки CMake. Вы можете просто настроить Gradle для включения существующего проекта собственной библиотеки, указав путь к файлу Android.mk .

Создайте сценарий сборки CMake.

Чтобы создать обычный текстовый файл, который можно использовать в качестве сценария сборки CMake, выполните следующие действия:

  1. Откройте панель «Проект» в левой части IDE и выберите представление «Проект» в раскрывающемся меню.
  2. Щелкните правой кнопкой мыши корневой каталог your-module и выберите «Создать» > «Файл» .

    Примечание. Вы можете создать сценарий сборки в любом месте. Однако при настройке сценария сборки пути к собственным исходным файлам и библиотекам указываются относительно местоположения сценария сборки.

  3. Введите «CMakeLists.txt» в качестве имени файла и нажмите «ОК» .

Теперь вы можете настроить сценарий сборки, добавив команды CMake. Чтобы указать CMake создать собственную библиотеку из собственного исходного кода, добавьте команды cmake_minimum_required() и add_library() в свой сценарий сборки:

# 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 )

Совет: Подобно тому, как вы можете указать CMake создать собственную библиотеку из исходных файлов, вы можете использовать команду add_executable() , чтобы указать CMake вместо этого создать исполняемый файл из этих исходных файлов. Однако создание исполняемых файлов из собственных источников не является обязательным, а создание собственных библиотек для упаковки в APK удовлетворяет большинству требований проекта.

Когда вы добавляете исходный файл или библиотеку в сценарий сборки CMake с помощью add_library() , Android Studio также отображает связанные файлы заголовков в представлении «Проект» после синхронизации проекта. Однако для того, чтобы CMake мог найти файлы заголовков во время компиляции, вам необходимо добавить команду include_directories() в сценарий сборки CMake и указать путь к вашим заголовкам:

add_library(...)

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

Соглашение, которое CMake использует для именования файла вашей библиотеки, выглядит следующим образом:

lib library-name .so

Например, если вы укажете «native-lib» в качестве имени вашей общей библиотеки в сценарии сборки, CMake создаст файл с именем libnative-lib.so . Однако при загрузке этой библиотеки в код Java или Kotlin используйте имя, указанное в сценарии сборки CMake:

Котлин

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

Ява

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

Примечание. Если вы переименовываете или удаляете библиотеку в сценарии сборки CMake, вам необходимо очистить проект, прежде чем Gradle применит изменения или удалит старую версию библиотеки из вашего APK. Чтобы очистить проект, выберите «Сборка» > «Очистить проект» в строке меню.

Android Studio автоматически добавляет исходные файлы и заголовки в группу cpp на панели «Проект» . Используя несколько команд add_library() , вы можете определить дополнительные библиотеки, которые CMake будет собирать из других исходных файлов.

Добавить API NDK

Android NDK предоставляет набор собственных API и библиотек, которые могут оказаться вам полезными. Вы можете использовать любой из этих API, включив библиотеки NDK в файл сценария CMakeLists.txt вашего проекта.

Готовые библиотеки NDK уже существуют на платформе Android, поэтому вам не нужно собирать их или упаковывать в APK. Поскольку библиотеки NDK уже являются частью пути поиска CMake, вам даже не нужно указывать расположение библиотеки в вашей локальной установке NDK — вам нужно только предоставить CMake имя библиотеки, которую вы хотите использовать, и ссылку это против вашей собственной родной библиотеки.

Добавьте команду find_library() в сценарий сборки CMake, чтобы найти библиотеку NDK и сохранить ее путь в виде переменной. Эту переменную вы используете для ссылки на библиотеку NDK в других частях сценария сборки. В следующем примере находится библиотека поддержки журналов, специфичная для Android , и сохраняется путь к ней в 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 )

Чтобы ваша собственная библиотека могла вызывать функции в библиотеке log , вам необходимо связать библиотеки с помощью команды target_link_libraries() в сценарии сборки 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} )

NDK также включает в себя некоторые библиотеки в качестве исходного кода, которые вам необходимо собрать и связать с вашей собственной библиотекой. Вы можете скомпилировать исходный код в собственную библиотеку, используя команду add_library() в сценарии сборки CMake. Чтобы указать путь к вашей локальной библиотеке NDK, вы можете использовать переменную пути ANDROID_NDK , которую Android Studio автоматически определяет для вас.

Следующая команда сообщает CMake собрать android_native_app_glue.c , который управляет событиями жизненного цикла NativeActivity и сенсорным вводом, в статическую библиотеку и связывает ее с 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} )

Добавьте другие готовые библиотеки

Добавление предварительно созданной библиотеки аналогично указанию другой собственной библиотеки для сборки CMake. Однако, поскольку библиотека уже собрана, вам необходимо использовать флаг IMPORTED , чтобы сообщить CMake, что вы хотите только импортировать библиотеку в свой проект:

add_library( imported-lib
             SHARED
             IMPORTED )

Затем вам нужно указать путь к библиотеке с помощью команды set_target_properties() , как показано ниже.

Некоторые библиотеки предоставляют отдельные пакеты для конкретных архитектур ЦП или двоичных интерфейсов приложений (ABI) и организуют их в отдельные каталоги. Этот подход помогает библиотекам использовать преимущества определенных архитектур ЦП, позволяя вам использовать только те версии библиотеки, которые вам нужны. Чтобы добавить несколько версий ABI библиотеки в сценарий сборки CMake без необходимости писать несколько команд для каждой версии библиотеки, вы можете использовать переменную пути ANDROID_ABI . Эта переменная использует список ABI по умолчанию, которые поддерживает NDK , или отфильтрованный список ABI, которые вы вручную настраиваете для использования Gradle . Например:

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 )

Чтобы CMake нашел файлы заголовков во время компиляции, вам необходимо использовать команду include_directories() и указать путь к файлам заголовков:

include_directories( imported-lib/include/ )

Примечание. Если вы хотите упаковать предварительно созданную библиотеку, которая не является зависимостью во время сборки — например, при добавлении предварительно созданной библиотеки, которая является зависимостью imported-lib , вам не нужно выполнять следующие инструкции для связывания библиотеки.

Чтобы связать предварительно созданную библиотеку с вашей собственной библиотекой, добавьте ее в команду target_link_libraries() в сценарии сборки CMake:

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

Чтобы упаковать готовую библиотеку в APK, вам необходимо вручную настроить Gradle с помощью блока sourceSets , включив в него путь к вашему файлу .so . После сборки APK вы можете проверить, какие библиотеки Gradle упаковывает в ваш APK, с помощью анализатора APK .

Включить другие проекты CMake

Если вы хотите создать несколько проектов CMake и включить их результаты в свой проект Android, вы можете использовать один файл CMakeLists.txt в качестве сценария сборки CMake верхнего уровня (который вы связываете с Gradle ) и добавлять дополнительные проекты CMake в качестве зависимостей. этого сценария сборки. Следующий сценарий сборки CMake верхнего уровня использует команду add_subdirectory() для указания другого файла CMakeLists.txt в качестве зависимости сборки, а затем связывается с его выходными данными так же, как и с любой другой предварительно созданной библиотекой.

# 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 )

Вызов CMake из командной строки

Используйте следующую команду для вызова CMake для создания проекта Ninja за пределами 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

Эта команда сгенерирует проект Ninja, который можно выполнить для создания исполняемых библиотек Android (файлы .so ). CMAKE_TOOLCHAIN_FILE необходим для использования поддержки CMake NDK. Для CMake 3.21 или более поздней версии вместо этого можно использовать встроенную поддержку NDK CMake, но необходимо использовать другую группу переменных, как описано в документации CMake по кросс-компиляции для Android .