Spec-Zone.ru › CMake 3.11

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 с помощью CMake’s script mode. Вызов 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.11/module/FetchContent.html

Spec-Zone.ru

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