Spec-Zone.ru › CMake 3.26

FetchContent

Новое в версии 3.11.

  • Обзор
  • Команды
  • Переменные
  • Примеры

    • Типичный случай
    • Интеграция с find_package()
    • Переопределение местоположения CMakeLists.txt
    • Сложные иерархии зависимостей
    • Заполнение содержимого без добавления в сборку
    • Заполнение содержимого в режиме сценариев CMake

Примечание

Документация Using Dependencies Guide предоставляет общее введение в данную тему. Она дает более широкую картину того, как модуль FetchContent интегрируется в общую систему, включая его взаимосвязь с командой find_package(). Рекомендуется ознакомиться с этим руководством перед изучением подробностей ниже.

Обзор

Этот модуль позволяет заполнять содержимое во время конфигурации с помощью любого метода, поддерживаемого модулем ExternalProject. В то время как ExternalProject_Add() скачивает данные во время сборки, модуль FetchContent делает содержимое доступным сразу, позволяя шагу конфигурации использовать его в командах, таких как add_subdirectory(), include() или операциях с file().

Подробности заполнения содержимого должны быть определены отдельно от команды, которая выполняет фактическое заполнение. Это разделение гарантирует, что все детали зависимостей будут определены до того, как что-либо может попытаться их использовать для заполнения содержимого. Это особенно важно в более сложных иерархиях проектов, где зависимости могут быть общими для нескольких проектов.

Следующий пример демонстрирует типичный способ объявления деталей содержимого для некоторых зависимостей и обеспечения их заполнения с помощью отдельного вызова:

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)
FetchContent_Declare(
  myCompanyIcons
  URL      https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
  URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)

FetchContent_MakeAvailable(googletest myCompanyIcons)

Команда FetchContent_MakeAvailable() гарантирует, что указанные зависимости заполнены, либо предыдущим вызовом, либо путем их заполнения самой командой. При выполнении заполнения она также добавит их в основную сборку, если это возможно, чтобы основная сборка могла использовать цели и т.д. заполненных проектов. Подробнее об этих этапах см. в документации к команде.

При использовании иерархической структуры проекта проекты на более высоких уровнях иерархии могут переопределять объявленные детали содержимого, указанные на более низких уровнях проекта. Первые объявленные детали для определенной зависимости имеют приоритет, независимо от того, где они объявлены в иерархии проекта. Аналогично, первый вызов, который пытается заполнить зависимость, «выигрывает», и последующие заполнения повторно используют результат первого, вместо того, чтобы повторять заполнение снова. См. Примеры для демонстрации этой ситуации.

В некоторых случаях главному проекту может потребоваться более точный контроль над заполнением или необходимо явно определить шаги заполнения таким образом, который не может быть описан только объявленными деталями. В таких ситуациях можно использовать команды более низкого уровня FetchContent_GetProperties() и FetchContent_Populate(). Однако эти команды лишены расширенных возможностей, предоставляемых FetchContent_MakeAvailable(), поэтому их прямое использование следует рассматривать как крайнюю меру. Типичный шаблон таких пользовательских шагов выглядит так:

# NOTE: Where possible, prefer to use FetchContent_MakeAvailable()
#       instead of custom logic like this

# Check if population has already been performed
FetchContent_GetProperties(depname)
if(NOT depname_POPULATED)
  # Fetch the content using previously declared details
  FetchContent_Populate(depname)

  # Set custom variables, policies, etc.
  # ...

  # Bring the populated content into the build
  add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
endif()

Модуль FetchContent также поддерживает определение и заполнение содержимого в одном вызове без проверки того, было ли содержимое уже заполнено в другом месте. Это не рекомендуется для проектов, но может быть уместно для заполнения содержимого в режиме сценариев CMake. См. FetchContent_Populate() для подробностей.

Команды

FetchContent_Declare
FetchContent_Declare(
  <name>
  <contentOptions>...
  [SYSTEM]
  [OVERRIDE_FIND_PACKAGE |
   FIND_PACKAGE_ARGS args...]
)

Функция FetchContent_Declare() записывает параметры, описывающие, как заполнить указанный контент. Если такие данные уже были записаны ранее в этом проекте (независимо от места в иерархии проекта), этот и все последующие вызовы для того же контента <name> игнорируются. Такой подход «первый записанный имеет приоритет» позволяет родительским проектам в иерархической структуре переопределять детали контента дочерних проектов.

Контент <name> может быть любой строкой без пробелов, но рекомендуется использовать только буквы, цифры и символы подчеркивания. Имя будет обрабатываться без учета регистра и должно быть очевидно для представляемого контента, часто являясь именем дочернего проекта или значением, заданным для команды верхнего уровня project() (если это проект CMake). Для известных публичных проектов имя должно, как правило, совпадать с официальным названием проекта. Выбор необычного имени делает маловероятным использование одного и того же имени другими проектами, нуждающимися в том же контенте, что приводит к многократному заполнению контента.

Параметры <contentOptions> могут быть любыми из параметров загрузки, обновления или исправления, которые понимает команда ExternalProject_Add(). Шаги конфигурации, сборки, установки и тестирования явным образом отключены, поэтому связанные с ними параметры будут проигнорированы. Параметр SOURCE_SUBDIR является исключением, см. FetchContent_MakeAvailable() для получения подробностей о том, как это влияет на поведение.

В большинстве случаев <contentOptions> будет состоять всего из нескольких параметров, определяющих метод загрузки и специфические для метода детали, такие как тег коммита или хэш архива. Например:

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
)

