Cmake

Сборка Qt-проекта под разные платформы с помощью CMake presets

Введение

Конфигурация CMake-проекта под несколько целевых платформ (Windows MSVC, Linux GCC, macOS Clang, кросс-компиляция под embedded) исторически решалась через длинные команды с десятками флагов -D, которые передавались вручную или прятались в shell-скриптах. CMake Presets (начиная с CMake 3.19+) формализует эти конфигурации в декларативный JSON-файл, который хранится в репозитории и понимается современными IDE (CLion, Visual Studio, VS Code) без дополнительной настройки. В статье разберём, как описать несколько профилей сборки Qt-проекта через presets.

Концепция

Файл CMakePresets.json в корне проекта описывает именованные presets для конфигурации (configurePresets) и сборки (buildPresets). Каждый preset может задавать генератор, переменные CMake (включая CMAKE_TOOLCHAIN_FILE, CMAKE_PREFIX_PATH для Qt и vcpkg), тип сборки (Debug/Release) и условия применимости (condition), например — preset активен только на конкретной ОС. Presets могут наследовать друг от друга через inherits, что позволяет выносить общие настройки в базовый preset и переопределять только различающиеся параметры.

Это устраняет необходимость в платформо-специфичных bat/sh скриптах и делает процесс сборки воспроизводимым одной командой cmake --preset <name> независимо от того, кто и на какой машине её запускает.

Пример кода: Сборка Qt-проекта под

{
    "version": 6,
    "configurePresets": [
        {
            "name": "base",
            "hidden": true,
            "binaryDir": "${sourceDir}/build/${presetName}",
            "cacheVariables": {
                "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
            }
        },
        {
            "name": "windows-msvc-debug",
            "inherits": "base",
            "generator": "Visual Studio 17 2022",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Debug",
                "CMAKE_PREFIX_PATH": "C:/Qt/6.7.0/msvc2019_64"
            },
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Windows"
            }
        },
        {
            "name": "linux-gcc-release",
            "inherits": "base",
            "generator": "Ninja",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release",
                "CMAKE_PREFIX_PATH": "/opt/Qt/6.7.0/gcc_64",
                "CMAKE_CXX_COMPILER": "g++"
            },
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Linux"
            }
        },
        {
            "name": "macos-clang-release",
            "inherits": "base",
            "generator": "Ninja",
            "cacheVariables": {
                "CMAKE_BUILD_TYPE": "Release",
                "CMAKE_PREFIX_PATH": "/opt/homebrew/opt/qt"
            },
            "condition": {
                "type": "equals",
                "lhs": "${hostSystemName}",
                "rhs": "Darwin"
            }
        }
    ],
    "buildPresets": [
        { "name": "windows-msvc-debug", "configurePreset": "windows-msvc-debug" },
        { "name": "linux-gcc-release", "configurePreset": "linux-gcc-release" },
        { "name": "macos-clang-release", "configurePreset": "macos-clang-release" }
    ]
}
# Использование presets
cmake --preset linux-gcc-release
cmake --build --preset linux-gcc-release

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

Preset base помечен как hidden: true — он не предназначен для прямого использования, а служит общим набором настроек (директория сборки, экспорт compile_commands.json для языковых серверов и IDE), от которого наследуются платформо-специфичные presets через inherits. Каждый конкретный preset задаёт генератор, путь к нужной сборке Qt через CMAKE_PREFIX_PATH и условие condition, ограничивающее его применимость конкретной ОС — это предотвращает случайный выбор неподходящего preset на чужой платформе.

Команды cmake --preset и cmake --build --preset заменяют длинные ручные вызовы с флагами, делая процесс сборки идентичным для любого разработчика и CI-агента, использующего этот файл.

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

  • CMakePresets.json версионируется в Git, а пользовательские локальные пути (например, специфичный путь к Qt на конкретной машине разработчика) — нет. Для индивидуальных переопределений путей существует CMakeUserPresets.json, который должен быть в .gitignore и наследовать общие presets из CMakePresets.json.
  • Несовпадение версии схемы ("version") с установленной версией CMake. Новые возможности presets (например, condition) появились не в первой версии формата — слишком старая версия CMake в CI может не поддерживать нужную версию схемы, выдавая неясную ошибку парсинга.
  • Жёстко прописанные абсолютные пути к Qt в самом CMakePresets.json ломают переносимость между машинами с разной структурой каталогов — там, где путь действительно различается у каждого разработчика, его стоит выносить в CMakeUserPresets.json или передавать через переменные окружения внутри preset ("environment").
  • Condition не означает автоматический выбор подходящего preset. Если запустить cmake --preset windows-msvc-debug на Linux, CMake выдаст ошибку валидации condition — это защита от ошибки, а не автоматический подбор платформы; правильный preset для CI всё равно нужно выбирать явно по имени, соответствующему агенту.