Содержание статьи

Для современных C++ проектов подключение внешней библиотеки через CMake требует точного указания путей к заголовочным файлам и бинарным библиотекам. Неправильная настройка переменных CMAKE_PREFIX_PATH или CMAKE_MODULE_PATH может привести к ошибкам компиляции и линковки.
Встроенные команды CMake, такие как find_package, include_directories и target_link_libraries, позволяют управлять подключением библиотек на уровне таргета. Практический подход включает проверку существования файлов .h и .lib/.a перед сборкой, чтобы исключить пропущенные зависимости.
Особое внимание стоит уделять совместимости версий библиотек. Для CMake 3.20 и выше рекомендуется использовать именованные аргументы в target_link_libraries и указывать PUBLIC, PRIVATE или INTERFACE, чтобы управление зависимостями оставалось прозрачным при расширении проекта.
В этой статье подробно рассмотрены шаги от настройки проекта до проверки корректного подключения библиотеки. Все примеры ориентированы на конкретные пути, команды и конфигурации, которые можно сразу использовать в реальном проекте.
Создание проекта CMake с базовой структурой

Для начала необходимо создать каталог проекта с минимальной структурой, включающей исходники и CMakeLists.txt:
- root/
- src/ – исходные файлы (.cpp, .h)
- include/ – заголовочные файлы внешнего интерфейса
- CMakeLists.txt – основной файл сборки
В CMakeLists.txt указываются минимальная версия CMake и имя проекта:
cmake_minimum_required(VERSION 3.20)
project(MyProject LANGUAGES CXX)
Для компиляции исполняемого файла добавляется таргет с исходниками:
add_executable(MyProject src/main.cpp)
Если планируется использование внешней библиотеки, рекомендуется сразу определить папку include и добавить её к таргету через target_include_directories:
target_include_directories(MyProject PRIVATE include)
Для проектов с несколькими исходными файлами удобно использовать file(GLOB …) или явно перечислять файлы, чтобы новые файлы автоматически включались в сборку:
file(GLOB SOURCES "src/*.cpp")
add_executable(MyProject ${SOURCES})
Структура проекта, включающая src и include, обеспечивает четкое разделение логики и интерфейса, упрощает подключение внешних библиотек и поддержку сборки в разных конфигурациях.
Поиск библиотеки с помощью find_package

Команда find_package используется для обнаружения установленных библиотек и их конфигураций. Она проверяет системные пути и переменные CMake, чтобы определить наличие требуемой библиотеки и подготовить соответствующие переменные для сборки.
Синтаксис базовой формы:
find_package(<ИмяБиблиотеки> [Версия] [REQUIRED|QUIET] [COMPONENTS <Компоненты>])
Параметры:
| Параметр | Описание |
|---|---|
| ИмяБиблиотеки | Название библиотеки, например, Boost или Eigen3. |
| Версия | Минимальная требуемая версия библиотеки, например, 3.10. |
| REQUIRED | Если библиотека не найдена, сборка завершится ошибкой. |
| QUIET | Подавляет предупреждения при отсутствии библиотеки. |
| COMPONENTS | Перечисление обязательных модулей библиотеки, например, filesystem system для Boost. |
После вызова find_package доступны переменные:
| Переменная | Описание |
|---|---|
| <ИмяБиблиотеки>_FOUND | Булевое значение, найден ли пакет. |
| <ИмяБиблиотеки>_INCLUDE_DIRS | Список путей для подключения заголовочных файлов. |
| <ИмяБиблиотеки>_LIBRARIES | Список файлов библиотек для линковки. |
Пример подключения Boost:
find_package(Boost 1.82 REQUIRED COMPONENTS filesystem system)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(MyTarget ${Boost_LIBRARIES})
Для современных CMake рекомендуется использовать импортируемые цели, если пакет их предоставляет:
find_package(Eigen3 3.4 REQUIRED)
target_link_libraries(MyTarget Eigen3::Eigen)
Это автоматически управляет include-путями и линковкой, упрощая поддержку различных платформ.
Подключение заголовочных файлов через include_directories

