Termux давно перестал быть «только терминалом»: это полноценная среда для сборки и тестирования утилит на C/C++ прямо на смартфоне. Однако разработка быстро упирается в три задачи: правильная сборка (toolchain и флаги), отладка (symbol’ы, gdb/lldb, трассировка), и качество кода (статический анализ и профилирование). В этой статье соберём типовой пайплайн разработки собственных утилит в Termux: от кросс‑компиляции до анализа производительности.
Материал ориентирован на законное использование: без обхода ограничений и без инструкций, нарушающих политику платформ. Если вам потребуется соединение между устройствами (например, для тестов в локальной сети), в конце дам безопасный вариант с VPN как инструментом для создания локальной сети.
Подготовка Termux: репозитории, компилятор и базовые инструменты
Начинаем с обновления пакетов и установки toolchain. В Termux лучше использовать Clang/LLVM как основной компилятор и набор утилит для отладки и анализа.
pkg update -y && pkg upgrade -y
pkg install -y clang llvm gdb make cmake ninja git python termux-apiДля статического анализа и профилирования также пригодятся дополнительные пакеты. Набор можно расширять, но стартовый набор выглядит так:
pkg install -y clang-tools-extra cppcheck lld llvm-profdata gperftoolsЕсли каких-то пакетов в вашем окружении нет (это зависит от версии Termux и репозиториев), можно заменить их аналогами: например, cppcheck обычно доступен, а для профилирования иногда используют perf‑подобные инструменты из LLVM или gperftools.
Структура проекта: чтобы сборка не превращалась в ручной набор команд
Рекомендуемый подход — собрать минимальный CMake‑проект, чтобы:
- компиляция и линковка были воспроизводимыми;
- сборка Debug/Release переключалась одним параметром;
- легко подключались статические анализаторы.
Создадим структуру:
mkdir -p ~/dev/cpputils/{src,include,build}
cd ~/dev/cpputilsСкелет проекта:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(cpputils LANGUAGES C CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Базовые флаги
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
add_executable(mytool src/main.cpp)
target_include_directories(mytool PRIVATE include)
# Debug символы и оптимизации по ситуации
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(mytool PRIVATE -g -O0)
else()
target_compile_options(mytool PRIVATE -O2)
endif()Пример утилиты на C++: минимально, но с заделом под диагностику
Сделаем утилиту, которая принимает аргументы и выполняет простую проверку. Код будет пригоден и для статического анализа, и для профилирования.
#include <iostream>
#include <string>
#include <vector>
#include <stdexcept>
static int run(const std::vector<std::string>& args) {
if (args.empty()) return 0;
const std::string& cmd = args[0];
if (cmd == "sum") {
long long s = 0;
for (size_t i = 1; i < args.size(); ++i) {
size_t pos = 0;
long long v = 0;
try {
v = std::stoll(args[i], &pos, 10);
} catch (const std::exception&) {
throw std::runtime_error("Bad integer: " + args[i]);
}
if (pos != args[i].size()) {
throw std::runtime_error("Not a pure integer: " + args[i]);
}
s += v;
}
std::cout << s << std::endl;
return 0;
}
if (cmd == "ping") {
std::cout << "pong" << std::endl;
return 0;
}
std::cerr << "Unknown command: " << cmd << std::endl;
return 2;
}
int main(int argc, char** argv) {
std::vector<std::string> args;
args.reserve(static_cast<size_t>(argc));
for (int i = 1; i < argc; ++i) {
args.emplace_back(argv[i]);
}
try {
return run(args);
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}Сохраняем как src/main.cpp.
Сборка: Debug/Release и корректные флаги для отладки
Собираем Debug (с символами), затем Release (для скорости). Для CMake удобно делать отдельные директории сборки.
cd ~/dev/cpputils
cmake -S . -B build/debug -DCMAKE_BUILD_TYPE=Debug -G Ninja
cmake --build build/debugЗапуск:
./build/debug/mytool ping
./build/debug/mytool sum 1 2 3 4Release:
cmake -S . -B build/release -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build build/releaseКросс‑компиляция vs нативная сборка в Termux
В Termux вы обычно компилируете для архитектуры устройства (ARM/ARM64). Это не «кросс‑компиляция» в строгом смысле, но на практике важно понимать разницу:
- Нативная сборка: целевая архитектура совпадает с устройством.
- Кросс‑компиляция: вы собираете бинарник для другой архитектуры/ABI (например, для размещения артефактов под несколько целей).
Если вам нужно собрать под другую архитектуру, обычно используют отдельный toolchain и sysroot. На практике в рамках Termux удобнее начинать с нативной сборки и уже затем, при необходимости, переносить сборку в отдельную среду (например, на ПК) и копировать артефакты в Termux. Для типовых «утилит разработчика» нативная сборка чаще всего оптимальна по времени.
Тем не менее, если у вас есть задача собрать несколько вариантов под разные ABI, можно рассмотреть подход: сборка в CMake с явным указанием target и toolchain‑файла. Это тема отдельного цикла, но базовая идея: -DCMAKE_TOOLCHAIN_FILE=... и подготовка sysroot. В большинстве кейсов начинайте с нативной сборки и используйте сборку в Release для переносимых артефактов.
Отладка: gdb, символы и полезные практики
Отладка в Termux обычно делается через gdb. Главное — убедиться, что бинарник собран с -g и без чрезмерных оптимизаций (для Debug это обеспечивается флагами).
Запустим gdb и поставим точку останова:
gdb --args ./build/debug/mytool sum 1 2 3 xВнутри gdb:
(gdb) break main
(gdb) run
(gdb) backtrace
(gdb) continueПолезные команды при анализе:
backtrace— получить стек вызовов;info locals— локальные переменные;print <expr>— значения выражений;bt full— расширенная трасса;
Если у вас включены исключения, иногда полезно поймать момент броска. В gdb можно попытаться:
(gdb) catch throw
(gdb) runА для «разглаживания» отладочных проблем используйте Debug сборку и старайтесь не запускать оптимизированный Release под gdb.
Статический анализ: clang-tidy и cppcheck для C/C++ качества
Статический анализ помогает поймать ошибки до запуска и ускоряет ревью кода. Есть два разных класса инструментов:
- clang‑tools (например, clang‑tidy) — ориентированы на семантический анализ;
- cppcheck — быстрые проверки на типичные проблемы.
Для clang‑подхода часто используют clang-tidy. В Termux доступность зависит от набора пакетов, но можно попробовать:
pkg install -y clang-tidyЕсли пакет доступен, запускаем по проекту (примерно):
clang-tidy src/main.cpp -- -std=c++17 -IincludeСмысл команды: clang-tidy анализирует файл, учитывая опции компиляции. Для расширенного набора файлов обычно используют compile_commands.json из CMake.
Чтобы получить compile_commands.json, добавьте флаг CMake (обычно он генерируется автоматически современными версиями при использовании CMake генераторов). Либо можно сделать это явно:
cmake -S . -B build/debug -DCMAKE_BUILD_TYPE=Debug -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ONТогда clang-tidy можно запускать в режиме по всему проекту через compile commands (зависит от наличия параметров в вашей версии clang-tidy):
clang-tidy -p build/debug src/main.cppCppcheck запускаем проще:
cppcheck --enable=warning,performance,portability,style --std=c++17 --I include src/main.cppПодсказка по работе: сначала исправляйте ошибки уровня warning и потенциальные undefined behavior. Статический анализ не заменяет тесты, но заметно сокращает количество «глупых» багов.
Профилирование: где тратить время и как измерять эффект
Профилирование в Termux обычно решает две задачи: понять «горячие участки» и проверить, помогли ли оптимизации. На практике удобнее начать с CPU‑профилирования и замеров времени.
Быстрый практический вариант — gperftools (heap/profile) или профайлеры из экосистемы LLVM. Рассмотрим подход с gperftools (если он доступен и вы установили gperftools).
Например, для heap/profile нужны специальные директивы при линковке. Более универсально для начала — просто измерить время запуска и итераций (микробенчмарк), а затем переходить к профилировщику.
Добавим в проект простой режим «нагрузки» и измерение. Например, расширим код так, чтобы можно было прогнать сумму на большом количестве чисел и сравнить Debug/Release.
// Вариант: быстрое измерение времени (время работы, грубо)
#include <chrono>
static int bench_sum(int n) {
std::vector<std::string> args;
args.reserve(static_cast<size_t>(n + 1));
args.push_back("sum");
for (int i = 0; i < n; ++i) args.push_back(std::to_string(i));
auto start = std::chrono::high_resolution_clock::now();
int rc = run(args);
auto end = std::chrono::high_resolution_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
std::cerr << "bench_sum: rc=" << rc << ", ms=" << ms << std::endl;
return rc;
}Дальше компилируете в Release и сравниваете. Такой подход не заменяет полноценный профайлер, но быстро показывает, «куда смотреть». Когда найдете горячее место, тогда уже включайте глубокую диагностику профилировщиком.
Проверка совместимости: запуск, зависимости, размер бинарника
Для утилит на Termux важно помнить:
- статическая линковка может быть сложной (зависит от библиотек);
- динамические зависимости часто «подтягиваются» через стандартную среду Termux;
- размер бинарника — критерий, особенно на мобильных устройствах.
Проверить зависимости можно утилитой ldd:
ldd ./build/release/mytoolЕсли вы хотите уменьшить бинарник, ориентируйтесь на Release‑флаги и аккуратную компиляцию. Для CMake можно подключить дополнительные оптимизации (но для начала не перегружайте флаги).
Сборка «как на ПК»: удобный запуск через scripts и единые команды
Чтобы не вводить команды руками, полезно завести скрипты.
# build.sh
#!/data/data/com.termux/files/usr/bin/bash
set -e
cd ~/dev/cpputils
cmake -S . -B build/release -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build build/release
./build/release/mytool pingСделайте исполняемым:
chmod +x build.sh
./build.shТестирование в локальной сети: безопасный подход с VPN (для подключения устройств)
Если вы тестируете утилиты на нескольких устройствах (например, отправляете запросы на смартфон с серверной частью или используете локальную точку для бенчмарков), иногда удобен VPN для создания локальной сети между устройствами. Это не «обход блокировок», а инфраструктурный способ организовать связность и стабильные адреса.
Практически это обычно сводится к: выбрать VPN‑приложение, создать соединение «device-to-device» или в рамках одной локальной сети, настроить firewall порты и проверить доступность. Конкретные шаги зависят от выбранного VPN‑клиента, но принцип везде одинаков: сначала достигаем связности в локальной сети, затем тестируем вашу C/C++‑утилиту (например, HTTP/IPC поверх TCP).
Типичный рабочий цикл разработчика
- Собираем Debug:
cmake --build ...с символами. - Пишем тестовый сценарий запуска и воспроизводим проблему.
- Отлаживаем в gdb: точка останова, стек, локальные переменные.
- Прогоняем статический анализ: clang-tidy/cppcheck.
- Переходим в Release и измеряем производительность.
- Устраняем узкие места и сравниваем метрики.
Заключение
Разработка и отладка собственных C/C++‑утилит в Termux реальны и практичны: правильная структура проекта (CMake), аккуратные Debug/Release сборки, полноценная отладка через gdb, регулярный статический анализ (clang-tidy и/или cppcheck) и понятный цикл профилирования дают ощутимый выигрыш в качестве и скорости разработки. Начните с нативной сборки под архитектуру вашего устройства, и только при необходимости переходите к сложным сценариям кросс‑компиляции.
Если хотите ускорить настройку среды, подобрать toolchain, наладить pipeline статического анализа или помочь с профилированием конкретной утилиты — обращайтесь в РыбинскЛАБ. Мы помогаем с разработкой, аудитом C/C++‑кода и инженерной настройкой окружений под ваши цели.