CMake

Android NDK поддерживает использование CMake для компиляции кода C и C++ для вашего приложения. На этой странице обсуждается, как использовать CMake с NDK через ExternalNativeBuild подключаемого модуля Android Gradle или при непосредственном вызове CMake.

Файл набора инструментов CMake

NDK поддерживает CMake через файл цепочки инструментов . Файлы цепочки инструментов — это файлы CMake, которые настраивают поведение цепочки инструментов для кросс-компиляции. Файл цепочки инструментов, используемый для NDK, находится в NDK по адресу <NDK>/build/cmake/android.toolchain.cmake .

Параметры сборки, такие как ABI, minSdkVersion и т. д., передаются в командной строке при вызове cmake . Список поддерживаемых аргументов см. в разделе «Аргументы Toolchain» .

«Новый» файл инструментальной цепочки

Ранее NDK экспериментировали с новой реализацией файла цепочки инструментов, которая позволила бы уменьшить различия в поведении между использованием файла цепочки инструментов NDK и использованием встроенной поддержки CMake. В конечном итоге это потребовало значительного объема работы (которая не была завершена), но на самом деле не улучшило поведение, поэтому мы больше не занимаемся этим.

Поведение «нового» файла цепочки инструментов ухудшилось по сравнению с «устаревшим» файлом цепочки инструментов. Поведение по умолчанию — это рекомендуемый рабочий процесс. Если вы используете -DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF , мы рекомендуем удалить этот флаг из вашей сборки. Новый файл цепочки инструментов так и не достиг четности с устаревшим файлом цепочки инструментов, поэтому возможны регрессии поведения.

Хотя мы не рекомендуем использовать новый файл цепочки инструментов, в настоящее время нет планов удалять его из NDK. Это нарушит работу сборок, основанных на различиях в поведении между новыми и устаревшими файлами набора инструментов, и, к сожалению, переименование опции, чтобы было ясно, что на самом деле рекомендуется «устаревшая версия», также нарушит работу пользователей этой опции. Если вы успешно используете новый файл цепочки инструментов, вам не нужно мигрировать, но знайте, что любые ошибки, обнаруженные в поведении нового файла цепочки инструментов, вероятно, не будут исправлены, и вместо этого вам придется выполнить миграцию.

Использование

Градл

Использование файла цепочки инструментов CMake происходит автоматически при использовании externalNativeBuild . Дополнительную информацию см . в разделе «Добавление кода C и C++» Android Studio в руководство по проекту .

Командная строка

При сборке с помощью CMake вне Gradle сам файл набора инструментов и его аргументы должны быть переданы в CMake. Например:

$ cmake \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=$ABI \
    -DANDROID_PLATFORM=android-$MINSDKVERSION \
    $OTHER_ARGS

Аргументы инструментальной цепочки

Следующие аргументы можно передать в файл цепочки инструментов CMake. При сборке с помощью Gradle добавьте аргументы в android.defaultConfig.externalNativeBuild.cmake.arguments , как описано в документации ExternalNativeBuild . При сборке из командной строки передайте аргументы CMake с помощью -D . Например, чтобы заставить Armeabi-v7a не создавать поддержку Neon, передайте -DANDROID_ARM_NEON=FALSE .

ANDROID_ABI

Целевой ABI. Информацию о поддерживаемых ABI см. в разделе Android ABI .

Градл

Gradle предоставляет этот аргумент автоматически. Не указывайте этот аргумент явно в файле build.gradle . Чтобы контролировать целевые ABI Gradle, используйте abiFilters , как описано в Android ABI .

Командная строка

CMake строит для одной цели в каждой сборке. Чтобы настроить таргетинг на несколько Android ABI, необходимо создать один ABI для каждого ABI. Рекомендуется использовать разные каталоги сборки для каждого ABI, чтобы избежать конфликтов между сборками.

Ценить Примечания
armeabi-v7a
armeabi-v7a with NEON То же, что и armeabi-v7a .
arm64-v8a
x86
x86_64

ANDROID_ARM_MODE

Указывает, следует ли генерировать инструкции руки или большого пальца для Armeabi-v7a. Не оказывает влияния на другие ABI. Дополнительную информацию см. в документации Android ABI .

Ценить Примечания
рука
большой палец Поведение по умолчанию.

ANDROID_NATIVE_API_LEVEL

Псевдоним для ANDROID_PLATFORM .

ANDROID_PLATFORM

Указывает минимальный уровень API, поддерживаемый приложением или библиотекой. Это значение соответствует minSdkVersion приложения.

Градл

При использовании подключаемого модуля Android Gradle это значение автоматически устанавливается в соответствии с minSdkVersion приложения, и его не следует задавать вручную.

Командная строка

При прямом вызове CMake это значение по умолчанию равно самому низкому уровню API, поддерживаемому используемым NDK. Например, в NDK r20 это значение по умолчанию равно уровню API 16.

Для этого параметра принимаются несколько форматов:

  • android-$API_LEVEL
  • $API_LEVEL
  • android-$API_LETTER

Формат $API_LETTER позволяет указать android-N без необходимости определять номер, связанный с этой версией. Обратите внимание, что некоторые выпуски получили увеличение API без увеличения буквы. Эти API можно указать, добавив суффикс -MR1 . Например, уровень API 25 — android-N-MR1 .

ANDROID_STL

Указывает, какой STL использовать для этого приложения. Дополнительные сведения см. в разделе Поддержка библиотек C++ . По умолчанию будет использоваться c++_static .

Ценить Примечания
c++_общий Вариант общей библиотеки libc++ .
c++_static Вариант статической библиотеки libc++ .
никто Нет поддержки стандартной библиотеки C++.
система Система СТЛ

