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

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

Автоматизация сборки и кросс‑компиляции C/C++ проектов для Android‑ARM64 с использованием CMake и Ninja в Termux

Termux давно стал удобной средой для разработки и автоматизации. Когда проект состоит из нескольких библиотек, требуется воспроизводимая сборка, а также периодическая пересборка с разными флагами (например, Debug/Release), ручное выполнение команд быстро превращается в рутину.

Ниже — практический подход к автоматизации сборки и кросс‑компиляции C/C++ проектов для Android ARM64 с помощью CMake и Ninja прямо из Termux. Мы сделаем:

  • подготовку toolchain для Android‑ARM64;
  • единый скрипт конфигурации и сборки;
  • организацию build‑директорий;
  • настройки флагов, install‑шага и логирования.

Предварительные требования

Убедитесь, что у вас установлены необходимые пакеты Termux. Терминалы и команды могут отличаться в зависимости от версии Termux и архитектуры устройства, но логика одна.

Установим базовые инструменты сборки:

pkg update
pkg install -y clang cmake ninja git make python

Для кросс‑компиляции C/C++ под Android ARM64 также часто нужен NDK. На практике есть два пути:

  • использовать готовый NDK и toolchain файл;
  • собрать окружение под вашу задачу и хранить NDK локально в проекте.

Рекомендуемый подход для автоматизации: хранить путь к NDK явно в переменных окружения или в конфигурационном файле проекта.

Структура проекта и идея “одного входа”

Хорошая автоматизация начинается с простой структуры:

  • toolchain/ — файлы toolchain CMake;
  • scripts/ — скрипты сборки;
  • build/ — папки сборки по профилям (release/debug).

Например:

my-project/
  toolchain/
  scripts/
  CMakeLists.txt
  src/ ...

Мы реализуем сборку командой вида:

  • ./scripts/build-android-arm64.sh --build-type Release
  • ./scripts/build-android-arm64.sh --build-type Debug

Скрипт сам сделает конфигурацию, сборку Ninja и (опционально) install‑шаг.

Подготовка NDK и toolchain для Android ARM64

Предположим, что NDK уже скачан и распакован. Пусть путь будет, например:

export ANDROID_NDK_HOME=$HOME/ndk/android-ndk-r26b

Для CMake создадим toolchain файл. В папке toolchain/ создайте файл, например android-arm64.cmake.

Важно: конкретные имена исполняемых файлов и layout NDK могут слегка отличаться по версиям. Ниже — рабочая стартовая схема для распространённых NDK.

mkdir -p toolchain
cat > toolchain/android-arm64.cmake <<'EOF'
# Toolchain file for Android ARM64 cross-compilation

# Ожидается, что ANDROID_NDK_HOME задано в окружении
if(NOT DEFINED ENV{ANDROID_NDK_HOME})
  message(FATAL_ERROR "ANDROID_NDK_HOME is not set")
endif()

set(ANDROID_NDK $ENV{ANDROID_NDK_HOME})

# Минимальная версия API (подстройте под задачу)
if(NOT DEFINED ANDROID_PLATFORM)
  set(ANDROID_PLATFORM android-24)
endif()

set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_VERSION ${ANDROID_PLATFORM})

# Архитектура
set(ANDROID_ABI arm64-v8a)