FetchContent_Declare(
  myCompanyIcons
  URL      https://intranet.mycompany.com/assets/iconset_1.12.tar.gz
  URL_HASH MD5=5588a7b18261c20068beabfb4f530b87
)

FetchContent_Declare(
  myCompanyCertificates
  SVN_REPOSITORY svn+ssh://svn.mycompany.com/srv/svn/trunk/certs
  SVN_REVISION   -r12345
)

Если контент извлекается из удалённого расположения, и вы не контролируете этот сервер, рекомендуется использовать хэш для GIT_TAG вместо имени ветки или тега. Хэш коммита более безопасен и помогает подтвердить, что загруженный контент соответствует ожиданиям.

Изменено в версии 3.14: Команды для шагов загрузки, обновления или исправления могут получить доступ к терминалу. Это может потребоваться для таких задач, как запросы паролей или отображение прогресса команд в режиме реального времени.

Добавлена в версии 3.22: Переменные CMAKE_TLS_VERIFY, CMAKE_TLS_CAINFO, CMAKE_NETRC и CMAKE_NETRC_FILE теперь предоставляют значения по умолчанию для соответствующих параметров контента, как и для ExternalProject_Add(). Ранее эти переменные игнорировались модулем FetchContent.

Добавлена в версии 3.24:

FIND_PACKAGE_ARGS

Этот параметр предназначен для случаев, когда команда FetchContent_MakeAvailable() может сначала попытаться вызвать find_package() для удовлетворения зависимости <name>. По умолчанию такой вызов был бы просто find_package(<name>), но FIND_PACKAGE_ARGS может быть использован для предоставления дополнительных аргументов, которые будут добавлены после <name>. FIND_PACKAGE_ARGS также может быть указан без последующих аргументов, что указывает на то, что find_package() все еще может быть вызван, если FETCHCONTENT_TRY_FIND_PACKAGE_MODE установлено в OPT_IN или не установлено.

Всё после ключевого слова FIND_PACKAGE_ARGS добавляется к вызову find_package(), поэтому все остальные <contentOptions> должны стоять перед ключевым словом FIND_PACKAGE_ARGS. Если переменная CMAKE_FIND_PACKAGE_TARGETS_GLOBAL установлена в true во время вызова FetchContent_Declare(), ключевое слово GLOBAL будет добавлено к аргументам find_package(), если оно не было уже указано. Оно также будет добавлено, если FIND_PACKAGE_ARGS не было указано, но FETCHCONTENT_TRY_FIND_PACKAGE_MODE было установлено в ALWAYS.

OVERRIDE_FIND_PACKAGE не может быть использовано, когда указано FIND_PACKAGE_ARGS.

Поставщики зависимостей обсуждает другой способ перенаправления вызовов FetchContent_MakeAvailable(). FIND_PACKAGE_ARGS предназначен для управления проектом, тогда как поставщики зависимостей позволяют пользователям переопределять поведение проекта.

OVERRIDE_FIND_PACKAGE

Когда вызов FetchContent_Declare(<name> ...) включает этот параметр, последующие вызовы find_package(<name> ...) будут гарантировать, что FetchContent_MakeAvailable(<name>) был вызван, а затем использовать файлы конфигурации пакета в каталоге CMAKE_FIND_PACKAGE_REDIRECTS_DIR (которые обычно создаются FetchContent_MakeAvailable()). Это фактически заставляет FetchContent_MakeAvailable() переопределять find_package() для указанной зависимости, позволяя первому удовлетворить требования к пакету последнего. FIND_PACKAGE_ARGS не может быть использовано, когда указано OVERRIDE_FIND_PACKAGE.

Если поставщик зависимостей был установлен и проект вызывает find_package() для зависимости <name>, OVERRIDE_FIND_PACKAGE не помешает поставщику увидеть этот вызов. Поставщики зависимостей всегда имеют возможность перехватывать любой прямой вызов find_package(), за исключением случаев, когда этот вызов содержит параметр BYPASS_PROVIDER.

Добавлена в версии 3.25:

SYSTEM

Если предоставлен аргумент SYSTEM, свойство каталога SYSTEM подкаталога, добавленного командой FetchContent_MakeAvailable(), будет установлено в true. Это повлияет на неимпортированные цели, созданные в рамках этой команды. См. документацию свойства цели SYSTEM для более подробного обсуждения последствий.

FetchContent_MakeAvailable

Новое в версии 3.14.

FetchContent_MakeAvailable(<name1> [<name2>...])

Эта команда гарантирует, что все указанные зависимости станут доступными для проекта ко времени его возврата. Должна быть вызвана команда FetchContent_Declare() для каждой зависимости, и первый такой вызов будет определять, как эта зависимость будет сделана доступной, как описано ниже.

