Как понять, что проблема связана именно с CMake, а не с приложением, окружением, сетью или зависимостями?
Изолируйте слой: откатите CMakeLists.txt через git bisect, включите --log-level=DEBUG и --verbose при сборке, проверьте compile_commands.json и ldd-вывод для runtime. Если проблема воспроизводится без CMake — виновато окружение или код.
Диагностика: CMake или что-то другое?
Когда сборка или запуск начинают вести себя непредсказуемо после изменений в CMake-конфигурации, нужна чёткая методология, чтобы отделить виновника от наблюдателя. CMake — это генератор систем сборки, он сам по себе не компилирует и не линкует код, поэтому цепочка ответственности длиннее, чем кажется.
Шаг 1: изолируйте слой
Задайте себе несколько уточняющих вопросов в строгом порядке:
- Воспроизводится ли проблема на чистой машине / чистом Docker-образе? Если нет — дело в окружении, а не в CMake.
- Воспроизводится ли проблема без последних изменений CMakeLists.txt? Откатитесь через
git bisectдо последнего рабочего коммита. - Воспроизводится ли при ручной линковке / компиляции без CMake? Если
g++ main.cpp -lfoo -o appтоже падает — CMake ни при чём. - Меняется ли поведение при смене генератора? Пересоберите с
-G Ninjaвместо Makefiles: если поведение отличается, проблема в генераторе или race condition в параллельной сборке.
Шаг 2: включите максимальный вывод CMake
cmake -S . -B build --log-level=DEBUG 2>&1 | tee cmake_debug.log
cmake --build build --verbose 2>&1 | tee build_verbose.log
Флаг --log-level=DEBUG покажет, какие пути разрешаются, какие переменные выставлены и какие файлы включаются. Флаг --verbose при сборке выведет точные команды компилятора и линковщика.
Шаг 3: проверьте сгенерированные артефакты
CMake генерирует Makefile / build.ninja / .sln. Откройте их и проверьте:
- Правильные ли пути к заголовкам (
-I)? Совпадают ли они с тем, что вы ожидаете отtarget_include_directories()? - Правильные ли флаги компилятора (
-std=c++17,-O2)? - Правильные ли зависимости линковщика (
-L,-l)?
# Просмотр compile_commands.json (если включён)
cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cat build/compile_commands.json | python3 -m json.tool | grep "command" | head -20
Шаг 4: проверьте зависимости независимо
Если CMake находит библиотеку через find_package() или pkg_check_modules(), проверьте, что нашёл:
cmake -S . -B build -DCMAKE_FIND_DEBUG_MODE=ON 2>&1 | grep -i "found\|not found\|looking"
Если библиотека найдена некорректно (не та версия, не тот путь) — это проблема окружения или зависимостей, а не CMake-скриптов.
Шаг 5: изолируйте сетевые и runtime-проблемы
- Runtime: используйте
ldd ./app(Linux) илиotool -L ./app(macOS) чтобы убедиться, что динамические библиотеки разрешаются корректно. НеправильныйRPATH, заданный черезCMAKE_INSTALL_RPATH, — типичная CMake-проблема. - Сеть: если падает загрузка зависимостей через
FetchContentилиExternalProject— проблема сетевая, не CMake-логическая.
ldd build/myapp | grep "not found"
Подводные камни
- Кешированный CMakeCache.txt: старые значения переменных остаются в кеше и перекрывают новые. Всегда удаляйте
build/при подозрении на такие проблемы. - Разные генераторы дают разный порядок флагов: поведение Ninja и Make может отличаться при параллельной сборке.
- find_package() vs pkg-config: CMake может найти другую версию библиотеки, не ту, что видит
pkg-config. - CMAKE_BUILD_TYPE по умолчанию пуст: без явного указания Release/Debug флаги оптимизации не применяются, поведение может отличаться от Production.
- RPATH не включён при установке:
make installможет сломать запуск из-за отсутствияCMAKE_INSTALL_RPATH. - Политики CMake (cmake_policy): разные версии CMake интерпретируют одни и те же команды по-разному без явного
cmake_minimum_required(). - Переменные среды vs CMake-переменные:
CC/CXXиз окружения могут перекрыватьCMAKE_C_COMPILER. - ExternalProject строит во время сборки, а не configure: ошибки в нём появляются позже, чем ожидается, что сбивает с толку.
What hurts your answer
- Сразу обвинять CMake, не проверив соседние слои системы
- Чинить симптом без минимального воспроизведения и evidence
- Не учитывать версии, конфигурацию, окружение и recent changes
What they're listening for
- Умеет локализовать проблему вокруг CMake
- Двигается от симптома к гипотезам и проверкам
- Отличает баг инструмента от ошибки использования или окружения