Управление флагами компилятора

Если вам нужно передать определенные флаги компилятору или компоновщику для вашей сборки, обратитесь к документации CMake для set_target_compile_options и связанного семейства параметров. В разделе «см. также» внизу страницы есть несколько полезных подсказок.

В общем, лучше всего применять флаги компилятора как самую узкую доступную область применения. Флаги, которые вы хотите применить ко всем вашим целям (например, -Werror ), неудобно повторять для каждого модуля, но их все же редко следует применять глобально ( CMAKE_CXX_FLAGS ), поскольку они могут иметь нежелательные последствия для сторонних зависимостей в вашем проекте. . В таких случаях флаги можно применять в области каталога ( add_compile_options ).

Для узкого подмножества флагов компилятора их также можно установить в файле build.gradle с помощью cppFlags или аналогичных свойств. Не следует этого делать. Флаги, передаваемые в CMake из Gradle, будут иметь удивительное поведение приоритета, в некоторых случаях переопределяя флаги, неявно переданные реализацией, которые необходимы для создания кода Android. Всегда предпочитайте обрабатывать поведение CMake непосредственно в CMake. Если вам нужно управлять флагами компилятора для каждого buildType AGP, см. раздел Работа с типами сборки AGP в CMake .

Работа с типами сборки AGP в CMake

Если вам нужно адаптировать поведение CMake к пользовательскому buildType Gradle, используйте этот тип сборки для передачи дополнительного флага CMake (а не флага компилятора), который могут читать ваши сценарии сборки CMake. Например, если у вас есть «бесплатные» и «премиум» варианты сборки, которые контролируются вашим build.gradle.kts, и вам необходимо передать эти данные в CMake:

android {
    buildTypes {
        free {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=OFF")
                }
            }
        }
        premium {
            externalNativeBuild {
                cmake {
                    arguments.add("-DPRODUCT_VARIANT_PREMIUM=ON")
                }
            }
        }
    }
}

Затем в вашем CMakeLists.txt:

if (DPRODUCT_VARIANT_PREMIUM)
  # Do stuff for the premium build.
else()
  # Do stuff for the free build.
endif()

Имя переменной зависит от вас, но убедитесь, что вы избегаете чего-либо с префиксом ANDROID_ , APP_ или CMAKE_ , чтобы избежать конфликта или путаницы с существующими флагами.

Пример см. в образце Sanitizers NDK .

Понимание команды сборки CMake

При отладке проблем сборки CMake полезно знать конкретные аргументы сборки, которые Gradle использует при кросс-компиляции для Android.

Плагин Android Gradle сохраняет аргументы сборки, которые он использует для выполнения сборки CMake для каждой пары ABI и типа сборки, в build_command.txt . Эти файлы находятся в следующем каталоге:

<project-root>/<module-root>/.cxx/cmake/<build-type>/<ABI>/

В следующем фрагменте показан пример аргументов CMake для создания отлаживаемой версии примера hello-jni предназначенной для архитектуры 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

Используйте готовые библиотеки

Если предварительно созданная библиотека, которую вам необходимо импортировать, распространяется как AAR, следуйте документации по зависимостям Studio, чтобы импортировать и использовать ее. Если вы не используете AGP, вы можете следовать https://google.github.io/prefab/example-workflow.html, но, вероятно, перейти на AGP гораздо проще.

Для библиотек, которые не распространяются как AAR, инструкции по использованию готовых библиотек с CMake см. в документации add_library относительно IMPORTED целей в руководстве CMake .

Создание стороннего кода

Существует несколько способов создания стороннего кода в рамках вашего проекта CMake, и какой вариант работает лучше всего, будет зависеть от вашей ситуации. Лучшим вариантом часто будет не делать этого вообще. Вместо этого создайте AAR для библиотеки и используйте его в своем приложении. Вам не обязательно публиковать этот AAR. Он может быть внутренним для вашего проекта Gradle.

Если это не вариант:

  • Продайте (т. е. скопируйте) сторонний исходный код в свой репозиторий и используйте add_subdirectory для его сборки. Это работает только в том случае, если другая библиотека также собрана с помощью CMake.
  • Определите внешний проект .
  • Создайте библиотеку отдельно от вашего проекта и следуйте инструкциям «Использовать готовые библиотеки», чтобы импортировать ее как готовую.

Поддержка YASM в CMake

NDK обеспечивает поддержку CMake для создания ассемблерного кода, написанного на YASM, для работы на архитектурах x86 и x86-64. YASM — это ассемблер с открытым исходным кодом для архитектур x86 и x86-64, основанный на ассемблере NASM.

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

  1. Вызовите enable_language со значением, установленным в ASM_NASM .
  2. В зависимости от того, создаете ли вы общую библиотеку или исполняемый двоичный файл, вызовите add_library или add_executable . В качестве аргументов передайте список исходных файлов, состоящий из файлов .asm для программы сборки в YASM и файлов .c для связанных библиотек или функций C.

В следующем фрагменте показано, как можно настроить CMakeLists.txt для создания программы YASM в качестве общей библиотеки.

cmake_minimum_required(VERSION 3.6.0)

enable_language(ASM_NASM)

add_library(test-yasm SHARED jni/test-yasm.c jni/print_hello.asm)

Пример того, как создать программу YASM в виде исполняемого файла, см. в тесте yasm в репозитории NDK git.

Сообщить о проблемах

Если у вас возникнут какие-либо проблемы с NDK или его файлом набора инструментов CMake, сообщите о них через систему отслеживания проблем android-ndk/ndk на GitHub. В случае проблем с Gradle или плагином Android Gradle вместо этого сообщите об ошибке Studio .