CMakeSeniorExperience

Как понять, что проблема связана именно с 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
  • Двигается от симптома к гипотезам и проверкам
  • Отличает баг инструмента от ошибки использования или окружения

Related topics