We detected you are likely not from a Russian-speaking region. Would you like to switch to the international version of the site?

  Назад к списку статей

Автоматизация сборки и кросс‑компиляции Android‑нативных библиотек с CMake и Ninja в Termux

Termux — мощная среда для разработки под Android, где можно разворачивать инструментальную цепочку, собирать проекты на C/C++ и автоматизировать повторяемые задачи. Когда требуется собрать Android‑нативную библиотеку (JNI/NDK‑ориентированную) с использованием CMake и Ninja, на практике важно обеспечить: единообразную структуру сборки, предсказуемые параметры toolchain, корректную изоляцию артефактов по архитектурам и вариантам (Debug/Release), а также воспроизводимость команд.

В этой статье я покажу подход к автоматизации сборки и кросс‑компиляции под Android из Termux: подготовка зависимостей, выбор схемы toolchain, написание кросс‑компиляционных конфигураций для CMake, настройка Ninja и создание скриптов, которые можно запускать в один-два шага.

Требования и общая архитектура процесса

Цель — получать библиотеки .so для нужных ABI (например, arm64-v8a, armeabi-v7a, x86_64) без ручной рутины. Ключевые компоненты:

  • Среда Termux для сборки и запуска генератора/компилятора.
  • Android NDK (toolchain) для кросс‑компиляции.
  • CMake как система генерации сборки.
  • Ninja как быстрый генератор.
  • Скрипты (bash) для: выбора ABI, подготовки build‑директорий, запуска CMake, запуска сборки и упаковки результатов.

Логика сборки обычно такая:

  1. Определяем ABI, API level, тип сборки (Debug/Release).
  2. Настраиваем переменные CMake: компилятор, sysroot, target triple, флаги.
  3. Генерируем build‑файлы Ninja через CMake.
  4. Запускаем сборку Ninja.
  5. Собираем артефакты в папку out/ и фиксируем версию окружения.

Подготовка окружения в Termux

Начнем с базовой подготовки пакетов. В Termux установите необходимые инструменты: CMake, Ninja, компилятор (для хостовых утилит) и вспомогательные пакеты. Конкретные названия пакетов могут зависеть от версии Termux/репозиториев, но общий принцип такой:

pkg update
pkg upgrade
pkg install cmake ninja-build clang llvm
pkg install git curl unzip

Дальше нужен Android NDK. На практике NDK удобно хранить в доступной директории (например, внутри домашней папки Termux) и ссылаться на него переменными окружения. Допустим, вы распаковали NDK в $HOME/android-ndk.

export ANDROID_NDK_HOME="$HOME/android-ndk"

Проверьте, что NDK действительно доступен:

ls "$ANDROID_NDK_HOME"

Если NDK установлен корректно, можно переходить к настройке кросс‑компиляции.

Схема сборки: build‑директории по ABI и конфигурациям

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

project/
  CMakeLists.txt
  scripts/
    build.sh
  toolchains/
    android.cmake
  out/
    arm64-v8a/
      debug/
      release/
    armeabi-v7a/
      debug/
      release/

Так вы избегаете конфликтов артефактов и упрощаете сборку многoархитектурного пакета.

Кросс‑компиляционный toolchain файл для CMake

Один из самых практичных подходов — создать toolchain file для CMake. Он описывает, как CMake должен понимать NDK, sysroot, целевую архитектуру и компоновку.

Создадим файл, например toolchains/android.cmake. Он будет использоваться в скриптах с параметрами ABI.

# toolchains/android.cmake
# Пример: кросс-компиляция Android-native библиотеки через CMake + Ninja

if(NOT DEFINED ANDROID_NDK_HOME)
  message(FATAL_ERROR "ANDROID_NDK_HOME is not set")
endif()

set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_ANDROID_NDK "${ANDROID_NDK_HOME}")

# Важно: задавайте API level.
# В скрипте лучше прокидывать -DANDROID_PLATFORM=android-XX
if(NOT DEFINED ANDROID_PLATFORM)
  set(ANDROID_PLATFORM android-24)
endif()

set(CMAKE_ANDROID_ARCH_ABI "${ANDROID_ABI}")
set(CMAKE_ANDROID_API "${ANDROID_PLATFORM}")

# Поддержка стандартной переменной Toolchain в CMake.
# Для новых CMake достаточно корректно задать NDK + ABI + API.
# При необходимости можно расширить кастомными флагами.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

