CMakeMiddleTechnical

Как находить и использовать внешние библиотеки с помощью find_package()?

find_package() ищет библиотеку в Config- или Module-режиме; современный подход — использовать imported targets (OpenSSL::SSL, fmt::fmt), которые автоматически тянут include-пути и транзитивные зависимости через target_link_libraries().

Механизм find_package()

find_package() — основной способ подключить стороннюю библиотеку в CMake-проекте. Команда работает в двух режимах: Module mode (ищет файл Find<PackageName>.cmake в CMAKE_MODULE_PATH) и Config mode (ищет <PackageName>Config.cmake или <package-name>-config.cmake, которые устанавливает сама библиотека).

Базовый синтаксис

find_package(OpenSSL REQUIRED)
find_package(Boost 1.80 REQUIRED COMPONENTS filesystem system)
find_package(fmt 9.0 QUIET)
  • REQUIRED — прерывает конфигурацию с ошибкой, если пакет не найден.
  • QUIET — подавляет диагностические сообщения об отсутствии пакета.
  • COMPONENTS — выбирает конкретные компоненты (Boost, Qt и др. поддерживают это).
  • Версионный аргумент задаёт минимально допустимую версию.

Что происходит после успешного поиска

CMake устанавливает переменные вида <Package>_FOUND, <Package>_INCLUDE_DIRS, <Package>_LIBRARIES (устаревший стиль) и создаёт imported targets — предпочтительный современный способ:

find_package(OpenSSL REQUIRED)
target_link_libraries(my_app PRIVATE OpenSSL::SSL OpenSSL::Crypto)

find_package(fmt REQUIRED)
target_link_libraries(my_app PRIVATE fmt::fmt)

Imported target автоматически тянет include-пути, compile definitions и транзитивные зависимости — ничего указывать вручную не нужно.

Config mode: как CMake ищет файл конфигурации

Порядок поиска определяется переменными:

  • CMAKE_PREFIX_PATH — список корней, в которых CMake ищет lib/cmake/<Name>, share/cmake/<Name> и т.д.
  • <Package>_DIR — явно задаёт каталог с конфигурационным файлом.
  • Переменная окружения <Package>_ROOT (CMake 3.12+).
  • Системные пути вроде /usr/lib/cmake, /usr/local/lib/cmake.
# Передача при вызове cmake
cmake -B build -DCMAKE_PREFIX_PATH=/opt/mylibs
# Или несколько корней
cmake -B build -DCMAKE_PREFIX_PATH="/opt/lib1;/opt/lib2"

Module mode: написание собственного FindXxx.cmake

Когда библиотека не поставляет Config-файл (старые системные библиотеки), пишут модуль вручную:

# cmake/FindMyLib.cmake
find_path(MyLib_INCLUDE_DIR mylib.h
  HINTS ${MYLIB_ROOT}/include /usr/local/include)

find_library(MyLib_LIBRARY
  NAMES mylib mylib_static
  HINTS ${MYLIB_ROOT}/lib /usr/local/lib)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLib
  REQUIRED_VARS MyLib_LIBRARY MyLib_INCLUDE_DIR)

if(MyLib_FOUND AND NOT TARGET MyLib::MyLib)
  add_library(MyLib::MyLib UNKNOWN IMPORTED)
  set_target_properties(MyLib::MyLib PROPERTIES
    IMPORTED_LOCATION "${MyLib_LIBRARY}"
    INTERFACE_INCLUDE_DIRECTORIES "${MyLib_INCLUDE_DIR}")
endif()

Подключить модуль в проект:

list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
find_package(MyLib REQUIRED)
target_link_libraries(my_app PRIVATE MyLib::MyLib)

Интеграция с пакетными менеджерами

vcpkg и Conan генерируют Config-файлы автоматически. При использовании vcpkg достаточно передать toolchain:

cmake -B build -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake

После этого find_package(fmt REQUIRED) найдёт библиотеку из vcpkg без дополнительных настроек.

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

  • Смешение старого и нового стиля. Использование ${Package_INCLUDE_DIRS} вместо imported targets ломает транзитивные зависимости и приводит к ошибкам линковки в подпроектах.
  • Разные имена пакета и target. find_package(OpenCV), но цель называется opencv_core, не OpenCV::core — нужно читать документацию конкретного пакета.
  • Кешированные переменные. После смены CMAKE_PREFIX_PATH старые значения <Package>_DIR остаются в CMakeCache.txt. Решение: удалить кеш или передать -U <Package>_DIR.
  • Конфликт Module vs Config mode. Если в проекте есть собственный FindOpenSSL.cmake, а система предоставляет OpenSSLConfig.cmake, CMake выберет Module mode и может найти не ту версию.
  • QUIET скрывает реальные ошибки. Пакет найден, но с неверными путями — диагностика отключена QUIET, приложение падает при запуске.
  • COMPONENTS не обязательно проверяются. Не все пакеты валидируют COMPONENTS; ошибка компиляции возникает позже — труднее диагностировать.
  • Разрядность и конфигурация. На Windows Debug/Release библиотеки лежат в разных каталогах; без правильного CMAKE_BUILD_TYPE и multi-config генераторов линкуется не та версия.
  • Виртуальные окружения Python/Conda. При сборке проектов, использующих find_package(Python3), CMake может найти системный Python вместо окружения, если Python3_ROOT_DIR не задан явно.

Common mistakes

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

What the interviewer is testing

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

Sources

Related topics