Руководство по использованию зависимостей
- Введение
-
Использование предварительно собранных пакетов с
find_package() - Загрузка и сборка из исходного кода с
FetchContent FetchContentиfind_package()Интеграция- Поставщики зависимостей
Введение
Проекты часто зависят от других проектов, ресурсов и артефактов. CMake предоставляет множество способов включения таких элементов в процесс сборки. Проекты и пользователи могут гибко выбирать методы, которые лучше всего подходят для их потребностей.
Основными методами добавления зависимостей в процесс сборки являются команда find_package() и модуль FetchContent. Модуль FindPkgConfig также иногда используется, хотя он уступает по интеграции двум другим и более подробно не рассматривается в данном руководстве.
Зависимости также могут быть доступны с помощью пользовательского поставщика зависимостей. Это может быть сторонний менеджер пакетов или пользовательский код, реализованный разработчиком. Поставщики зависимостей взаимодействуют с основными методами, упомянутыми выше, чтобы расширить их гибкость.
Использование предварительно собранных пакетов с find_package()
Пакет, необходимый для проекта, может быть уже собран и доступен в определенной системе пользователя. Этот пакет мог быть собран с помощью CMake, или для него могла быть использована другая система сборки. Или это просто набор файлов, которые вообще не нужно было собирать. CMake предоставляет команду find_package() для таких случаев. Она ищет известные места, а также дополнительные подсказки и пути, предоставленные проектом или пользователем. Она также поддерживает компоненты пакетов и необязательные пакеты. Предоставляются переменные результатов, чтобы проект мог настроить собственное поведение в зависимости от того, был ли найден пакет или определенные компоненты.
В большинстве случаев проекты должны использовать Базовый синтаксис. В большинстве случаев это будет включать просто имя пакета, возможно, ограничение по версии и ключевое слово REQUIRED, если зависимость не является необязательной. Также может быть указан набор компонентов пакета.
find_package() базового синтаксисаfind_package(Catch2) find_package(GTest REQUIRED) find_package(Boost 1.79 COMPONENTS date_time)
Команда find_package() поддерживает два основных метода поиска:
- Режим конфигурации
-
С этим методом команда ищет файлы, обычно предоставляемые самим пакетом. Это более надежный метод из двух, так как детали пакета всегда должны быть синхронизированы с самим пакетом.
- Режим модуля
-
Не все пакеты являются CMake-совместимыми. Многие не предоставляют файлы, необходимые для поддержки режима конфигурации. В таких случаях можно предоставить отдельный файл модуля поиска, либо самим проектом, либо CMake. Модуль поиска — это обычно эвристическое решение, которое знает, что обычно предоставляет пакет и как представить этот пакет проекту. Поскольку модули поиска обычно распространяются отдельно от пакета, они не так надежны. Они обычно поддерживаются отдельно, и у них, вероятно, разные графики выпуска, поэтому они легко могут устареть.
В зависимости от используемых аргументов, find_package() может использовать один или оба из вышеуказанных методов. Ограничив опции только базовым синтаксисом, можно использовать как режим конфигурации, так и режим модуля для удовлетворения зависимости. Наличие других опций может ограничить вызов только одним из двух методов, потенциально снижая способность команды найти зависимость. См. документацию find_package() для получения полной информации об этой сложной теме.
Для обоих методов поиска пользователь также может установить переменные кэша в командной строке cmake(1) или в графических инструментах ccmake(1) или cmake-gui(1), чтобы повлиять и переопределить, где искать пакеты. См. руководство по взаимодействию с пользователем для получения дополнительной информации о том, как задавать переменные кэша.
Пакеты с конфигурационными файлами
Предпочтительный способ для стороннего разработчика предоставить исполняемые файлы, библиотеки, заголовки и другие файлы для использования с CMake — предоставить конфигурационные файлы. Это текстовые файлы, поставляемые вместе с пакетом, которые определяют CMake-цели, переменные, команды и т. д. Конфигурационный файл — это обычный скрипт CMake, который считывается командой find_package().
Конфигурационные файлы обычно можно найти в каталоге, имя которого соответствует шаблону lib/cmake/<PackageName>, хотя они могут находиться и в других местах (см. Процедура поиска в режиме конфигурации). <PackageName> обычно является первым аргументом команды find_package(), и он может даже быть единственным аргументом. Альтернативные имена также можно указать с помощью опции NAMES:
find_package(SomeThing
NAMES
SameThingOtherName # Another name for the package
SomeThing # Also still look for its canonical name
)
Конфигурационный файл должен называться либо <PackageName>Config.cmake или <LowercasePackageName>-config.cmake (первый используется в оставшейся части этого руководства, но оба поддерживаются). Этот файл является точкой входа в пакет для CMake. Отдельный необязательный файл с именем <PackageName>ConfigVersion.cmake или <LowercasePackageName>-config-version.cmake также может существовать в той же директории. Этот файл используется CMake для определения того, удовлетворяет ли версия пакета каким-либо ограничениям версии, включенным в вызов find_package(). Версию можно не указывать при вызове find_package(), даже если файл <PackageName>ConfigVersion.cmake присутствует.
Если файл <PackageName>Config.cmake найден и какое-либо ограничение версии удовлетворено, команда find_package() считает пакет найденным, и весь пакет предполагается полным как задумывалось.
Возможно, будут дополнительные файлы, предоставляющие CMake-команды или импортированные целевые элементы для использования. CMake не навязывает никаких правил именования для этих файлов. Они связаны с основным файлом <PackageName>Config.cmake с помощью команды CMake include(). Файл <PackageName>Config.cmake обычно включает их, поэтому обычно они не требуют дополнительных шагов, помимо вызова find_package().
Если местоположение пакета находится в каталоге, известном CMake, вызов find_package() должен быть успешным. Каталоги, известные CMake, зависят от платформы. Например, пакеты, установленные на Linux с помощью стандартного менеджера пакетов системы, будут находиться в префиксе /usr автоматически. Пакеты, установленные в Program Files на Windows, аналогичным образом будут находиться автоматически.
Пакеты не будут найдены автоматически без помощи, если они находятся в местах, неизвестных CMake, таких как /opt/mylib или $HOME/dev/prefix. Это обычная ситуация, и CMake предоставляет несколько способов для пользователей указать, где искать такие библиотеки.
Переменная CMAKE_PREFIX_PATH может быть установлена при вызове CMake. Она рассматривается как список базовых путей, в которых следует искать файлы конфигурации. Установленный пакет в /opt/somepackage обычно устанавливает файлы конфигурации, такие как /opt/somepackage/lib/cmake/somePackage/SomePackageConfig.cmake. В этом случае /opt/somepackage следует добавить в CMAKE_PREFIX_PATH.
Переменная среды CMAKE_PREFIX_PATH также может быть заполнена префиксами для поиска пакетов. Как и переменная среды PATH, это список, но ему необходимо использовать разделитель элементов списка для конкретной платформы (: в Unix и ; в Windows).
Переменная CMAKE_PREFIX_PATH обеспечивает удобство в случаях, когда необходимо указать несколько префиксов или когда несколько пакетов доступны под одним префиксом. Пути к пакетам также можно указать, установив переменные, соответствующие <PackageName>_DIR, такие как SomePackage_DIR. Обратите внимание, что это не префикс, а полный путь к каталогу, содержащему файл конфигурации пакета, например, /opt/somepackage/lib/cmake/SomePackage в приведённом примере. См. документацию по find_package() для других переменных CMake и переменных среды, которые могут повлиять на поиск.
Файлы модулей поиска
Пакеты, которые не предоставляют файлы конфигурации, по-прежнему могут быть найдены с помощью команды find_package(), если доступен файл FindSomePackage.cmake. Эти файлы модулей поиска отличаются от файлов конфигурации тем, что:
- Файлы модулей поиска не должны предоставляться самим пакетом.
- Доступность файла
Find<PackageName>.cmakeне свидетельствует о доступности пакета или любой его части. - CMake не ищет места, указанные в переменной
CMAKE_PREFIX_PATH, для файловFind<PackageName>.cmake. Вместо этого CMake ищет такие файлы в местах, указанных в переменнойCMAKE_MODULE_PATH. Пользователи обычно устанавливают переменнуюCMAKE_MODULE_PATHпри запуске CMake, и проекты CMake обычно добавляют вCMAKE_MODULE_PATHдля использования локальных файлов модулей поиска. - CMake поставляет файлы
Find<PackageName>.cmakeдля некоторыхthird party packages. Эти файлы являются проблемой с точки зрения обслуживания для CMake, и нередко они отстают от последних версий пакетов, с которыми они связаны. В целом, новые модули поиска больше не добавляются в CMake. Проекты должны поощрять поставщиков пакетов предоставлять файлы конфигурации, где это возможно. Если это не удалось, проект должен предоставить собственный модуль поиска для пакета.
См. Модули поиска для подробного обсуждения того, как написать файл модуля поиска.
Импортированные цели
Как файлы конфигурации, так и файлы модулей поиска могут определять импортированные цели. Обычно они имеют имена вида SomePrefix::ThingName. Если они доступны, проект должен предпочесть использовать их вместо любых переменных CMake, которые также могут быть предоставлены. Такие цели обычно содержат требования к использованию и автоматически применяют такие вещи, как пути поиска заголовков, определения компилятора и т. д. к другим целям, которые с ними связаны (например, с использованием target_link_libraries()). Это и более надёжно, и более удобно, чем пытаться применить то же самое вручную с использованием переменных. Проверьте документацию по пакету или модулю поиска, чтобы узнать, какие импортированные цели он определяет, если таковые имеются.
Импортированные цели также должны обобщать любые пути, специфичные для конфигурации. Это включает расположение двоичных файлов (библиотек, исполняемых файлов), флаги компилятора и любые другие конфигурационные данные. Модули поиска могут быть менее надёжными в предоставлении этих деталей, чем файлы конфигурации.
Полный пример, который находит сторонний пакет и использует библиотеку из него, может выглядеть следующим образом:
cmake_minimum_required(VERSION 3.10)
project(MyExeProject VERSION 1.0.0)
# Make project-provided Find modules available
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
find_package(SomePackage REQUIRED)
add_executable(MyExe main.cpp)
target_link_libraries(MyExe PRIVATE SomePrefix::LibName)
Обратите внимание, что вышеприведенный вызов find_package() может быть разрешен файлом конфигурации или модулем поиска. Он использует только основные аргументы, поддерживаемые основной подписью. Файл FindSomePackage.cmake в каталоге ${CMAKE_CURRENT_SOURCE_DIR}/cmake позволит команде find_package() успешно завершить работу в режиме модуля, например. Если такой файл модуля отсутствует, система будет искать файл конфигурации.
Загрузка и сборка из исходного кода с FetchContent
Зависимости не обязательно должны быть предварительно скомпилированы, чтобы их можно было использовать с CMake. Их можно собрать из исходного кода как часть основного проекта. Модуль FetchContent предоставляет функциональность для загрузки содержимого (обычно исходных кодов, но может быть что угодно) и добавления его в основной проект, если зависимость также использует CMake. Исходные коды зависимости будут собраны вместе со всем остальным проектом, так как будто они являются частью собственных исходных кодов проекта.
Общий шаблон заключается в том, что проект сначала должен объявить все зависимости, которые он хочет использовать, а затем попросить сделать их доступными. Следующее демонстрирует принцип (см. Примеры для получения более подробной информации):
include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0 ) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG 605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1 ) FetchContent_MakeAvailable(googletest Catch2)
Поддерживаются различные методы загрузки, включая загрузку и извлечение архивов из URL-адреса (поддерживается ряд форматов архивов), а также ряд форматов репозиториев, включая Git, Subversion и Mercurial. Также можно использовать пользовательские команды загрузки, обновления и применения исправлений для поддержки произвольных случаев использования.
Когда зависимость добавляется в проект с помощью FetchContent, проект связывается с целями зависимости так же, как и с любой другой целью из проекта. Если зависимость предоставляет целевые объекты с пространством имён вида SomePrefix::ThingName, проект должен связываться с ними, а не с любыми целевыми объектами без пространства имён. См. следующий раздел, чтобы понять, почему это рекомендуется.
Не все зависимости можно добавить в проект таким образом. Некоторые зависимости определяют цели, имена которых конфликтуют с другими целями из проекта или других зависимостей. Конкретные исполняемые и библиотечные цели, созданные с помощью add_executable() и add_library(), являются глобальными, поэтому каждая должна быть уникальной во всём построении. Если зависимость добавит конфликтующее имя цели, её нельзя напрямую добавить в построение этим методом.
FetchContent и find_package() интеграция
Добавлен в версии 3.24.
Некоторые зависимости поддерживают добавление с помощью либо find_package(), либо FetchContent. Такие зависимости должны гарантировать, что они определяют те же целевые объекты с пространством имён как в установленных, так и в собираемых из исходного кода сценариях. Затем потребляющий проект связывается с этими целевыми объектами с пространством имён и может прозрачно обрабатывать оба сценария, пока проект не использует ничего другого, что не предоставляется обоими методами.
Проект может указать, что он рад принять зависимость любым из методов, используя параметр FIND_PACKAGE_ARGS для FetchContent_Declare(). Это позволяет FetchContent_MakeAvailable() сначала попробовать удовлетворить зависимость с помощью вызова find_package() с аргументами после ключевого слова FIND_PACKAGE_ARGS, если таковые имеются. Если это не найдёт зависимость, она будет собрана из исходного кода, как описано ранее.
include(FetchContent) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0 FIND_PACKAGE_ARGS NAMES GTest ) FetchContent_MakeAvailable(googletest) add_executable(ThingUnitTest thing_ut.cpp) target_link_libraries(ThingUnitTest GTest::gtest_main)
В приведенном выше примере сначала вызывается find_package(googletest NAMES GTest). CMake предоставляет модуль FindGTest, поэтому, если он найдёт пакет GTest, установленный где-то, он сделает его доступным, и зависимость не будет построена из исходного кода. Если пакет GTest не найден, он будет построен из исходного кода. В любом случае, ожидается, что целевой объект GTest::gtest_main будет определён, поэтому мы связываем исполняемый файл нашего модульного теста с этой целью.
Высокоуровневый контроль также доступен через переменную FETCHCONTENT_TRY_FIND_PACKAGE_MODE. Она может быть установлена в значение NEVER, чтобы отключить всё перенаправление на find_package(). Она может быть установлена в значение ALWAYS, чтобы попробовать find_package() даже если FIND_PACKAGE_ARGS не был указан (это следует использовать с осторожностью).
Проект также может принять решение, что определённая зависимость должна быть построена из исходного кода. Это может потребоваться, если требуется исправленная или невыпущенная версия зависимости, или для удовлетворения какой-либо политики, требующей, чтобы все зависимости строились из исходного кода. Проект может принудительно выполнить это, добавив ключевое слово OVERRIDE_FIND_PACKAGE к FetchContent_Declare(). Вызов find_package() для этой зависимости затем будет перенаправлен на FetchContent_MakeAvailable() вместо этого.
include(FetchContent) FetchContent_Declare( Catch2 URL https://intranet.mycomp.com/vendored/Catch2_2.13.4_patched.tgz URL_HASH MD5=abc123... OVERRIDE_FIND_PACKAGE ) # The following is automatically redirected to FetchContent_MakeAvailable(Catch2) find_package(Catch2)
Для более сложных случаев использования см. переменную CMAKE_FIND_PACKAGE_REDIRECTS_DIR.
Обзор поставщиков зависимостей
Добавлена в версии 3.24.
В предыдущем разделе обсуждались методы, которые проекты могут использовать для указания своих зависимостей. В идеале, проект не должен заботиться о том, откуда происходит зависимость, если только она предоставляет то, что он ожидает (часто это просто некоторые импортированные целевые объекты). Проект указывает, что ему нужно, и может также указать, откуда его получить, в отсутствии других данных, так что он всё ещё может быть собран «из коробки».
С другой стороны, разработчик может быть гораздо больше заинтересован в управлении тем, как зависимость предоставляется проекту. Возможно, вы захотите использовать определённую версию пакета, который вы сами создали. Возможно, вы захотите использовать сторонний менеджер пакетов. Возможно, вы захотите перенаправить некоторые запросы на другой URL на управляемом вами сервере по причинам безопасности или производительности. CMake поддерживает такие сценарии с помощью Поставщиков зависимостей.
Поставщик зависимостей может быть настроен на перехват вызовов find_package() и FetchContent_MakeAvailable(). Поставщику предоставляется возможность удовлетворить такие запросы, прежде чем вернуться к встроенной реализации, если поставщик не выполняет её.
Может быть установлен только один поставщик зависимостей, и он может быть установлен только в очень определённый момент в начале выполнения CMake. Переменная CMAKE_PROJECT_TOP_LEVEL_INCLUDES перечисляет файлы CMake, которые будут считаны во время обработки первого вызова project() (и только этого вызова). Это единственное время, когда может быть установлен поставщик зависимостей. В целом проекте ожидается, что будет использоваться не более одного поставщика.
В некоторых сценариях пользователю не нужно знать подробности о том, как настроен поставщик зависимостей. Стороннее лицо может предоставить файл, который можно добавить в CMAKE_PROJECT_TOP_LEVEL_INCLUDES, что настроит поставщика зависимостей от имени пользователя. Это рекомендуемый подход для менеджеров пакетов. Разработчик может использовать такой файл следующим образом:
cmake -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=/path/to/package_manager/setup.cmake ...
Для получения подробной информации о том, как реализовать собственный пользовательский поставщик зависимостей, см. команду cmake_language(SET_DEPENDENCY_PROVIDER).
© 2000–2024 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.31/guide/using-dependencies/index.html