CMake 4.0: что нового и как правильно собирать современные C++ проекты

Введение

Мы живем в удивительное время. C++26 уже здесь, привнося в язык рефлексию и долгожданные сетевые возможности, а инструменты сборки наконец-то догнали и перегнали потребности разработчиков. Долгие годы CMake был тем самым «необходимым злом» — могучим, но неповоротливым монстром, скрипты на котором было сложно читать, а ошибки конфигурации пугали новичков.


Новая парадигма: File-Based API 2.0 и тотальная прозрачность

Проблема прошлого: Отладка сложных сборок была похожа на шаманство. Нужно было запускать make, смотреть вывод, гадать, почему линковщик ругается.

Решение в CMake 4.0: API, работающий с файлами, был кардинально расширен. Теперь это не просто «экспорт данных», а полноценная система интроспекции .

Глубокое Debug-информирование

Теперь в процессе конфигурации CMake генерирует не просто команды, а граф зависимостей с метаданными. Вы можете получить JSON-файл, в котором расписано:

  • Почему линкуется конкретная библиотека (трассировка до корневого target_link_libraries).

  • Какие флаги откуда пришли (из INTERFACE, из свойства таргета, из переменной окружения).

  • Граф вызовов функций самого CMake (профилирование производительности скриптов).

Пример использования для отладки:
Вместо того чтобы гадать, почему линковщик подхватил старую версию библиотеки, вы запрашиваете:

cmake --build . --target help-file-api
cat build/.cmake/api/v1/query/client-/target-[your_target]-debug.json

На выходе вы видите полную родословную каждого объекта.

Революция в работе с линкером: Префикс LINKER:

Это, пожалуй, самое важное изменение для тех, кто мучился с флагами линковки на разных платформах. Раньше, чтобы передать флаг линковщику, приходилось использовать обертки компилятора (-Wl, в GCC/Clang) или специфические для MSVC флаги. Это делало код CMake уродливым и полным платформенно-зависимых условий.

CMake 4.0 вводит гениально простой префикс LINKER: .

Было (старый стиль, полный костылей):

# Для GCC/Clang
target_link_options(my_app PRIVATE "-Wl,--wrap,malloc")
# Для MSVC
if(MSVC)
  target_link_options(my_app PRIVATE "/INCLUDE:malloc_wrap")
endif()

Стало (CMake 4.0+):

# CMake сам преобразует этот флаг в синтаксис, понятный текущему тулчейну
target_link_options(my_app PRIVATE "LINKER:--wrap,malloc")

Как это работает под капотом:

  • GCC/Clang: Преобразуется в -Wl,--wrap,malloc.

  • MSVC: Преобразуется в /INCLUDE:malloc_wrap.

  • Apple LD: Преобразуется в -Wl,-wrap,malloc.

Это кардинально упрощает написание кроссплатформенных проектов. Теперь не нужно писать горы условий. Это работает и для передачи целых скриптов линковщику.

Интеграция с экосистемами: Python и Ruby больше не «чужие»

Раньше, если вы писали модуль на C++ для Python (Pybind11) или Ruby, вам приходилось вручную вызывать find_package для PythonLibs, возиться с виртуальными окружениями, правильно линковать интерпретатор.

CMake 4.0 представил Native Ecosystem Modules . Теперь CMake понимает структуру этих языков «из коробки».

Пример сборки модуля для Python 3.12+

cmake_minimum_required(VERSION 4.0)
project(SuperFastCalculator LANGUAGES CXX)

# Встроенный модуль. Не нужно искать пути вручную!
find_package(Python REQUIRED COMPONENTS Interpreter Development.Module)

# Создаем разделяемую библиотеку, которая является Python-модулем
python_add_library(super_calc MODULE src/calc.cpp)

# Линкуем с правильными флагами для Python (автоматически)
target_link_libraries(super_calc PRIVATE Python::Module)

# Устанавливаем прямо в site-packages
install(TARGETS super_calc DESTINATION ${Python_SITEARCH})

Ключевой момент: CMake 4.2.1 расширил поддержку компиляторов для фортрана (LLVMFlang) , что важно для научных гибридных проектов (C++ + Fortran).

Эволюция поддержки компиляторов и платформ (Версии 4.1 и 4.2)

Выход обновлений 4.1 и 4.2 в конце 2025 — начале 2026 года привнес важные коррективы, которые обязан знать каждый .

Visual Studio 2026 (v18) и ARM64

CMake 4.2 полностью адаптирован под VS 2026. Критическое изменение касается Android-разработки в Visual Studio: обновлен параметр ApplicationTypeRevision до версии 3.0. Если вы собираете мобильные приложения под Android через VS, теперь это требует явного указания в тулчейне.

Emscripten (WebAssembly) стал «взрослым»

Раньше CMake принудительно добавлял флаг -fPIC для Emscripten, что было избыточно. В версии 4.2.0 этот хардкод убрали .

  • Фикс: try_run теперь корректно запускает .js файл, а не пытается выполнить .wasm бинарник напрямую. Это исправляет кучу ошибок при конфигурации проектов, собираемых в WebAssembly.