# Указываем компилятор из LLVM toolchain NDK
set(CMAKE_C_COMPILER   ${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android${ANDROID_PLATFORM_LEVEL}-clang)
set(CMAKE_CXX_COMPILER ${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android${ANDROID_PLATFORM_LEVEL}-clang++)

# Но часто PLATFORM android-24 – это строка. Удобнее задать уровень явно.
# Поэтому зададим ANDROID_PLATFORM_LEVEL как число, если его нет.
if(NOT DEFINED ANDROID_PLATFORM_LEVEL)
  # грубое извлечение цифр: android-24 -> 24
  string(REGEX REPLACE "^android-" "" ANDROID_PLATFORM_LEVEL "${ANDROID_PLATFORM}")
endif()

# Исправим пути компиляторов после вычисления уровня
set(CMAKE_C_COMPILER   ${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android${ANDROID_PLATFORM_LEVEL}-clang)
set(CMAKE_CXX_COMPILER ${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android${ANDROID_PLATFORM_LEVEL}-clang++)

# Корень sysroot
set(CMAKE_SYSROOT ${ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/sysroot)

set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_LIBRARY_ARCHITECTURE ${ANDROID_ABI})

# CMake режимы
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_ANDROID_ARCH_ABI ${ANDROID_ABI})
set(CMAKE_ANDROID_NDK ${ANDROID_NDK})

# Параметры поиска
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

# Сборка под Android обычно требует отключать некоторые host-специфичные штуки.
# Дополнительно можно настроить rpath и прочее, но это зависит от проекта.
EOF

Важное замечание: путь prebuilt/linux-x86_64 может отличаться (например, darwin-x86_64 или linux-aarch64). В NDK папка toolchains/llvm/prebuilt/ содержит конкретный таргет-хост. Если Termux работает на вашем устройстве Android (ARM), то NDK может быть распакован с хостом “linux-x86_64” (содержит бинарники x86_64), и тогда их исполнение на ARM без соответствующих возможностей будет проблемным.

В реальных сценариях Termux кросс‑компиляция обычно требует, чтобы в NDK были доступны исполняемые бинарники на архитектуре хоста. Практически: выбирайте NDK, подходящий для вашего хоста, или используйте сборку через корректную среду/контейнер/CI. В этой статье мы фокусируемся на механике CMake/Ninja и автоматизации, но обязательно проверьте совместимость toolchain с вашим “host” окружением.

Скрипт автоматической конфигурации и сборки

Создадим скрипт scripts/build-android-arm64.sh. Он будет:

  • принимать параметры;
  • формировать build‑директорию;
  • вызывать CMake с Ninja;
  • выполнять сборку;
  • при необходимости делать install.
mkdir -p scripts
cat > scripts/build-android-arm64.sh <<'EOF'
#!/data/data/com.termux/files/usr/bin/bash
set -euo pipefail

BUILD_TYPE="Release"
ANDROID_PLATFORM="android-24"
DO_INSTALL="false"

usage() {
  echo "Usage: $0 [--build-type Debug|Release] [--android-platform android-24|android-26] [--install]"
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --build-type)
      BUILD_TYPE="$2"
      shift 2
      ;;
    --android-platform)
      ANDROID_PLATFORM="$2"
      shift 2
      ;;
    --install)
      DO_INSTALL="true"
      shift 1
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown argument: $1"
      usage
      exit 1
      ;;
  esac
done

# Убедимся, что NDK задан
: "${ANDROID_NDK_HOME:?Please set ANDROID_NDK_HOME to your Android NDK path}"

# Корень проекта: предполагаем, что скрипт лежит в scripts/
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"

TOOLCHAIN_FILE="${ROOT_DIR}/toolchain/android-arm64.cmake"

# build dir: build/android-arm64--
BUILD_DIR="${ROOT_DIR}/build/android-arm64-${ANDROID_PLATFORM}-${BUILD_TYPE}"

mkdir -p "${BUILD_DIR}"

CMAKE_ARGS=(
  -G Ninja
  -DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}"
  -DCMAKE_BUILD_TYPE="${BUILD_TYPE}"
  -DANDROID_PLATFORM="${ANDROID_PLATFORM}"
)

# Часто полезно явно ограничить сборку только тем, что нужно.
# Если у проекта есть опции (например WITH_TESTS), добавьте их сюда.

echo "[INFO] Configure: ${BUILD_DIR}"
cmake -S "${ROOT_DIR}" -B "${BUILD_DIR}" "${CMAKE_ARGS[@]}"

echo "[INFO] Build (Ninja): ${BUILD_DIR}"
ninja -C "${BUILD_DIR}"

if [[ "${DO_INSTALL}" == "true" ]]; then
  # install prefix: build/<...>/install
  INSTALL_DIR="${BUILD_DIR}/install"
  echo "[INFO] Install to: ${INSTALL_DIR}"
  ninja -C "${BUILD_DIR}" install DESTDIR="${INSTALL_DIR}"
fi