Если <lowercaseName>_SOURCE_DIR не задано:

  • Новое в версии 3.24: Если установлен поставщик зависимостей, вызовите команду поставщика с FETCHCONTENT_MAKEAVAILABLE_SERIAL в качестве первого аргумента, а затем аргументами первого вызова FetchContent_Declare() для <name>. Если SOURCE_DIR или BINARY_DIR не были частью исходных аргументов, они будут добавлены со значениями по умолчанию. Если FETCHCONTENT_TRY_FIND_PACKAGE_MODE было установлено в NEVER при объявлении деталей, любые FIND_PACKAGE_ARGS будут опущены. Ключевое слово OVERRIDE_FIND_PACKAGE также всегда опускается. Если поставщик выполнил запрос, FetchContent_MakeAvailable() будет считать, что зависимость обработана, пропустит оставшиеся шаги и перейдет к следующей зависимости в списке.

  • Новое в версии 3.24: Если разрешено, будет вызван find_package(<name> [<args>...]), где <args>... может быть предоставлено параметром FIND_PACKAGE_ARGS в FetchContent_Declare(). Значение переменной FETCHCONTENT_TRY_FIND_PACKAGE_MODE на момент вызова FetchContent_Declare() определяет, может ли FetchContent_MakeAvailable() вызвать find_package(). Если переменная CMAKE_FIND_PACKAGE_TARGETS_GLOBAL установлена в true при вызове FetchContent_MakeAvailable(), она по-прежнему влияет на импортированные цели, созданные при вызове find_package(), даже если эта переменная была false при объявлении соответствующих деталей.

Если зависимость не была удовлетворена поставщиком или вызовом find_package(), FetchContent_MakeAvailable() использует следующий алгоритм, чтобы сделать зависимость доступной:

  • Если зависимость уже была обработана ранее в этом запуске, установите переменные <lowercaseName>_POPULATED, <lowercaseName>_SOURCE_DIR и <lowercaseName>_BINARY_DIR аналогично вызову FetchContent_GetProperties(), затем пропустите оставшиеся шаги и перейдите к следующей зависимости в списке.
  • Вызовите FetchContent_Populate() для обработки зависимости, используя детали, записанные в предыдущем вызове FetchContent_Declare(). Остановитесь с ошибкой, если такие детали не были записаны. FETCHCONTENT_SOURCE_DIR_<uppercaseName> можно использовать для переопределения объявленных деталей и использования контента из указанного расположения вместо этого.
  • Новое в версии 3.24: Убедитесь, что каталог CMAKE_FIND_PACKAGE_REDIRECTS_DIR содержит файлы <lowercaseName>-config.cmake и <lowercaseName>-config-version.cmake (или эквивалентные <name>Config.cmake и <name>ConfigVersion.cmake). Директория, на которую указывает переменная CMAKE_FIND_PACKAGE_REDIRECTS_DIR, очищается в начале каждого запуска CMake. Если конфигурационный файл не существует, когда FetchContent_Populate() возвращает значение, будет создан минимальный файл, который includes любые файлы <lowercaseName>-extra.cmake или <name>Extra.cmake со флагом OPTIONAL (так что файлы могут отсутствовать, и не будет генерироваться предупреждение). Аналогично, если файл версии конфигурации не существует, будет создан очень простой файл, который установит PACKAGE_VERSION_COMPATIBLE и PACKAGE_VERSION_EXACT в true. Это гарантирует, что все последующие вызовы find_package() для зависимости будут использовать перенаправленный конфигурационный файл, независимо от каких-либо требований к версии. CMake не может автоматически определить версию произвольной зависимости, поэтому он не может установить PACKAGE_VERSION. Когда зависимость подключается через add_subdirectory() на следующем шаге, она может выбрать перезапись сгенерированного файла версии конфигурации в CMAKE_FIND_PACKAGE_REDIRECTS_DIR таким образом, чтобы также установить PACKAGE_VERSION. Зависимость также может записать файлы <lowercaseName>-extra.cmake или <name>Extra.cmake для выполнения пользовательской обработки или определения переменных, которые обычно определяются их обычным (установленным) конфигурационным файлом пакета (многие проекты не выполняют никакой пользовательской обработки или не устанавливают никаких переменных и поэтому не нуждаются в этом). При необходимости главный проект может записать эти файлы, если проект зависимости не сделает этого. Это позволяет главному проекту добавлять отсутствующие детали из старых зависимостей, которые не были или не могут быть обновлены для поддержки этой функциональности. См. Интеграция с find_package() для примеров.

  • Если в корневой директории обработанного содержимого есть файл CMakeLists.txt, вызовите add_subdirectory() для добавления его в основной сборку. Отсутствие файла CMakeLists.txt не является ошибкой, что позволяет использовать команду для зависимостей, которые делают доступным загруженное содержимое в известном месте, но которым не нужно или не поддерживается непосредственное добавление в сборку.

    Новое в версии 3.18: Параметр SOURCE_SUBDIR можно указать в объявленных деталях, чтобы искать что-то ниже корневой директории вместо этого (аналогично тому, как SOURCE_SUBDIR используется командой ExternalProject_Add()). Путь, предоставленный с параметром SOURCE_SUBDIR, должен быть относительным и будет обрабатываться как относительный к корневой директории. Он также может указывать на директорию, которая не содержит файл CMakeLists.txt или даже на не существующую директорию. Это можно использовать для избежания добавления проекта, содержащего файл CMakeLists.txt в своей корневой директории.

    Новое в версии 3.25: Если ключевое слово SYSTEM было включено в вызов FetchContent_Declare(), ключевое слово SYSTEM будет добавлено к команде add_subdirectory() также.

Проекты должны стремиться объявлять детали всех зависимостей, которые они могут использовать, до того, как вызовут FetchContent_MakeAvailable() для любой из них. Это гарантирует, что если какие-либо зависимости также являются подзависимостями одной или нескольких других, основной проект все равно контролирует детали, которые будут использоваться (потому что он объявит их первым, прежде чем зависимости получат возможность).

В следующих примерах кода предполагается, что зависимость uses_other также использует FetchContent для добавления зависимости other внутри себя:

