CMakeJuniorTechnical

В чём разница между in-source build и out-of-source build? Какой предпочтителен и почему?

In-source build создаёт все артефакты рядом с исходниками (cmake .), что засоряет репозиторий. Out-of-source (cmake -S . -B build) изолирует генерируемые файлы в отдельную директорию, упрощает .gitignore и позволяет держать несколько конфигураций параллельно.

In-source и out-of-source сборка

In-source build

In-source сборка происходит, когда build-директория совпадает с директорией исходного кода. Пользователь запускает cmake . прямо в корне проекта.

# In-source: НЕ рекомендуется
cd /my/project
cmake .        # CMakeCache.txt, CMakeFiles/, Makefile появятся рядом с исходниками
make

После этого директория проекта выглядит так:

/my/project/
  CMakeLists.txt
  CMakeCache.txt      # сгенерировано
  CMakeFiles/         # сгенерировано
  Makefile            # сгенерировано
  src/
  include/

Out-of-source build

Out-of-source сборка помещает все генерируемые файлы в отдельную директорию, не затрагивая исходники.

# Out-of-source: правильный подход
cmake -S . -B build
cmake --build build

# Или классический вариант
mkdir build && cd build && cmake .. && make

Структура директорий:

/my/project/
  CMakeLists.txt
  src/
  include/
  build/              # только генерируемые файлы
    CMakeCache.txt
    CMakeFiles/
    Makefile
    myapp

Почему out-of-source предпочтителен

  • Чистый git status: все генерируемые файлы находятся в build/, который легко добавить в .gitignore одной строкой. При in-source придётся перечислять каждый генерируемый файл.
  • Несколько конфигураций одновременно: можно держать Debug, Release и Sanitizers в разных build-директориях параллельно.
  • Простая очистка: rm -rf build/ удаляет всё сгенерированное, не затрагивая исходники. В in-source нужно вручную удалять отдельные файлы.
  • Безопасность: нет риска случайно перезаписать исходный файл генерируемым артефактом с тем же именем.
# Несколько конфигураций одновременно
cmake -S . -B build/debug   -DCMAKE_BUILD_TYPE=Debug
cmake -S . -B build/release -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build/asan    -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_FLAGS="-fsanitize=address"

# Сборка каждой независимо
cmake --build build/debug
cmake --build build/release

Защита от in-source сборок

Можно явно запретить in-source сборку в CMakeLists.txt:

cmake_minimum_required(VERSION 3.21)
project(MyProject)

# Запрет in-source сборки
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
  message(FATAL_ERROR
    "In-source builds are not allowed. "
    "Please create a separate build directory: cmake -S . -B build"
  )
endif()

.gitignore для out-of-source

# .gitignore
build/
# Если используете разные директории
build-*/

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

  • Кеш остаётся после in-source: если проект уже был скомпилирован in-source, CMakeCache.txt остаётся и мешает при переходе на out-of-source. Нужно вручную удалить все CMake-артефакты из корня.
  • IDE может создавать in-source: некоторые IDE (старые версии CLion, Eclipse CDT) по умолчанию конфигурируют in-source сборку.
  • CMAKE_SOURCE_DIR vs CMAKE_CURRENT_SOURCE_DIR: в multi-directory проектах эти переменные отличаются — использование неправильной переменной даёт некорректный путь.
  • Relative paths в custom commands: команды с относительными путями могут ломаться при out-of-source, если не использовать CMAKE_CURRENT_SOURCE_DIR.
  • FetchContent всегда out-of-source: зависимости FetchContent помещаются в build/, независимо от типа сборки основного проекта.

Common mistakes

  • Объяснять in-source и out-of-source build только по синтаксису, без жизненного цикла и стоимости.
  • Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
  • Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
  • Трактовать CMake как shell-скрипт вместо описания графа таргетов.

What the interviewer is testing

  • Кандидат формулирует точную модель для in-source и out-of-source build, а не только определение.
  • Пример компилируем, безопасен по lifetime и соответствует версии технологии.
  • Названы trade-off, ограничения и диагностируемые симптомы ошибки.
  • Разделяет configure/generate/build и использует target-based подход.

Sources

Related topics