Руководство по использованию зависимостей
- Введение
-
Использование предварительно собранных пакетов с
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
Новая функция в версии 3.24.
Зависимости не обязательно должны быть предварительно скомпилированы, чтобы их можно было использовать с 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.30/guide/using-dependencies/index.html