# WRONG: Should declare all details first
FetchContent_Declare(uses_other ...)
FetchContent_MakeAvailable(uses_other)

FetchContent_Declare(other ...)    # Will be ignored, uses_other beat us to it
FetchContent_MakeAvailable(other)  # Would use details declared by uses_other
# CORRECT: All details declared first, so they will take priority
FetchContent_Declare(uses_other ...)
FetchContent_Declare(other ...)
FetchContent_MakeAvailable(uses_other other)

Обратите внимание, что CMAKE_VERIFY_INTERFACE_HEADER_SETS явно устанавливается в значение false при входе в FetchContent_MakeAvailable(), и восстанавливается до своего первоначального значения перед возвратом команды. Разработчики обычно хотят проверять наборы заголовков только из основного проекта, а не из зависимостей. Эта локальная манипуляция переменной CMAKE_VERIFY_INTERFACE_HEADER_SETS обеспечивает такое интуитивное поведение. Вы можете использовать переменные, такие как CMAKE_PROJECT_INCLUDE или CMAKE_PROJECT_<PROJECT-NAME>_INCLUDE, чтобы включить проверку для всех или некоторых зависимостей. Также можно установить свойство VERIFY_INTERFACE_HEADER_SETS отдельных целевых объектов.

FetchContent_Populate

Примечание

По возможности используйте FetchContent_MakeAvailable() вместо ручного заполнения с помощью этой команды.

FetchContent_Populate(<name>)

В большинстве случаев единственным аргументом, переданным команде FetchContent_Populate(), является <name>. В этом случае команда предполагает, что подробности о содержимом были записаны в предыдущем вызове FetchContent_Declare(). Данные хранятся в глобальном свойстве, поэтому они не зависят от таких вещей, как область действия переменных или директорий. Поэтому не имеет значения, где в проекте были предварительно объявлены данные, до тех пор, пока они были объявлены перед вызовом FetchContent_Populate(). Эти сохраненные данные затем используются для построения вызова ExternalProject_Add() в частном подстрое для немедленного заполнения содержимого. Реализация ExternalProject_Add() гарантирует, что если содержимое уже было заполнено в предыдущем запуске CMake, это содержимое будет повторно использовано, а не перезаполнено. В общем случае, когда заполнение включает загрузку содержимого, стоимость загрузки оплачивается только один раз.

Внутреннее глобальное свойство регистрирует, когда запрос на заполнение конкретного содержимого был обработан. Если FetchContent_Populate() вызывается более одного раза для одного и того же имени содержимого в ходе конфигурации, второй вызов завершится ошибкой. Проекты могут и должны проверить, было ли заполнение содержимого уже обработано с помощью команды FetchContent_GetProperties() перед вызовом FetchContent_Populate().

FetchContent_Populate() установит три переменные в области видимости вызывающей программы:

<lowercaseName>_POPULATED

Это всегда будет установлено в TRUE вызовом.

<lowercaseName>_SOURCE_DIR

Местоположение, где можно найти заполненное содержимое по возвращении.

<lowercaseName>_BINARY_DIR

Директория, предназначенная для использования в качестве соответствующей директории построения.

Основное использование переменных <lowercaseName>_SOURCE_DIR и <lowercaseName>_BINARY_DIR заключается в вызове add_subdirectory() сразу после заполнения:

FetchContent_Populate(FooBar)
add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR})

Значения трех переменных также могут быть получены из любой точки иерархии проекта с помощью команды FetchContent_GetProperties().

Команда FetchContent_Populate() также поддерживает синтаксис, позволяющий указать данные о содержимом напрямую, а не использовать сохраненные данные. Это более низкий уровень, и использование этой формы обычно следует избегать в пользу использования сохраненных данных о содержимом, как указано выше. Тем не менее, в определенных ситуациях это может быть полезно для вызова заполнения содержимого как изолированной операции (обычно в рамках реализации какой-либо другой функции более высокого уровня или при использовании CMake в режиме сценариев):

FetchContent_Populate(
  <name>
  [QUIET]
  [SUBBUILD_DIR <subBuildDir>]
  [SOURCE_DIR <srcDir>]
  [BINARY_DIR <binDir>]
  ...
)

Эта форма имеет ряд ключевых отличий от той, где указан только <name>:

  • Все необходимые данные о заполнении предполагается предоставить непосредственно в вызове FetchContent_Populate(). Любые сохраненные данные для <name> игнорируются.
  • Не производится проверка, было ли содержимое для <name> уже заполнено.
  • Не устанавливается глобальное свойство для записи о том, что заполнение произошло.
  • Не сохраняются глобальные свойства источника или бинарных директорий, используемых для заполненного содержимого.
  • Переменные кэша FETCHCONTENT_FULLY_DISCONNECTED и FETCHCONTENT_UPDATES_DISCONNECTED игнорируются.

Переменные <lowercaseName>_SOURCE_DIR и <lowercaseName>_BINARY_DIR по-прежнему возвращаются вызывающей программе, но поскольку эти расположения не сохраняются как глобальные свойства при использовании этой формы, они доступны только в области видимости вызывающей программы и ниже, а не во всей иерархии проекта. Переменная <lowercaseName>_POPULATED не устанавливается в области видимости вызывающей программы в этой форме.

Поддерживаемые опции для FetchContent_Populate() совпадают с опциями для FetchContent_Declare(). Те несколько опций, показанных выше, либо специфичны для FetchContent_Populate(), либо их поведение немного отличается от того, как ExternalProject_Add() их обрабатывает:

