CMake подключение внешней библиотеки шаг за шагом

Cmake как подключить библиотеку

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

Cmake как подключить библиотеку

Для современных 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 с базовой структурой

Создание проекта 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

Команда 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 добавляет каталоги с заголовочными файлами для компиляции. Она может использовать абсолютные и относительные пути, системные директории или пути, определённые другими пакетами.

Синтаксис:

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 позволяют указать нестандартные пути для поиска библиотек и заголовочных файлов. Основные переменные:

Переменная Назначение
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, чтобы убедиться, что все компоненты работают корректно.

Ссылка на основную публикацию