Современный шаблон проекта на C++26 и CMake 4.0

Прошли времена, когда в CMakeLists.txt писали «глобальные» переменные и команды include_directories. Современный подход — это строгая изоляция таргетов.

Давайте разберем идеальный шаблон проекта, использующего C++26, модули (std modules) и пакетный менеджер Conan 2.x, интегрированный с CMake 4.0 .

Структура проекта:

my_modern_lib/
├── CMakeLists.txt (корневой)
├── conanfile.txt
├── src/
│   ├── CMakeLists.txt
│   ├── my_lib_module.cppm (C++20/26 модуль!)
│   └── my_lib.cpp
├── include/
│   └── my_lib/
│       └── public_api.h
└── tests/
    ├── CMakeLists.txt
    └── test_basic.cpp

Шаг 1: Корневой CMakeLists.txt (Тулчейн и Глобальные настройки)

cmake_minimum_required(VERSION 4.0)
# Политики CMake: используем поведение версии 4.0
cmake_policy(VERSION 4.0)

project(ModernProject VERSION 1.0.0 LANGUAGES CXX)

# Устанавливаем стандарт C++26 как ИНТЕРФЕЙСНЫЙ для всех, кто нас подключает
set(CMAKE_CXX_STANDARD 26)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF) # Не используем GNU расширения

# Оптимизация: кэш для компилятора (поддержка встроена в CMake 4)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
    set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
endif()

# Добавляем директории с кодом
add_subdirectory(src)
add_subdirectory(tests)

Шаг 2: Библиотека с поддержкой C++20 Modules (src/CMakeLists.txt)

Это магия CMake 4.0. Работа с .cppm файлами теперь «first-class citizen», а не через экспериментальные флаги.

# Объявляем библиотеку. PUBLIC_HEADER - явно указываем заголовочные файлы
add_library(my_core_library)
target_sources(my_core_library
    PUBLIC
        FILE_SET CXX_MODULES FILES
            my_lib_module.cppm   # Это C++ модуль!
    PRIVATE
        my_lib.cpp
    PUBLIC
        FILE_SET HEADERS
            BASE_DIRS ../include
            FILES ../include/my_lib/public_api.h
)

# Линковка библиотек
target_link_libraries(my_core_library
    PUBLIC
        Boost::boost # Виртуальный импорт из Conan
    PRIVATE
        Threads::Threads
)

# Флаги компиляции: только для этого таргета!
target_compile_options(my_core_library PRIVATE
    $<$<CXX_COMPILER_ID:MSVC>:/permissive- /EHsc>
    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Wextra -Wpedantic>
)

# Указываем, что установить
install(TARGETS my_core_library
    EXPORT MyProjectTargets
    FILE_SET HEADERS DESTINATION include
    FILE_SET CXX_MODULES DESTINATION lib/cmake)

Шаг 3: Использование Conan 2.x (Пакетный менеджер)

CMake 4.0 отлично понимает профили Conan. Файл conanfile.txt:

[requires]
boost/1.86.0
fmt/10.2.1

[generators]
CMakeDeps
CMakeToolchain

[options]
boost/*:shared=True

Сборка проекта одной командой:

conan install . --build=missing -s build_type=Release
cmake --preset conan-release # Используем пресет, сгенерированный Conan
cmake --build --preset conan-release

Интеграция тестирования: Google Test и CTest 4.0

CMake 4.0 расширил возможности CTest. Теперь он умеет напрямую парсить результаты тестов Google Test без лишних оберток. Функция gtest_discover_tests стала еще умнее .

Пример (tests/CMakeLists.txt):

add_executable(tests test_basic.cpp)
target_link_libraries(tests PRIVATE my_core_library GTest::gtest_main)

# Включаем тесты в CTest
include(GoogleTest)
gtest_discover_tests(tests
    PROPERTIES
        ENVIRONMENT "TEST_VAR=42"  # Переменные окружения для тестов
        TIMEOUT 10
)

# Новая фишка CMake 4.0: Пропуск тестов по условию
set_tests_properties(SomeFlakyTest PROPERTIES
    SKIP_RETURN_CODE 77
    # Если тест вернул 77, CTest пометит его как пропущенный, а не упавший
)

Деплой и упаковка: CPack стал умнее

CPack получил улучшенный генератор для AppImage и обновленную поддержку для пакетов . Теперь можно создавать установщики, которые учитывают модули C++20/26.

Важное изменение в экспорте (CPS): Исправлены ошибки экспорта определений (definitions) при использовании современных форматов пакетов, таких как CPS (Common Package Specification) . Это значит, что если вы подключаете библиотеку через find_package, макросы и определения (defs) передаются правильно и не теряются.


Заключение

Подводя итог нашему глубокому погружению в экосистему сборки 2026 года, можно сделать однозначный вывод: CMake 4.0 стал не просто инструментом, а полноценным оркестратором разработки.