QUIET

Опция QUIET может быть задана для скрытия вывода, связанного с заполнением указанного содержимого. Если заполнение завершится ошибкой, вывод будет показан независимо от того, была ли задана эта опция или нет, чтобы можно было диагностировать причину ошибки. Глобальная переменная кэша FETCHCONTENT_QUIET не оказывает влияния на вызовы FetchContent_Populate(), где данные о содержимом предоставляются напрямую.

SUBBUILD_DIR

Аргумент SUBBUILD_DIR может быть задан для изменения расположения подстрое, созданного для выполнения заполнения. Значение по умолчанию ${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-subbuild, и вряд ли возникнет необходимость в его переопределении. Если указан относительный путь, он будет интерпретирован относительно CMAKE_CURRENT_BINARY_DIR. Эту опцию не следует путать с опцией SOURCE_SUBDIR, которая влияет только на команду FetchContent_MakeAvailable().

SOURCE_DIR, BINARY_DIR

Аргументы SOURCE_DIR и BINARY_DIR поддерживаются ExternalProject_Add(), но FetchContent_Populate() используют разные значения по умолчанию. SOURCE_DIR по умолчанию ${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-src, а BINARY_DIR по умолчанию ${CMAKE_CURRENT_BINARY_DIR}/<lowercaseName>-build. Если указан относительный путь, он будет интерпретирован относительно CMAKE_CURRENT_BINARY_DIR.

В дополнение к явным опциям выше, любые другие нераспознанные опции передаются без изменений в ExternalProject_Add() для выполнения шагов загрузки, патчинга и обновления. Следующие опции запрещены явно (они отключены командой FetchContent_Populate()):

  • CONFIGURE_COMMAND
  • BUILD_COMMAND
  • INSTALL_COMMAND
  • TEST_COMMAND

Если использовать FetchContent_Populate() в режиме сценариев CMake, имейте в виду, что реализация создает подстрой, поэтому для этого требуется генератор CMake и инструмент построения. Если они не могут быть найдены по умолчанию, то переменные CMAKE_GENERATOR и/или CMAKE_MAKE_PROGRAM необходимо установить соответствующим образом в командной строке, вызывающей сценарий.

Введено в версии 3.18: Добавлена поддержка опции DOWNLOAD_NO_EXTRACT.

FetchContent_GetProperties

При использовании сохранённых данных о содержимом, вызов FetchContent_MakeAvailable() или FetchContent_Populate() записывает информацию в глобальные свойства, к которым можно обратиться в любое время. Эта информация может включать каталоги исходных и бинарных файлов, связанных с содержимым, а также информацию о том, было ли выполнено заполнение содержимого во время текущего запуска конфигурации.

FetchContent_GetProperties(
  <name>
  [SOURCE_DIR <srcDirVar>]
  [BINARY_DIR <binDirVar>]
  [POPULATED <doneVar>]
)

Опции SOURCE_DIR, BINARY_DIR и POPULATED можно использовать для указания, какие свойства должны быть получены. Каждая опция принимает значение, представляющее имя переменной, в которую должно быть сохранено это свойство. В большинстве случаев используется только <name>, в результате чего вызов устанавливает те же переменные, что и вызов FetchContent_MakeAvailable(name) или FetchContent_Populate(name). Обратите внимание, что значения SOURCE_DIR и BINARY_DIR могут быть пустыми, если вызов выполняется поставщиком зависимостей поставщиком зависимостей.

Этот команд используется редко при использовании FetchContent_MakeAvailable(). Он чаще используется в рамках реализации следующего шаблона с FetchContent_Populate(), что гарантирует, что соответствующие переменные всегда будут определены, независимо от того, было ли выполнено заполнение в другой части проекта:

# Check if population has already been performed
FetchContent_GetProperties(depname)
if(NOT depname_POPULATED)
  # Fetch the content using previously declared details
  FetchContent_Populate(depname)

  # Set custom variables, policies, etc.
  # ...

  # Bring the populated content into the build
  add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
endif()
FetchContent_SetPopulated

Новая версия с 3.24.

Примечание

Этот команд должен вызываться только поставщиками зависимостей поставщиками зависимостей. Вызов в любом другом контексте не поддерживается, и будущие версии CMake могут завершиться с ошибкой в таких случаях.

FetchContent_SetPopulated(
  <name>
  [SOURCE_DIR <srcDir>]
  [BINARY_DIR <binDir>]
)

Если команд поставщика выполняет запрос FETCHCONTENT_MAKEAVAILABLE_SERIAL, он должен вызвать эту функцию перед возвратом. Аргументы SOURCE_DIR и BINARY_DIR можно использовать для указания значений, которые FetchContent_GetProperties() должен возвращать для соответствующих аргументов. Предоставляйте SOURCE_DIR и BINARY_DIR только в том случае, если они имеют такое же значение, как если бы они были заполнены встроенной реализацией FetchContent_MakeAvailable().

Переменные

Несколько переменных кэша могут влиять на поведение, когда данные из вызова FetchContent_Declare() используются для заполнения содержимого.

Примечание

Все эти переменные предназначены для настройки поведения разработчиком. Обычно они не должны устанавливаться проектом.

FETCHCONTENT_BASE_DIR

В большинстве случаев сохранённые данные не содержат опций, относящихся к каталогам, используемым для внутреннего подстроения, конечных областей исходного и бинарного кода. Обычно лучше оставить эти решения модулю FetchContent для обработки от имени проекта. Переменная кэша FETCHCONTENT_BASE_DIR управляет точкой сбора всех каталогов заполнения содержимого, но в большинстве случаев разработчикам не нужно этого изменять. По умолчанию используется расположение ${CMAKE_BINARY_DIR}/_deps, но если разработчик изменит это значение, он должен стараться поддерживать короткий путь, расположенный чуть ниже верхнего уровня дерева построения, чтобы избежать проблем с длиной пути в Windows.

FETCHCONTENT_QUIET

Вывод журналов во время заполнения может быть довольно подробным, что делает этап конфигурации довольно шумным. Эта опция кэша (по умолчанию ON) скрывает весь вывод заполнения, если не возникает ошибка. Если возникают проблемы с зависанием загрузок, временное отключение этой опции может помочь диагностировать, какая операция заполнения содержимого вызывает проблему.

FETCHCONTENT_FULLY_DISCONNECTED

Когда эта опция включена, не предпринимается попыток загрузить или обновить какое-либо содержимое. Предполагается, что всё содержимое уже было заполнено в предыдущем запуске или каталоги исходных файлов указывают на существующее содержимое, предоставленное разработчиком вручную (используя опции, описанные ниже). Если разработчик знает, что изменения в данных о содержимом не вносились, включение этой опции ON может значительно ускорить этап конфигурации. По умолчанию она OFF.

FETCHCONTENT_UPDATES_DISCONNECTED

Это менее жёсткий контроль за загрузкой/обновлением по сравнению с FETCHCONTENT_FULLY_DISCONNECTED. Вместо того чтобы обойти всю логику загрузки и обновления, FETCHCONTENT_UPDATES_DISCONNECTED отключает только этап обновления. Поэтому, если содержимое ранее не было загружено, оно всё равно будет загружено, когда эта опция включена. Это может ускорить этап конфигурации, но не так сильно, как FETCHCONTENT_FULLY_DISCONNECTED. По умолчанию она OFF.

FETCHCONTENT_TRY_FIND_PACKAGE_MODE

Новая в версии 3.24.

Эта переменная изменяет детали, которые FetchContent_Declare() записывает для заданной зависимости. Хотя она в конечном итоге контролирует поведение FetchContent_MakeAvailable(), используется значение переменной при вызове FetchContent_Declare(). Это никак не влияет на то, чему установлено значение переменной при вызове FetchContent_MakeAvailable(). Поскольку переменная должна устанавливаться только пользователем, а не проектами напрямую, она обычно имеет одно и то же значение на протяжении всего процесса, поэтому это различие обычно незаметно.

FETCHCONTENT_TRY_FIND_PACKAGE_MODE в конечном итоге контролирует, разрешено ли FetchContent_MakeAvailable() вызывать find_package() для удовлетворения зависимости. Переменная может принимать следующие значения:

OPT_IN

FetchContent_MakeAvailable() будет вызывать find_package() только если вызов FetchContent_Declare() включал ключевое слово FIND_PACKAGE_ARGS. Это также является поведением по умолчанию, если FETCHCONTENT_TRY_FIND_PACKAGE_MODE не задано.

ALWAYS

find_package() может вызываться FetchContent_MakeAvailable() независимо от того, включал ли вызов FetchContent_Declare() ключевое слово FIND_PACKAGE_ARGS или нет. Если ключевое слово FIND_PACKAGE_ARGS не было указано, поведение будет таким же, как если бы было предоставлено FIND_PACKAGE_ARGS, без дополнительных аргументов после него.

NEVER

FetchContent_MakeAvailable() не будет вызывать find_package(). Любое ключевое слово FIND_PACKAGE_ARGS, указанное в вызове FetchContent_Declare(), будет проигнорировано.

В качестве специального случая, если переменная FETCHCONTENT_SOURCE_DIR_<uppercaseName> имеет непустое значение для зависимости, предполагается, что пользователь переопределяет все другие способы предоставления этой зависимости. FETCHCONTENT_TRY_FIND_PACKAGE_MODE не повлияет на эту зависимость, и FetchContent_MakeAvailable() не будет пытаться вызывать find_package() для нее.

В дополнение к вышеперечисленному, для каждого имени содержимого также определены следующие переменные:

FETCHCONTENT_SOURCE_DIR_<uppercaseName>

Если это установлено, шаги скачивания и обновления для указанного содержимого не выполняются, и переменная <lowercaseName>_SOURCE_DIR, возвращённая вызывающей стороне, указывает на это местоположение. Это даёт разработчикам возможность иметь отдельную копию содержимого, которую они могут свободно изменять без вмешательства сборки. Сборка просто использует существующий исходный код, но всё равно определяет <lowercaseName>_BINARY_DIR, указывающее на место внутри собственной области сборки. Разработчикам настоятельно рекомендуется использовать этот механизм вместо редактирования исходного кода по умолчанию, так как изменения исходного кода по умолчанию могут быть потеряны, когда сведения о заполнении содержимого изменены проектом.

FETCHCONTENT_UPDATES_DISCONNECTED_<uppercaseName>

Это аналог переменной FETCHCONTENT_UPDATES_DISCONNECTED для каждого содержимого. Если глобальный параметр или этот параметр равен ON, то обновления будут отключены для указанного содержимого. Отключение обновлений для отдельных содержимых может быть полезно для содержимого, сведения о котором редко меняются, сохраняя при этом обновления для часто меняющегося содержимого включёнными.

Примеры

Типичный случай

В этом первом довольно простом примере гарантируется, что некоторые популярные фреймворки тестирования доступны для основной сборки:

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
)

# After the following call, the CMake targets defined by googletest and
# Catch2 will be available to the rest of the build
FetchContent_MakeAvailable(googletest Catch2)

Интеграция с find_package()

Для предыдущего примера, если пользователь захотел сначала попытаться найти googletest и Catch2 с помощью find_package(), прежде чем пытаться скачать и собрать их из исходного кода, он мог установить переменную FETCHCONTENT_TRY_FIND_PACKAGE_MODE в значение ALWAYS. Это также повлияло бы на все остальные вызовы FetchContent_Declare() в проекте, что может быть неприемлемо. Поведение можно включить только для этих двух зависимостей, добавив FIND_PACKAGE_ARGS к объявленным деталям и оставив FETCHCONTENT_TRY_FIND_PACKAGE_MODE не заданной или установленной в значение OPT_IN.

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_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1
  FIND_PACKAGE_ARGS
)