Команда include_directories добавляет каталоги с заголовочными файлами для компиляции. Она может использовать абсолютные и относительные пути, системные директории или пути, определённые другими пакетами.
Синтаксис:
include_directories([AFTER|BEFORE] [SYSTEM] путь1 путь2 ...)
| Опция | Описание |
|---|---|
| AFTER | Добавляет путь в конец списка, приоритет имеют ранее добавленные директории. |
| BEFORE | Добавляет путь в начало списка, приоритет выше, чем у остальных. |
| SYSTEM | Помечает директорию как системную, предупреждения компилятора игнорируются для файлов из неё. |
Пример подключения локальной библиотеки:
include_directories(${CMAKE_SOURCE_DIR}/external/MyLibrary/include)
Если библиотека найдена через find_package, подключение выполняется с использованием её переменных:
include_directories(${MyLibrary_INCLUDE_DIRS})
Для изоляции зависимостей рекомендуется использовать свойства цели:
target_include_directories(MyTarget PRIVATE ${MyLibrary_INCLUDE_DIRS})
PRIVATE гарантирует, что включаемые пути доступны только для указанной цели, не влияя на другие цели проекта.
Добавление библиотеки к таргету с target_link_libraries

Команда target_link_libraries связывает библиотеку с указанной целью компиляции, обеспечивая линковку и корректное разрешение зависимостей.
Синтаксис:
target_link_libraries(<Цель> [PRIVATE|PUBLIC|INTERFACE] библиотека1 библиотека2 ...)
| Ключ | Описание |
|---|---|
| PRIVATE | Библиотека доступна только для указанной цели. |
| PUBLIC | Библиотека доступна для цели и для всех целей, которые ссылаются на неё. |
| INTERFACE | Библиотека не используется для этой цели, но её свойства доступны зависимым целям. |
Пример добавления библиотеки Boost:
find_package(Boost 1.82 REQUIRED COMPONENTS filesystem system)
target_link_libraries(MyApp PRIVATE ${Boost_LIBRARIES})
Для импортируемых целей современных библиотек можно указывать цель напрямую:
find_package(Eigen3 3.4 REQUIRED)
target_link_libraries(MyApp PRIVATE Eigen3::Eigen)
Рекомендации:
- Использовать импортируемые цели вместо переменных с путями, когда библиотека их предоставляет.
- Применять PRIVATE для внутренних зависимостей и PUBLIC для библиотек, которые должны быть доступны зависимым целям.
- Избегать прямого указания путей к .lib или .a файлам, если есть доступные цели CMake.
Настройка переменных CMake для пользовательских путей

