Cmake

CMake: настройка кросс-компиляции Qt-проекта под ARM

Введение

Кросс-компиляция — сборка приложения на одной архитектуре (обычно x86_64) для запуска на другой (ARM, часто встречается в embedded Linux устройствах, Raspberry Pi, промышленных контроллерах) — требует toolchain-файла CMake, который указывает компилятор, sysroot и пути поиска библиотек для целевой архитектуры. Для Qt-проектов добавляется необходимость использовать кросс-собранную версию самого Qt для целевой платформы. В статье рассмотрим базовую настройку кросс-компиляции под ARM.

Концепция

Toolchain-файл CMake задаёт переменные CMAKE_SYSTEM_NAME (целевая ОС), CMAKE_SYSTEM_PROCESSOR (целевая архитектура), пути к кросс-компилятору (CMAKE_C_COMPILER, CMAKE_CXX_COMPILER) и sysroot целевой системы (CMAKE_SYSROOT), содержащий заголовки и библиотеки целевой платформы (включая саму кросс-собранную версию Qt). CMake использует эти настройки, чтобы все вызовы find_package, find_library искали зависимости в sysroot целевой системы, а не в системных путях хост-машины.

Сам Qt должен быть либо кросс-собран вручную (процесс, требующий настройки qt-configure-module с указанием toolchain), либо получен через готовый SDK производителя платформы (например, Qt для встраиваемых Linux-плат часто поставляется через Boot2Qt или Yocto-рецепты).

Пример кода: CMake

# arm-toolchain.cmake — toolchain-файл для кросс-компиляции под ARM Linux
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(TOOLCHAIN_PREFIX arm-linux-gnueabihf)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)

set(CMAKE_SYSROOT /opt/arm-sysroot)
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})

# Программы (например, moc, rcc) ищем на ХОСТ-машине, а не в sysroot
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Библиотеки и заголовки ищем ТОЛЬКО в sysroot целевой системы
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

# Путь к кросс-собранной версии Qt для ARM
set(CMAKE_PREFIX_PATH /opt/arm-sysroot/usr/lib/arm-linux-gnueabihf/qt6)
# Конфигурация и сборка с использованием toolchain-файла
cmake -B build-arm -S . \
    -DCMAKE_TOOLCHAIN_FILE=arm-toolchain.cmake \
    -DCMAKE_BUILD_TYPE=Release

cmake --build build-arm

# Дальнейшее копирование на устройство, например через scp
scp build-arm/MyApp root@target-device:/usr/bin/

Пояснения к коду

Ключевые переменные CMAKE_FIND_ROOT_PATH_MODE_* решают принципиальную проблему кросс-компиляции: некоторые инструменты (например, moc, rcc, uic для Qt) должны выполняться на хост-машине во время сборки (они генерируют код, который затем компилируется кросс-компилятором), а библиотеки и заголовки, с которыми линкуется итоговый бинарник, должны браться исключительно из sysroot целевой архитектуры. Установка CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER явно говорит CMake искать исполняемые инструменты (которые запускаются во время сборки) вне ограничений sysroot — то есть на хосте.

CMAKE_PREFIX_PATH, указывающий на кросс-собранную версию Qt внутри sysroot, позволяет find_package(Qt6 ...) найти именно ARM-версию Qt, а не случайно подхватить версию Qt, установленную на хост-машине для разработки под x86_64.

Подводные камни

  • Случайное использование инструментов хоста вместо целевых библиотек. Если CMAKE_FIND_ROOT_PATH_MODE_LIBRARY не установлен в ONLY, CMake может найти и слинковать библиотеку из системных путей хоста (x86_64), что приведёт либо к ошибке линковки несовместимых архитектур, либо, в редких случаях, к «тихой» сборке бинарника, который не запустится на целевом устройстве.
  • Несовпадение версий glibc между sysroot и реальным целевым устройством. Кросс-собранный бинарник линкуется с версией стандартной библиотеки C из sysroot — если на реальном устройстве версия glibc старше, бинарник может не запуститься с ошибкой о неразрешённых символах версий (GLIBC_2.XX not found).
  • moc/uic/rcc недоступны или несовместимы. Эти инструменты должны быть собраны под архитектуру хоста и совпадать по версии с целевой версией Qt — использование moc от другой версии Qt может генерировать несовместимый с целевыми заголовками код.
  • Отладка на целевом устройстве сложнее обычной. Для отладки кросс-собранного приложения на ARM-устройстве нужен gdbserver на устройстве и кросс-собранный gdb (или интеграция через IDE с удалённой отладкой) — обычный локальный gdb хост-машины не умеет анализировать бинарники другой архитектуры напрямую.