# Ninja: обычно задается на уровне команды cmake -G Ninja.
# Здесь оставляем общие настройки.
set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}")

Обратите внимание: переменные ANDROID_ABI и ANDROID_PLATFORM мы будем передавать из скрипта. Также корректность зависит от версии CMake и NDK; при расхождениях можно уточнять флаги компилятора/линкера, но базовая схема обычно работает.

Сборочный скрипт: сборка одной архитектуры

Теперь соберем библиотеку для заданного ABI, типа сборки и уровня API. В scripts/build.sh реализуем логику:

  • Принимаем ABI и конфигурацию.
  • Формируем build‑директорию.
  • Запускаем CMake с генератором Ninja.
  • Запускаем Ninja.
  • Копируем результат в out/.
#!/data/data/com.termux/files/usr/bin/bash
set -e

# Параметры по умолчанию
ANDROID_NDK_HOME_DEFAULT="$HOME/android-ndk"
ANDROID_NDK_HOME="${ANDROID_NDK_HOME:-$ANDROID_NDK_HOME_DEFAULT}"

ANDROID_API="${ANDROID_API:-24}"
ANDROID_ABI="${ANDROID_ABI:-arm64-v8a}"
BUILD_TYPE="${BUILD_TYPE:-Release}"

PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TOOLCHAIN_FILE="$PROJECT_DIR/toolchains/android.cmake"

OUT_ROOT="$PROJECT_DIR/out/${ANDROID_ABI}/${BUILD_TYPE,,}"
BUILD_DIR="$PROJECT_DIR/build/${ANDROID_ABI}/${BUILD_TYPE,,}"

mkdir -p "$OUT_ROOT"
mkdir -p "$BUILD_DIR"

export ANDROID_NDK_HOME
export ANDROID_ABI

# Генерация и сборка
cmake -S "$PROJECT_DIR" -B "$BUILD_DIR" -G Ninja 
  -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" 
  -DANDROID_PLATFORM="android-${ANDROID_API}" 
  -DCMAKE_BUILD_TYPE="$BUILD_TYPE"

ninja -C "$BUILD_DIR"

# Пример копирования итогов.
# В реальных проектах пути к output могут отличаться.
# Ориентируйтесь на ваши target'ы и структуру CMake.
if [ -d "$BUILD_DIR" ]; then
  find "$BUILD_DIR" -maxdepth 4 -type f ( -name ".so" -o -name ".a" ) -print -exec cp -n {} "$OUT_ROOT/" ;
fi

echo "Done: ABI=$ANDROID_ABI API=$ANDROID_API BUILD_TYPE=$BUILD_TYPE -> $OUT_ROOT"

Скрипт копирует .so и .a на глубине до 4 уровней. Если у вас конкретные targets (например, один my_native_lib), лучше настроить CMake так, чтобы выходы попадали в предсказуемые каталоги, и копировать уже их.

Скрипт: сборка набора ABI одной командой

Чтобы полностью закрыть задачу автоматизации, создайте скрипт, который соберет список ABI. Например scripts/build_all.sh:

#!/data/data/com.termux/files/usr/bin/bash
set -e

ANDROID_API="${ANDROID_API:-24}"
BUILD_TYPE="${BUILD_TYPE:-Release}"

# Можно расширять список
ABIS=("arm64-v8a" "armeabi-v7a" "x86_64")

for abi in "${ABIS[@]}"; do
  echo "Building for $abi ..."
  ANDROID_ABI="$abi" ANDROID_API="$ANDROID_API" BUILD_TYPE="$BUILD_TYPE" 
    "$HOME/project/scripts/build.sh"
done

echo "All builds complete."

Важно: в реальном проекте укажите правильный путь к вашему scripts/build.sh. И давайте артефактам единое место out/, чтобы дальше их было легко упаковать в AAR/JAR или подхватить билдом приложения.

Интеграция с CMakeLists.txt: рекомендации по target'ам

Чтобы CMake корректно генерировал нативные библиотеки под Android, проверьте базовые моменты:

  • Используйте add_library(... SHARED ...) для .so.
  • Включайте target_include_directories и target_compile_options для нужных флагов.
  • Если есть зависимости, задавайте их через target_link_libraries.
  • Для C/C++ используйте корректный стандарт (например, C++17) через target_compile_features или CMAKE_CXX_STANDARD.

Пример упрощенной секции:

