Spec-Zone.ru › CMake

Руководство по использованию зависимостей

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

  1. Файлы модулей поиска не должны предоставляться самим пакетом.
  2. Доступность файла Find<PackageName>.cmake не указывает на доступность пакета или какой-либо его части.
  3. CMake не ищет расположения, указанные в переменной CMAKE_PREFIX_PATH, для файлов Find<PackageName>.cmake. Вместо этого CMake ищет такие файлы в расположениях, заданных переменной CMAKE_MODULE_PATH. Пользователи обычно устанавливают переменную CMAKE_MODULE_PATH при запуске CMake, и проекты CMake часто добавляют в CMAKE_MODULE_PATH для использования локальных файлов модулей поиска.
  4. 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/latest/guide/using-dependencies/index.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API