# This will try calling find_package() first for both dependencies
FetchContent_MakeAvailable(googletest Catch2)

Для Catch2, дополнительные аргументы для find_package() не нужны, поэтому дополнительные аргументы не предоставляются после ключевого слова FIND_PACKAGE_ARGS. Для googletest, её пакет чаще называют GTest, поэтому добавляются аргументы для поддержки поиска по этому имени.

Если пользователь хотел отключить FetchContent_MakeAvailable() от вызова find_package() для любой зависимости, даже если она предоставляет FIND_PACKAGE_ARGS в своих объявленных деталях, он мог установить FETCHCONTENT_TRY_FIND_PACKAGE_MODE в значение NEVER.

Если проект хотел указать, что эти две зависимости должны быть загружены и собраны из исходного кода и что вызовы find_package() должны быть перенаправлены для использования собранных зависимостей, то параметр OVERRIDE_FIND_PACKAGE должен быть использован при объявлении деталей содержимого:

include(FetchContent)
FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        703bd9caab50b139428cea1aaff9974ebee5742e # release-1.10.0
  OVERRIDE_FIND_PACKAGE
)
FetchContent_Declare(
  Catch2
  GIT_REPOSITORY https://github.com/catchorg/Catch2.git
  GIT_TAG        605a34765aa5d5ecbf476b4598a862ada971b0cc # v3.0.1
  OVERRIDE_FIND_PACKAGE
)