Переменные CMake позволяют указать нестандартные пути для поиска библиотек и заголовочных файлов. Основные переменные:
| Переменная | Назначение |
|---|---|
| CMAKE_PREFIX_PATH | Список директорий, где CMake ищет пакеты и конфигурационные файлы библиотек. |
| CMAKE_INCLUDE_PATH | Дополнительные пути для поиска заголовочных файлов. |
| CMAKE_LIBRARY_PATH | Дополнительные пути для поиска файлов библиотек (.lib, .a, .so, .dylib). |
| CMAKE_FRAMEWORK_PATH | Пути для поиска библиотек в формате macOS Framework. |
Пример добавления пользовательской библиотеки:
set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/external/MyLibrary" ${CMAKE_PREFIX_PATH})
find_package(MyLibrary REQUIRED)
Для заголовочных файлов и библиотек напрямую:
set(CMAKE_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/external/MyLibrary/include" ${CMAKE_INCLUDE_PATH})
set(CMAKE_LIBRARY_PATH "${CMAKE_SOURCE_DIR}/external/MyLibrary/lib" ${CMAKE_LIBRARY_PATH})
Рекомендации:
- Использовать абсолютные пути, чтобы избежать конфликтов с системными библиотеками.
- Для многоплатформенных проектов проверять наличие библиотек через
find_packageпосле установки путей. - Добавлять пользовательские пути до вызова
find_package, чтобы гарантировать корректный поиск.
Проверка корректности подключения и сборки проекта

После подключения внешних библиотек необходимо убедиться в их корректной интеграции и сборке проекта. Первым шагом выполняют конфигурацию CMake:
cmake -S . -B build
Команда проверяет доступность библиотек и заголовочных файлов, используя заданные пути и переменные.
Для проверки найденных библиотек используют переменные, определённые find_package:
if(MyLibrary_FOUND)
message(STATUS "MyLibrary успешно найден")
else()
message(FATAL_ERROR "MyLibrary не найден")
endif()
Сборка проекта выполняется в отдельной директории:
cmake --build build --config Release
Рекомендуется проверять наличие ошибок компиляции и линковки, особенно для импортируемых целей и пользовательских include-путей.
Дополнительно проверяют работу исполняемых файлов и тестов, если они настроены через CTest:
ctest --test-dir build
Рекомендации:
- Использовать отдельную директорию сборки, чтобы избежать конфликтов с исходными файлами.
- Проверять значения переменных _INCLUDE_DIRS и _LIBRARIES, чтобы убедиться в корректной ссылке на нужные версии библиотек.
- Для больших проектов подключать
message(STATUS)для отладки поиска и линковки библиотек.
Вопрос-ответ:
Как правильно использовать find_package для Boost с конкретными компонентами?
Для поиска Boost с конкретными компонентами необходимо указать их через ключ COMPONENTS и задать минимальную версию библиотеки. Пример: find_package(Boost 1.82 REQUIRED COMPONENTS filesystem system). После этого переменные Boost_INCLUDE_DIRS и Boost_LIBRARIES будут содержать пути к заголовкам и файлам библиотек, которые можно подключить через include_directories и target_link_libraries.
В чем разница между PRIVATE, PUBLIC и INTERFACE в target_link_libraries?
Ключ PRIVATE делает библиотеку доступной только для текущей цели. PUBLIC добавляет библиотеку к цели и к тем, которые ссылаются на эту цель. INTERFACE не подключает библиотеку к текущей цели, но её свойства становятся доступными зависимым целям. Для большинства внутренних зависимостей используют PRIVATE, а для библиотек, которые должны быть видны зависимым модулям, — PUBLIC.
Как задать пользовательские пути для CMake, если библиотека установлена в нестандартную директорию?
Необходимо использовать переменные CMAKE_PREFIX_PATH, CMAKE_INCLUDE_PATH и CMAKE_LIBRARY_PATH. Например, если библиотека находится в external/MyLibrary, добавляют:set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/external/MyLibrary" ${CMAKE_PREFIX_PATH})set(CMAKE_INCLUDE_PATH "${CMAKE_SOURCE_DIR}/external/MyLibrary/include" ${CMAKE_INCLUDE_PATH})set(CMAKE_LIBRARY_PATH "${CMAKE_SOURCE_DIR}/external/MyLibrary/lib" ${CMAKE_LIBRARY_PATH}). После этого find_package сможет корректно обнаружить библиотеку.
Можно ли использовать include_directories глобально для всего проекта?
Да, include_directories можно вызывать до создания целей, чтобы пути были доступны всем таргетам. Но современная практика рекомендует использовать target_include_directories с ключами PRIVATE, PUBLIC или INTERFACE, чтобы ограничить область видимости и избежать конфликтов между зависимостями разных целей.
Как проверить, что подключенная библиотека работает и линковка прошла успешно?
После вызова find_package проверяют переменную <Library>_FOUND. Затем проект собирают в отдельной директории: cmake -S . -B build и cmake --build build. При успешной сборке проверяют доступ к заголовкам и функции библиотеки через простой тестовый файл. Для проектов с тестами можно использовать ctest --test-dir build, чтобы убедиться, что все компоненты работают корректно.
