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 подход.