# The following will automatically forward through to FetchContent_MakeAvailable()
find_package(googletest)
find_package(Catch2)

CMake предоставляет модуль FindGTest, который определяет некоторые переменные, которые могут использовать более старые проекты вместо связывания с импортированными целевыми объектами. Для поддержки таких случаев мы можем предоставить дополнительный файл. В соответствии с философией «первый определяет, выигрывает» FetchContent, мы записываем этот файл только если это ещё не сделано.

FetchContent_MakeAvailable(googletest)

if(NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletest-extra.cmake AND
   NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletestExtra.cmake)
  file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletest-extra.cmake
[=[
if("${GTEST_LIBRARIES}" STREQUAL "" AND TARGET GTest::gtest)
  set(GTEST_LIBRARIES GTest::gtest)
endif()
if("${GTEST_MAIN_LIBRARIES}" STREQUAL "" AND TARGET GTest::gtest_main)
  set(GTEST_MAIN_LIBRARIES GTest::gtest_main)
endif()
if("${GTEST_BOTH_LIBRARIES}" STREQUAL "")
  set(GTEST_BOTH_LIBRARIES ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
endif()
]=])
endif()

Проекты, вероятно, также будут использовать find_package(GTest) вместо find_package(googletest), но можно использовать область CMAKE_FIND_PACKAGE_REDIRECTS_DIR для включения последнего как зависимости первого. Это, вероятно, будет достаточно для удовлетворения типичного вызова find_package(GTest).

FetchContent_MakeAvailable(googletest)

if(NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config.cmake AND
   NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/GTestConfig.cmake)
  file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config.cmake
[=[
include(CMakeFindDependencyMacro)
find_dependency(googletest)
]=])
endif()

if(NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config-version.cmake AND
   NOT EXISTS ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/GTestConfigVersion.cmake)
  file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/gtest-config-version.cmake