echo "[DONE] Build finished."
EOF
chmod +x scripts/build-android-arm64.sh

Пример вызова скрипта

Перед первым запуском задайте путь к NDK:

export ANDROID_NDK_HOME=$HOME/ndk/android-ndk-r26b

Соберём Release:

./scripts/build-android-arm64.sh --build-type Release --android-platform android-24 --install

Соберём Debug:

./scripts/build-android-arm64.sh --build-type Debug --android-platform android-24

Настройки CMakeLists: чтобы кросс‑сборка была “правильной”

В целом CMake‑проекты сами неплохо кросс‑компилируются, но есть типовые места, где кросс‑сборка ломается:

  • проверки, которые выполняются на хосте (try_run);
  • поиск пакетов через find_package (нужно, чтобы они были доступны для target);
  • использование библиотек “host” вместо “target”.

На практике полезно:

  • для внешних зависимостей использовать vendored сборку или заранее собранные Android‑артефакты;
  • избегать try_run и предпочтительно использовать try_compile или ручные параметры.

Небольшой фрагмент (пример для параметров проекта):

# Пример: если проект поддерживает отключение тестов
option(WITH_TESTS "Build tests" OFF)

if(WITH_TESTS)
  add_subdirectory(tests)
endif()

Оптимизация под Android: компилятор, флаги, LTO

Для Android часто хотят аккуратно управлять оптимизациями, бинарным размером и отладкой. В зависимости от вашего проекта можно добавлять:

  • флаги оптимизации;
  • контроль отладочной информации;
  • LTO (link-time optimization) — осторожно, т.к. может увеличивать время сборки.

Пример внесения флагов в CMake (универсальный подход):

if(ANDROID)
  # Базовые настройки
  add_compile_options(-fPIC)

  # Пример: можно включить LTO через опции проекта
  option(ENABLE_LTO "Enable LTO" OFF)
  if(ENABLE_LTO)
    set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
  endif()
endif()

Вызов скрипта тогда можно дополнить параметром, если ваш проект принимает -DENABLE_LTO=ON.

Логи, воспроизводимость и “быстрый повтор”

Автоматизация ценна тем, что результаты воспроизводимы. Для этого удобно:

  • использовать фиксированную версию NDK;
  • фиксировать API level через ANDROID_PLATFORM;
  • сохранять команды конфигурации;
  • контролировать переменные окружения.

Мини‑улучшение в скрипт: логировать команду CMake и Ninja. Например, можно добавить перенаправление stdout/stderr в файл в build‑директории.

cmake -S "${ROOT_DIR}" -B "${BUILD_DIR}" "${CMAKE_ARGS[@]}" 
  |& tee "${BUILD_DIR}/cmake-config.log"

И аналогично для сборки:

ninja -C "${BUILD_DIR}" |& tee "${BUILD_DIR}/build.log"

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

  • Не находится компилятор: проверьте, что toolchain файл указывает на существующий -clang и -clang++.
  • Ошибки sysroot: убедитесь, что в CMAKE_SYSROOT корректный путь. Для разных NDK иногда меняется layout.
  • Сборка идет, но не линкуется: проверьте, какие библиотеки подхватываются. Убедитесь, что нет “host” зависимостей.
  • try_run ломает конфигурацию: в кросс‑сценариях лучше отказаться от выполнения на хосте и заменить на try_compile или задать значения вручную.

Заключение

Автоматизация сборки и кросс‑компиляции C/C++ под Android ARM64 в Termux с использованием CMake и Ninja позволяет быстро и воспроизводимо получать артефакты для разных профилей (Debug/Release) и уровней API. Ключевые элементы решения — корректный toolchain, единый скрипт конфигурации/сборки и дисциплина в настройках проекта (особенно по зависимостям и поведению проверок в CMake).

Если вам нужно адаптировать скрипт под конкретный NDK, репозиторий и зависимости (например, OpenSSL, Boost, протоколы и т.п.), команда РыбинскЛАБ поможет: консультации и настройка CI/сборочных пайплайнов, правка CMake под кросс‑среды и сопровождение процесса сборки для ваших Android‑ARM64 целей.

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

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

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

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