Spec-Zone.ru › CMake 3.13

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 tar с использованием 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.13/module/FetchContent.html

Spec-Zone.ru

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