[=[
include(${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletest-config-version.cmake OPTIONAL)
if(NOT PACKAGE_VERSION_COMPATIBLE)
  include(${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/googletestConfigVersion.cmake OPTIONAL)
endif()
]=])
endif()

Переопределение места нахождения CMakeLists.txt

Если файл CMakeLists.txt подпроекта не находится в корне его дерева исходного кода, можно использовать параметр SOURCE_SUBDIR для указания FetchContent места его нахождения. Следующий пример демонстрирует как использовать этот параметр, а также устанавливает переменную, имеющую значение для подпроекта, прежде чем включать его в основную сборку (установлено как кеш-переменная INTERNAL для избежания проблем с политикой CMP0077):

include(FetchContent)
FetchContent_Declare(
  protobuf
  GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git
  GIT_TAG        ae50d9b9902526efd6c7a1907d09739f959c6297 # v3.15.0
  SOURCE_SUBDIR  cmake
)
set(protobuf_BUILD_TESTS OFF CACHE INTERNAL "")
FetchContent_MakeAvailable(protobuf)

Сложные иерархии зависимостей

В более сложных иерархиях проектов зависимости могут быть более сложными. Рассмотрим иерархию, где projA является проектом верхнего уровня, и он напрямую зависит от проектов projB и projC. Оба проекта projB и projC могут быть построены автономно, и оба также зависят от другого проекта projD. projB дополнительно зависит от projE. Этот пример предполагает, что все пять проектов доступны на сервере Git компании. В CMakeLists.txt каждого проекта могут быть разделы, подобные следующим:

projA
include(FetchContent)
FetchContent_Declare(
  projB
  GIT_REPOSITORY git@mycompany.com:git/projB.git
  GIT_TAG        4a89dc7e24ff212a7b5167bef7ab079d
)
FetchContent_Declare(
  projC
  GIT_REPOSITORY git@mycompany.com:git/projC.git
  GIT_TAG        4ad4016bd1d8d5412d135cf8ceea1bb9
)
FetchContent_Declare(
  projD
  GIT_REPOSITORY git@mycompany.com:git/projD.git
  GIT_TAG        origin/integrationBranch
)
FetchContent_Declare(
  projE
  GIT_REPOSITORY git@mycompany.com:git/projE.git
  GIT_TAG        v2.3-rc1
)

# Order is important, see notes in the discussion further below
FetchContent_MakeAvailable(projD projB projC)
projB
include(FetchContent)
FetchContent_Declare(
  projD
  GIT_REPOSITORY git@mycompany.com:git/projD.git
  GIT_TAG        20b415f9034bbd2a2e8216e9a5c9e632
)
FetchContent_Declare(
  projE
  GIT_REPOSITORY git@mycompany.com:git/projE.git
  GIT_TAG        68e20f674a48be38d60e129f600faf7d
)

FetchContent_MakeAvailable(projD projE)
projC
include(FetchContent)
FetchContent_Declare(
  projD
  GIT_REPOSITORY git@mycompany.com:git/projD.git
  GIT_TAG        7d9a17ad2c962aa13e2fbb8043fb6b8a
)

# This particular version of projD requires workarounds
FetchContent_GetProperties(projD)
if(NOT projd_POPULATED)
  FetchContent_Populate(projD)

  # Copy an additional/replacement file into the populated source
  file(COPY someFile.c DESTINATION ${projd_SOURCE_DIR}/src)

  add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR})
endif()

Несколько ключевых моментов следует отметить выше:

  • projB и projC определяют разные детали содержимого для projD, но projA также определяет набор деталей содержимого для projD. Поскольку projA определит их первыми, детали из projB и projC не будут использоваться. Детали переопределения, определенные projA, не обязаны совпадать ни с одной из деталей projB или projC, но проект верхнего уровня должен обеспечить, чтобы определённые им детали всё ещё имели смысл для дочерних проектов.
  • В вызове projA к FetchContent_MakeAvailable(), projD перечислен перед projB и projC, чтобы гарантировать, что projA контролирует, как заполняется projD.
  • Хотя projA определяет детали содержимого для projE, ему не нужно явно вызывать FetchContent_MakeAvailable(projE) или FetchContent_Populate(projD) сам. Вместо этого он оставляет это дочерним projB. Для проектов верхнего уровня часто достаточно просто определить детали переопределяемого содержимого и оставить фактическое заполнение дочерним проектам. Это позволяет избежать излишнего повторения одних и тех же действий на каждом уровне иерархии проектов.

Заполнение содержимого без добавления его в сборку

Проектам не всегда нужно добавлять заполненное содержимое в сборку. Иногда проект просто хочет сделать загруженное содержимое доступным в предсказуемом месте. Следующий пример гарантирует, что набор стандартных файлов инструментальной цепочки компании (и, возможно, даже сами двоичные файлы инструментальной цепочки) будут доступны достаточно рано для использования в этой же сборке.

cmake_minimum_required(VERSION 3.14)

include(FetchContent)
FetchContent_Declare(
  mycom_toolchains
  URL  https://intranet.mycompany.com//toolchains_1.3.2.tar.gz
)
FetchContent_MakeAvailable(mycom_toolchains)

project(CrossCompileExample)

Проект можно настроить для использования одного из загруженных наборов инструментов следующим образом:

cmake -DCMAKE_TOOLCHAIN_FILE=_deps/mycom_toolchains-src/toolchain_arm.cmake /path/to/src

Когда CMake обрабатывает файл CMakeLists.txt, он загрузит и распакует архив tar в _deps/mycompany_toolchains-src относительно каталога сборки. Переменная CMAKE_TOOLCHAIN_FILE не используется до тех пор, пока не будет достигнут команд project(), на котором CMake ищет указанный файл инструментальной цепочки относительно каталога сборки. Поскольку архив tar уже загружен и распакован к этому моменту, файл инструментальной цепочки будет на месте, даже в первый раз, когда cmake запускается в каталоге сборки.

Заполнение содержимого в режиме сценариев CMake

Этот последний пример демонстрирует, как можно загрузить и распаковать архив tar с микропрограммой с помощью CMake's script mode. Вызов FetchContent_Populate() указывает все детали содержимого, и распакованная микропрограмма будет помещена в каталог firmware ниже текущего рабочего каталога.

getFirmware.cmake
# NOTE: Intended to be run in script mode with cmake -P
include(FetchContent)
FetchContent_Populate(
  firmware
  URL        https://mycompany.com/assets/firmware-1.23-arm.tar.gz
  URL_HASH   MD5=68247684da89b608d466253762b0ff11
  SOURCE_DIR firmware
)

© 2000–2023 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.26/module/FetchContent.html

Spec-Zone.ru

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