Termux стал де-факто стандартом для разработки на Android: оболочка, пакеты, удобные сборочные цепочки. Однако когда задача выходит за рамки простых скриптов и упирается в нативные компоненты (.c/.cpp), возникает вопрос: как собрать библиотеку так, чтобы она корректно работала на Android и имела нужный формат артефакта.
В этой статье — практичный и юридически корректный (в рамках общих принципов разработки) подход к кросс‑компиляции нативных библиотек для Android в Termux: от исходного кода до итогового ELF‑модуля (.so) под конкретный ABI (arm64-v8a, armeabi-v7a, x86_64 и т.д.).
Материал ориентирован на типовой пайплайн: подготовка окружения, выбор ABI, сборка через NDK toolchain, проверка артефакта и разбор распространённых проблем.
Термины и целевой результат
Под нативной библиотекой в контексте Android обычно понимают shared object — файл вида libname.so. Формально это ELF‑модуль, содержащий код для конкретной архитектуры.
Ключевые точки:
- ABI (Application Binary Interface) определяет архитектуру и соглашения вызовов (например, arm64-v8a).
- NDK toolchain — компилятор и системные заголовки/линковка, рассчитанные под Android.
- Кросс‑компиляция означает, что сборка выполняется на одной платформе (Termux на Android), но бинарник нацелен на ABI/SDK/ABI‑настройки Android так, как ожидает загрузчик.
Предварительные требования
Для устойчивой сборки рекомендовано заранее определиться с тремя параметрами:
- ABI (например,
arm64-v8aилиarmeabi-v7a). - Минимальный API level (например, 21 для arm64 обычно удобно).
- Тип сборки: CMake/Autotools/прямой вызов компилятора.
Также учитывайте, что Android‑платформа строго ограничивает набор системных вызовов и доступность некоторых API — именно поэтому обычно используют Android NDK.
Шаг 1. Подготовка Termux и базовых пакетов
Обновите пакеты и установите базовые утилиты. В разных сборках Termux названия пакетов могут незначительно отличаться, но логика сохраняется.
pkg update && pkg upgrade -y
pkg install -y git wget curl tar unzip clang lld cmake make autoconf automake libtool pkg-configПримечание: для кросс‑компиляции под Android обычно потребуется NDK. Termux сам по себе может выступать лишь как среда запуска сборочных скриптов и линковки, но toolchain корректнее брать из NDK.
Шаг 2. Получение и раскладка Android NDK
Есть два типовых сценария:
- Использовать NDK, который уже установлен (например, в составе Android Studio).
- Скачать NDK и разархивировать вручную.
Для Termux удобно иметь NDK в доступной директории, например /data/data/.../files/ или в вашей рабочей папке.
Примерно это выглядит так (подставьте URL/путь под вашу ситуацию):
mkdir -p ~/android
cd ~/android
# Скачивание NDK (пример, используйте актуальный URL)
wget -O ndk.zip "https://example.com/android-ndk-rXX.zip"
unzip ndk.zip -d ndkПосле распаковки проверьте, где лежит toolchain:
ls ~/android/ndk//toolchains/llvm/prebuilt//bin/Внутри NDK путь к компиляторам будет примерно таким: .../toolchains/llvm/prebuilt/host-tag/bin.
Шаг 3. Выбор ABI и настройка параметров сборки
Выберите ABI и целевой API level. Пример ниже показывает схему установки переменных под arm64-v8a.
NDK_ROOT=~/android/ndk/ndk-bundle # подставьте фактический путь
API=21
ABI=arm64-v8a
# Для архитектур NDK соответствия выглядят так:
# arm64-v8a -> aarch64-linux-android
# armeabi-v7a -> armv7a-linux-androideabi
# x86_64 -> x86_64-linux-android
# i686 -> i686-linux-android
TARGET_TRIPLE=aarch64-linux-androidДалее нужно найти путь к бинарникам clang в NDK. Типичный хост‑тег для Android‑устройств может отличаться. Универсальнее — извлечь его автоматически.
HOST_TAG=$(ls "$NDK_ROOT/toolchains/llvm/prebuilt" | head -n 1)
CLANG_BIN="$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG/bin"
CC="$CLANG_BIN/${TARGET_TRIPLE}${API}-clang"
CXX="$CLANG_BIN/${TARGET_TRIPLE}${API}-clang++"
STRIP="$CLANG_BIN/${TARGET_TRIPLE}-strip"
echo $CC
echo $CXXЕсли конкретный ${TARGET_TRIPLE}${API}-clang не находится, проверьте доступные бинарники в каталоге $CLANG_BIN и скорректируйте шаблон именования. В разных версиях NDK формат может слегка варьироваться.
Шаг 4. Сборка библиотеки CMake (типовой современный сценарий)
Если ваша библиотека использует CMake, это самый удобный путь. Рассмотрим общую структуру:
- создаём отдельную директорию сборки;
- передаём toolchain параметры;
- собираем;
- проверяем, что появилась
.so.
Примерно так:
PROJECT_ROOT=~/src/mylib
BUILD_DIR=$PROJECT_ROOT/build-android-$ABI
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
cmake "$PROJECT_ROOT" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=$API \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$API \
-DCMAKE_C_COMPILER="$CC" \
-DCMAKE_CXX_COMPILER="$CXX" \
-DCMAKE_STRIP="$STRIP" \
-DBUILD_SHARED_LIBS=ON \
-DCMAKE_POSITION_INDEPENDENT_CODE=ONЗапуск сборки:
cmake --build . -- -j$(nproc)Ожидаемый результат: файл вида libmylib.so (путь зависит от проекта). Часто он оказывается в $BUILD_DIR/lib/ или $BUILD_DIR/<subdir>/.
find "$BUILD_DIR" -name ".so" -maxdepth 6
Шаг 5. Сборка библиотеки с Autotools (если CMake нет)
Для Autotools обычно применяется схема configure → make с указанием cross tools и host triplet. Пример (концептуальный):
PROJECT_ROOT=~/src/mylib
BUILD_DIR=~/tmp/build-autotools-$ABI
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
# Псевдо-значения: подстройте под ваш проект
HOST=$TARGET_TRIPLE
"$PROJECT_ROOT/configure" \
--host="$HOST" \
--build=$(uname -m)-linux-gnu \
CC="$CC" \
CXX="$CXX" \
CFLAGS="-fPIC -O2" \
CXXFLAGS="-fPIC -O2" \
--enable-shared \
--disable-static
make -j$(nproc)
find . -name ".so" -maxdepth 4Autotools проекты сильно различаются по параметрам configure. Если сборка падает, ориентируйтесь на сообщения о том, что именно не найдено (заголовки, функции, библиотеки) и устраняйте это корректной настройкой include/lib путей или отключением опций, требующих неподдерживаемых компонентов Android.
Шаг 6. Проверка ELF‑модуля под нужный ABI
Кросс‑компиляция считается успешной, когда:
- появился
.so; - архитектура внутри ELF соответствует ABI;
- зависимости корректно найдены (в рамках вашего механизма загрузки/упаковки).
Проверка архитектуры ELF:
# file обычно покажет архитектуру
file path/to/libmylib.so
# readelf поможет посмотреть заголовки
readelf -h path/to/libmylib.so
readelf -A path/to/libmylib.so || true
# ldd на Android часто не работает как на Linux, поэтому ориентируйтесь на readelf
readelf -d path/to/libmylib.so | grep -E "NEEDED|RPATH|RUNPATH"Если вы видите несоответствие ABI (например, arm64‑устройство, а библиотека собрана под ARMv7), приложение/нагрузка загрузчика обычно завершится ошибкой.
Шаг 7. Упаковка .so в структуру для Android‑проекта
Когда вы хотите использовать библиотеку в Android приложении, обычно размещают её в:
app/src/main/jniLibs/<abi>/libname.so
Примерно:
OUT_SO=/path/to/libmylib.so
ANDROID_JNILIBS=~/android-project/app/src/main/jniLibs/$ABI
mkdir -p "$ANDROID_JNILIBS"
cp "$OUT_SO" "$ANDROID_JNILIBS/$(basename "$OUT_SO")"Далее корректность проверяется уже на стороне Android сборки и тестирования.
Распространённые ошибки и как их диагностировать
- Linker errors (undefined references): чаще всего из-за несоответствия набора флагов или опций сборки (например, неправильные компоненты включены/исключены), либо не нашли зависимые библиотеки. Проверьте параметры линковки и порядок библиотек.
- Отсутствие PIC: для shared object обычно требуется
-fPIC. Включайте-DCMAKE_POSITION_INDEPENDENT_CODE=ON(для CMake) илиCFLAGS="-fPIC ...". - Несовпадение ABI: проверяйте ELF заголовок через
fileиreadelf. - Проблемы с Sysroot/заголовками: иногда configure/cmake пытается использовать хост‑заголовки вместо Android. Убедитесь, что указаны
CMAKE_SYSTEM_NAME=Androidи выбран правильный NDK/compilers.
Практический пример: мульти‑ABI сборка (серия конфигураций)
Если библиотека должна поставляться в нескольких ABI, обычно делают цикл и собирают по одному разу на ABI.
LIBSRC=~/src/mylib
build_one() {
local ABI=$1
local API=$2
local TARGET_TRIPLE
case "$ABI" in
arm64-v8a) TARGET_TRIPLE=aarch64-linux-android ;;
armeabi-v7a) TARGET_TRIPLE=armv7a-linux-androideabi ;;
x86_64) TARGET_TRIPLE=x86_64-linux-android ;;
i686) TARGET_TRIPLE=i686-linux-android ;;
*) echo "Unknown ABI: $ABI"; return 1;;
esac
local HOST_TAG
HOST_TAG=$(ls "$NDK_ROOT/toolchains/llvm/prebuilt" | head -n 1)
local CLANG_BIN="$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG/bin"
local CC="$CLANG_BIN/${TARGET_TRIPLE}${API}-clang"
local CXX="$CLANG_BIN/${TARGET_TRIPLE}${API}-clang++"
local BUILD_DIR=$LIBSRC/build-$ABI
rm -rf "$BUILD_DIR" && mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
cmake "$LIBSRC" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_SYSTEM_NAME=Android \
-DCMAKE_SYSTEM_VERSION=$API \
-DANDROID_ABI=$ABI \
-DANDROID_PLATFORM=android-$API \
-DCMAKE_C_COMPILER="$CC" \
-DCMAKE_CXX_COMPILER="$CXX" \
-DBUILD_SHARED_LIBS=ON \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
cmake --build . -- -j$(nproc)
}
NDK_ROOT=~/android/ndk/ndk-bundle
build_one arm64-v8a 21
build_one armeabi-v7a 21
build_one x86_64 21После этого останется собрать все .so в нужные директории для jniLibs.
Заключение
Кросс‑компиляция нативных библиотек для Android в Termux — это управляемый процесс, если вы последовательно выбираете ABI, корректно настраиваете NDK toolchain и проверяете итоговый ELF‑модуль на соответствие архитектуре. Типовой путь выглядит так: подготовка окружения → настройка компиляторов под выбранный ABI/API → сборка (через CMake или Autotools) → проверка .so и упаковка в формат, удобный для Android‑проекта.
Если вы хотите быстрее довести проект до рабочего состояния, в РыбинскЛАБ мы помогаем с настройкой toolchain, подбором флагов сборки под конкретные ABI, диагностикой linker/ELF‑ошибок и подготовкой библиотеки для интеграции в ваше Android‑приложение.