[build::eng]
раздел 03 · практика

СОБИРАЕМ БЫСТРО И ВОСПРОИЗВОДИМО

Шесть шагов от пустой папки до проекта, который собирается одной командой, кэшируется и масштабируется на все ядра.

01

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

Исходники отделены от заголовков, сборка живёт вне дерева исходников (out-of-source).

дерево проекта
engine-lab/
├── CMakeLists.txt
├── CMakePresets.json
├── src/
│   ├── main.cpp
│   ├── parser.cpp
│   └── engine.cpp
├── include/engine/
│   ├── parser.h
│   └── engine.h
├── tests/
│   └── test_parser.cpp
└── build/          # создаётся автоматически
02

Базовый CMakeLists

Современный CMake — это цели и их свойства, а не глобальные переменные.

CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(engine_lab VERSION 0.1.0 LANGUAGES CXX)

add_library(core
    src/parser.cpp
    src/engine.cpp)
target_include_directories(core PUBLIC include)

add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE core)

enable_testing()
add_subdirectory(tests)
cmake -B build && cmake --build build -j
03

Пресеты: конфигурации как код

Именованные конфигурации в файле. Все разработчики и CI собирают идентично.

CMakePresets.json
{
  "version": 6,
  "configurePresets": [
    { "name": "dev",
      "binaryDir": "${sourceDir}/build/dev",
      "generator": "Ninja",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_CXX_COMPILER_LAUNCHER": "ccache" } },
    { "name": "release",
      "inherits": "dev",
      "binaryDir": "${sourceDir}/build/release",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Release",
        "CMAKE_INTERPROCEDURAL_OPTIMIZATION": "ON" } }
  ]
}
cmake --preset dev && cmake --build --preset dev
04

Кэширование компиляции

ccache хранит результаты компиляции по хэшу входов. Повторная сборка попадает в кэш.

терминал
# установка и подключение
sudo apt install ccache
cmake -B build -DCMAKE_CXX_COMPILER_LAUNCHER=ccache

# статистика
ccache -s
#   cache hit rate:  92.4 %

Совет: в CI прогревайте кэш артефактом между запусками.

05

Ускоряем: PCH, unity, параллелизм

Три рычага: предкомпилированные заголовки, слияние единиц трансляции и параллельная сборка.

CMakeLists.txt — ускорение
target_precompile_headers(core PRIVATE
    <vector> <string> <memory>)

set_target_properties(core PROPERTIES
    UNITY_BUILD ON
    UNITY_BUILD_BATCH_SIZE 8)
ninja -C build -j$(nproc)
06

Флаги, санитайзеры, LTO

Debug ловит ошибки санитайзерами, Release выжимает скорость через LTO.

CMakeLists.txt — генераторные выражения
target_compile_options(core PRIVATE
    $<$<CONFIG:Release>:-O2 -march=native>
    $<$<CONFIG:Debug>:-O0 -g -fsanitize=address,undefined>)

set_target_properties(app PROPERTIES
    INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
бонус

Калькулятор параллельной сборки

Модель учитывает конфигурацию и линковку — разгон всегда меньше числа ядер.

Последовательно, -j16 мин 0 с
Параллельно, -j849 с
×7.3
разгон · эффективность: 92%