Spec-Zone.ru › CMake 3.12

FetchContent

  • Обзор
  • Определение деталей контента
  • Заполнение контента
  • Получение свойств заполнения
  • Примеры

Обзор

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

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

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        release-1.8.0
)

FetchContent_GetProperties(googletest)
if(NOT googletest_POPULATED)
  FetchContent_Populate(googletest)
  add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
endif()

При использовании вышеприведенной схемы с иерархической организацией проекта, проекты на более высоких уровнях иерархии могут определять или переопределять подробности заполнения контента, указанного где-либо ниже в иерархии проекта. Возможность обнаружить, был ли контент уже заполнен, гарантирует, что даже если несколько дочерних проектов хотят, чтобы определенный контент был доступен, первый, кто его заполнит, победит. Другой дочерний проект может просто использовать уже доступный контент, а не повторять заполнение для себя. См. раздел Примеры, который демонстрирует эту ситуацию.

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

Определение деталей контента

FetchContent_Declare
FetchContent_Declare(<name> <contentOptions>...)

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

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

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

FetchContent_Declare(
  googletest
  GIT_REPOSITORY https://github.com/google/googletest.git
  GIT_TAG        release-1.8.0
)

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

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

Заполнение контента

FetchContent_Populate
FetchContent_Populate( <name> )

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

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

FetchContent_Populate() установит три переменные в области видимости вызывающего; <lcName>_POPULATED, <lcName>_SOURCE_DIR и <lcName>_BINARY_DIR, где <lcName> — это строка <name> в нижнем регистре. <lcName>_POPULATED всегда будет установлено в True вызывающей стороной. <lcName>_SOURCE_DIR — это местоположение, где содержимое будет найдено по возвращении (оно уже будет заполнено), а <lcName>_BINARY_DIR — это каталог, предназначенный для использования в качестве соответствующего каталога сборки. Основной сценарий использования двух переменных каталогов — вызов add_subdirectory() сразу после заполнения, например:

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

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

Ряд переменных кэша влияют на поведение всего заполнения содержимого, выполняемого с использованием сведений, сохранённых из вызова 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.

В дополнение к вышеперечисленным переменным кэша, для каждого имени содержимого также определены следующие переменные кэша (<ucName> — это значение <name> в верхнем регистре):

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

Команда 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 игнорируются.

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

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

QUIET
Параметр QUIET можно указать для скрытия вывода, связанного с заполнением указанного содержимого. Если заполнение завершится ошибкой, вывод будет показан независимо от того, был ли указан этот параметр или нет, чтобы можно было диагностировать причину ошибки. Глобальная переменная кэша FETCHCONTENT_QUIET не влияет на вызовы FetchContent_Populate() с прямым указанием сведений о содержимом.
SUBBUILD_DIR
Аргумент SUBBUILD_DIR можно указать для изменения расположения подсборки, созданной для выполнения заполнения. Значение по умолчанию — ${CMAKE_CURRENT_BINARY_DIR}/<lcName>-subbuild, и обычно нет необходимости переопределять это значение по умолчанию. Если указан относительный путь, он будет интерпретироваться как относительный к CMAKE_CURRENT_BINARY_DIR.
SOURCE_DIR, BINARY_DIR
Аргументы SOURCE_DIR и BINARY_DIR поддерживаются ExternalProject_Add(), но FetchContent_Populate() используют разные значения по умолчанию. SOURCE_DIR по умолчанию ${CMAKE_CURRENT_BINARY_DIR}/<lcName>-src, а BINARY_DIR по умолчанию ${CMAKE_CURRENT_BINARY_DIR}/<lcName>-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 необходимо соответствующим образом задать в командной строке, вызывающей скрипт.

Получение свойств популяции

FetchContent_GetProperties

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

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

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

FetchContent_GetProperties(foobar)
if(NOT foobar_POPULATED)
  FetchContent_Populate(foobar)

  # Set any custom variables, etc. here, then
  # populate the content as part of this build

  add_subdirectory(${foobar_SOURCE_DIR} ${foobar_BINARY_DIR})
endif()

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

Примеры

Рассмотрим иерархию проекта, где projA является верхним уровнем проекта и зависит от проектов projB и projC. Оба projB и projC могут быть построены автономно, и оба также зависят от другого проекта projD. Для простоты этот пример предполагает, что все четыре проекта доступны на сервере компании 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_GetProperties(projB)
if(NOT projb_POPULATED)
  FetchContent_Populate(projB)
  add_subdirectory(${projb_SOURCE_DIR} ${projb_BINARY_DIR})
endif()

FetchContent_GetProperties(projC)
if(NOT projc_POPULATED)
  FetchContent_Populate(projC)
  add_subdirectory(${projc_SOURCE_DIR} ${projc_BINARY_DIR})
endif()

projB:

include(FetchContent)
FetchContent_Declare(
  projD
  GIT_REPOSITORY git@mycompany.com/git/projD.git
  GIT_TAG        20b415f9034bbd2a2e8216e9a5c9e632
)

FetchContent_GetProperties(projD)
if(NOT projd_POPULATED)
  FetchContent_Populate(projD)
  add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR})
endif()

projC:

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

FetchContent_GetProperties(projD)
if(NOT projd_POPULATED)
  FetchContent_Populate(projD)
  add_subdirectory(${projd_SOURCE_DIR} ${projd_BINARY_DIR})
endif()

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

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

Следующий пример демонстрирует, как можно загрузить и распаковать архив firmware с использованием script mode CMake. Вызов FetchContent_Populate() указывает все данные о содержимом, и распакованное firmware будет помещено в каталог 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–2019 Kitware, Inc. and Contributors
Licensed under the BSD 3-clause License.
https://cmake.org/cmake/help/v3.12/module/FetchContent.html

Spec-Zone.ru

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