cmake_minimum_required(VERSION 3.22)
project(my_native_lib LANGUAGES C CXX)

add_library(my_native_lib SHARED
  src/native.cpp
)

target_compile_features(my_native_lib PRIVATE cxx_std_17)

# Если у вас есть заголовки:
target_include_directories(my_native_lib PRIVATE
  include
)

Если библиотека должна поддерживать разные ABI, CMake сам подхватит подходящий компилятор из toolchain файла/NDK при корректно указанных переменных ABI и API.

Запуск сборки: практические команды

Допустим, вы находитесь в Termux и уже подготовили проект. Для сборки одной архитектуры выполните:

cd $HOME/my_native_project

chmod +x scripts/build.sh scripts/build_all.sh

ANDROID_NDK_HOME="$HOME/android-ndk" 
ANDROID_ABI="arm64-v8a" 
ANDROID_API="24" 
BUILD_TYPE="Release" 
scripts/build.sh

Для сборки нескольких ABI сразу:

ANDROID_API="24" BUILD_TYPE="Release" scripts/build_all.sh

Результаты окажутся в out/ согласно структуре, которую вы заложили.

Оптимизация скорости сборки и повторяемости

Чтобы сборка не «тянулась» бесконечно, используйте базовые улучшения:

  • Никогда не пересобирайте «в одном и том же build‑каталоге» разные ABI — только через изоляцию по ABI/типу.
  • Кэшируйте зависимости, если проект их скачивает; используйте стабильные версии.
  • Контролируйте переменные окружения: фиксируйте ANDROID_API, NDK версию и CMake генератор.
  • Логируйте вывод CMake/Ninja в файл при CI/ночных сборках.

Например, логирование можно сделать так:

ANDROID_ABI="arm64-v8a" ANDROID_API="24" BUILD_TYPE="Release" 
scripts/build.sh |& tee "$HOME/my_native_project/build.log"

Частые проблемы и как их диагностировать

1) CMake ругается на toolchain/ABI.
Проверьте, что переменная ANDROID_ABI соответствует ожидаемому формату: arm64-v8a, armeabi-v7a, x86_64 и т.п. Убедитесь, что ANDROID_NDK_HOME указывает на корень NDK.

2) Ошибки линковки (.so не собирается).
Проверьте, что у вас верно определены зависимости через target_link_libraries, а также что библиотека собирается как SHARED если нужен .so.

3) Несовпадение API level и поддерживаемых функций.
Если вы используете системные API, которые требуют более высокий Android API, повышайте ANDROID_API и/или корректируйте условную компиляцию.

4) Слишком долгие пересборки.
Проверьте, что меняются входные файлы и флаги. В частности, если вы генерируете CMake каждый раз в новом каталоге — это ожидаемо; если же в одном и том же каталоге и всё равно пересобирается, вероятно, меняются параметры или версии инструментов.

Про VPN (локальная сеть)

Если вам нужно организовать работу Termux и внешних устройств в рамках одной локальной сети (например, для обмена артефактами/доступа к сборочным артефактам), можно использовать VPN для создания локальной сети. При этом VPN следует настраивать строго для сетевого удобства, а не для обхода блокировок.

Заключение

Автоматизация сборки Android‑нативных библиотек в Termux через CMake и Ninja — это, прежде всего, дисциплина в настройке toolchain, изоляция build‑директорий по ABI и конфигурациям, а также грамотные скрипты, которые превращают сложный процесс кросс‑компиляции в предсказуемую последовательность команд. Следуя предложенной структуре (toolchain file + скрипты сборки одной архитектуры и набора ABI), вы быстро получите воспроизводимый конвейер и легко сможете интегрировать его в разработку и публикацию.

Если хотите, чтобы ваш проект был настроен «под ключ» (NDK/CMake/Ninja, корректная структура, скрипты, упаковка артефактов под приложение, CI‑подготовка) — обращайтесь в РыбинскЛАБ. Мы поможем оптимизировать процесс сборки и обеспечить стабильность кросс‑компиляции для Android.

* Текст статьи подготовлен и структурирован с использованием технологий искусственного интеллекта. Проверен и доработан перед публикацией.

Нужна помощь с настройкой Termux, Linux и серверов?

Я оказываю ИТ-услуги: настройка серверов, автоматизация, безопасность, помощь с Linux и инфраструктурой. Материалы сайта — только в ознакомительных и образовательных целях.

Связаться со мной
Поддержать проект