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++‑утилит в Termux: кросс‑компиляция, статический анализ и профилирование

Практическое руководство по разработке C/C++‑утилит в Termux: сборка и отладка, настройка toolchain, статический анализ (clang-tidy, cppcheck), профилирование и производительность — от идеи до рабочего бинарника.

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 4

Release:

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.cpp

Cppcheck запускаем проще:

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).

Типичный рабочий цикл разработчика

  1. Собираем Debug: cmake --build ... с символами.
  2. Пишем тестовый сценарий запуска и воспроизводим проблему.
  3. Отлаживаем в gdb: точка останова, стек, локальные переменные.
  4. Прогоняем статический анализ: clang-tidy/cppcheck.
  5. Переходим в Release и измеряем производительность.
  6. Устраняем узкие места и сравниваем метрики.

Заключение

Разработка и отладка собственных C/C++‑утилит в Termux реальны и практичны: правильная структура проекта (CMake), аккуратные Debug/Release сборки, полноценная отладка через gdb, регулярный статический анализ (clang-tidy и/или cppcheck) и понятный цикл профилирования дают ощутимый выигрыш в качестве и скорости разработки. Начните с нативной сборки под архитектуру вашего устройства, и только при необходимости переходите к сложным сценариям кросс‑компиляции.

Если хотите ускорить настройку среды, подобрать toolchain, наладить pipeline статического анализа или помочь с профилированием конкретной утилиты — обращайтесь в РыбинскЛАБ. Мы помогаем с разработкой, аудитом C/C++‑кода и инженерной настройкой окружений под ваши цели.

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

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

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

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