summaryrefslogtreecommitdiff
path: root/Modules/FetchContent.cmake
diff options
context:
space:
mode:
authorCraig Scott <craig.scott@crascit.com>2022-05-18 23:29:21 +1000
committerCraig Scott <craig.scott@crascit.com>2022-05-25 08:46:18 +1000
commit2aa83fa15b01941f0267e20a1a4e29793651fefd (patch)
tree7cd1ba4d72b087094815716e53f8a90fee035205 /Modules/FetchContent.cmake
parent8a28368feb938f301604c24c0294e2a25749cc77 (diff)
downloadcmake-2aa83fa15b01941f0267e20a1a4e29793651fefd.tar.gz
Dependency providers: Add find_package and FetchContent support
Fixes: #22619
Diffstat (limited to 'Modules/FetchContent.cmake')
-rw-r--r--Modules/FetchContent.cmake213
1 files changed, 177 insertions, 36 deletions
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index f74454c660..e1cc8432bf 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -193,6 +193,11 @@ Commands
``OVERRIDE_FIND_PACKAGE`` cannot be used when ``FIND_PACKAGE_ARGS`` is
given.
+ :ref:`dependency_providers` discusses another way that
+ :command:`FetchContent_MakeAvailable` calls can be redirected.
+ ``FIND_PACKAGE_ARGS`` is intended for project control, whereas
+ dependency providers allow users to override project behavior.
+
``OVERRIDE_FIND_PACKAGE``
When a ``FetchContent_Declare(<name> ...)`` call includes this option,
subsequent calls to ``find_package(<name> ...)`` will ensure that
@@ -204,6 +209,13 @@ Commands
satisfy the package requirements of the latter. ``FIND_PACKAGE_ARGS``
cannot be used when ``OVERRIDE_FIND_PACKAGE`` is given.
+ If a :ref:`dependency provider <dependency_providers>` has been set
+ and the project calls :command:`find_package` for the ``<name>``
+ dependency, ``OVERRIDE_FIND_PACKAGE`` will not prevent the provider
+ from seeing that call. Dependency providers always have the opportunity
+ to intercept any direct call to :command:`find_package`, except if that
+ call contains the ``BYPASS_PROVIDER`` option.
+
.. command:: FetchContent_MakeAvailable
.. versionadded:: 3.14
@@ -217,17 +229,35 @@ Commands
:command:`FetchContent_Declare` for each dependency, and the first such call
will control how that dependency will be made available, as described below.
- .. versionadded:: 3.24
- If permitted, :command:`find_package(<name> [<args>...]) <find_package>`
- will be called, where ``<args>...`` may be provided by the
- ``FIND_PACKAGE_ARGS`` option in :command:`FetchContent_Declare`.
- The value of the :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` variable
- at the time :command:`FetchContent_Declare` was called determines whether
- ``FetchContent_MakeAvailable()`` can call :command:`find_package`.
+ If ``<lowercaseName>_SOURCE_DIR`` is not set:
+
+ * .. versionadded:: 3.24
+
+ If a :ref:`dependency provider <dependency_providers>` is set, call the
+ provider's command with ``FETCHCONTENT_MAKEAVAILABLE_SERIAL`` as the
+ first argument, followed by the arguments of the first call to
+ :command:`FetchContent_Declare` for ``<name>``. If ``SOURCE_DIR`` or
+ ``BINARY_DIR`` were not part of the original declared arguments, they
+ will be added with their default values.
+ If :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` was set to ``NEVER``
+ when the details were declared, any ``FIND_PACKAGE_ARGS`` will be
+ omitted. The ``OVERRIDE_FIND_PACKAGE`` keyword is also always omitted.
+ If the provider fulfilled the request, ``FetchContent_MakeAvailable()``
+ will consider that dependency handled, skip the remaining steps below
+ and move on to the next dependency in the list.
+
+ * .. versionadded:: 3.24
- If :command:`find_package` was unsuccessful or was not allowed to be called,
- ``FetchContent_MakeAvailable()`` then uses the following logic to make the
- dependency available:
+ If permitted, :command:`find_package(<name> [<args>...]) <find_package>`
+ will be called, where ``<args>...`` may be provided by the
+ ``FIND_PACKAGE_ARGS`` option in :command:`FetchContent_Declare`.
+ The value of the :variable:`FETCHCONTENT_TRY_FIND_PACKAGE_MODE` variable
+ at the time :command:`FetchContent_Declare` was called determines whether
+ ``FetchContent_MakeAvailable()`` can call :command:`find_package`.
+
+ If the dependency was not satisfied by a provider or a
+ :command:`find_package` call, ``FetchContent_MakeAvailable()`` then uses
+ the following logic to make the dependency available:
* If the dependency has already been populated earlier in this run, set
the ``<lowercaseName>_POPULATED``, ``<lowercaseName>_SOURCE_DIR`` and
@@ -468,7 +498,7 @@ Commands
When using saved content details, a call to
:command:`FetchContent_MakeAvailable` or :command:`FetchContent_Populate`
records information in global properties which can be queried at any time.
- This information includes the source and binary directories associated with
+ This information may include the source and binary directories associated with
the content and also whether or not the content population has been processed
during the current configure run.
@@ -488,6 +518,8 @@ Commands
set the same variables as a call to
:command:`FetchContent_MakeAvailable(name) <FetchContent_MakeAvailable>` or
:command:`FetchContent_Populate(name) <FetchContent_Populate>`.
+ Note that the ``SOURCE_DIR`` and ``BINARY_DIR`` values can be empty if the
+ call is fulfilled by a :ref:`dependency provider <dependency_providers>`.
This command is rarely needed when using
:command:`FetchContent_MakeAvailable`. It is more commonly used as part of
@@ -511,6 +543,33 @@ Commands
add_subdirectory(${depname_SOURCE_DIR} ${depname_BINARY_DIR})
endif()
+.. command:: FetchContent_SetPopulated
+
+ .. versionadded:: 3.24
+
+ .. note::
+ This command should only be called by
+ :ref:`dependency providers <dependency_providers>`. Calling it in any
+ other context is unsupported and future CMake versions may halt with a
+ fatal error in such cases.
+
+ .. code-block:: cmake
+
+ FetchContent_SetPopulated(
+ <name>
+ [SOURCE_DIR <srcDir>]
+ [BINARY_DIR <binDir>]
+ )
+
+ If a provider command fulfills a ``FETCHCONTENT_MAKEAVAILABLE_SERIAL``
+ request, it must call this function before returning. The ``SOURCE_DIR``
+ and ``BINARY_DIR`` arguments can be used to specify the values that
+ :command:`FetchContent_GetProperties` should return for its corresponding
+ arguments. Only provide ``SOURCE_DIR`` and ``BINARY_DIR`` if they have
+ the same meaning as if they had been populated by the built-in
+ :command:`FetchContent_MakeAvailable` implementation.
+
+
Variables
^^^^^^^^^
@@ -588,7 +647,7 @@ A number of cache variables can influence the behavior where details from a
behavior if ``FETCHCONTENT_TRY_FIND_PACKAGE_MODE`` is not set.
``ALWAYS``
- :command:`find_package` will be called by
+ :command:`find_package` can be called by
:command:`FetchContent_MakeAvailable` regardless of whether the
:command:`FetchContent_Declare` call included a ``FIND_PACKAGE_ARGS``
keyword or not. If no ``FIND_PACKAGE_ARGS`` keyword was given, the
@@ -1099,14 +1158,26 @@ function(FetchContent_Declare contentName)
endif()
set(options "")
- set(oneValueArgs SVN_REPOSITORY)
+ set(oneValueArgs
+ BINARY_DIR
+ SOURCE_DIR
+ SVN_REPOSITORY
+ )
set(multiValueArgs "")
cmake_parse_arguments(PARSE_ARGV 1 ARG
"${options}" "${oneValueArgs}" "${multiValueArgs}")
- unset(srcDirSuffix)
- unset(svnRepoArgs)
+ string(TOLOWER ${contentName} contentNameLower)
+
+ if(NOT ARG_BINARY_DIR)
+ set(ARG_BINARY_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build")
+ endif()
+
+ if(NOT ARG_SOURCE_DIR)
+ set(ARG_SOURCE_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src")
+ endif()
+
if(ARG_SVN_REPOSITORY)
# Add a hash of the svn repository URL to the source dir. This works
# around the problem where if the URL changes, the download would
@@ -1116,25 +1187,21 @@ function(FetchContent_Declare contentName)
# problem on windows due to path length limits).
string(SHA1 urlSHA ${ARG_SVN_REPOSITORY})
string(SUBSTRING ${urlSHA} 0 7 urlSHA)
- set(srcDirSuffix "-${urlSHA}")
- set(svnRepoArgs SVN_REPOSITORY ${ARG_SVN_REPOSITORY})
+ string(APPEND ARG_SOURCE_DIR "-${urlSHA}")
+ list(PREPEND ARG_UNPARSED_ARGUMENTS SVN_REPOSITORY "${ARG_SVN_REPOSITORY}")
endif()
- string(TOLOWER ${contentName} contentNameLower)
+ list(PREPEND ARG_UNPARSED_ARGUMENTS
+ SOURCE_DIR "${ARG_SOURCE_DIR}"
+ BINARY_DIR "${ARG_BINARY_DIR}"
+ )
set(__argsQuoted)
foreach(__item IN LISTS ARG_UNPARSED_ARGUMENTS)
string(APPEND __argsQuoted " [==[${__item}]==]")
endforeach()
- cmake_language(EVAL CODE "
- __FetchContent_declareDetails(
- ${contentNameLower}
- SOURCE_DIR \"${FETCHCONTENT_BASE_DIR}/${contentNameLower}-src${srcDirSuffix}\"
- BINARY_DIR \"${FETCHCONTENT_BASE_DIR}/${contentNameLower}-build\"
- \${svnRepoArgs}
- # List these last so they can override things we set above
- ${__argsQuoted}
- )"
+ cmake_language(EVAL CODE
+ "__FetchContent_declareDetails(${contentNameLower} ${__argsQuoted})"
)
endfunction()
@@ -1145,11 +1212,11 @@ endfunction()
# The setter also records the source and binary dirs used.
#=======================================================================
-# Internal use, projects must not call this directly. It is intended
-# for use by things like the FetchContent_Populate() function to
-# record when FetchContent_Populate() is called for a particular
-# content name.
-function(__FetchContent_setPopulated contentName)
+# Semi-internal use. Projects must not call this directly. Dependency
+# providers must call it if they satisfy a request made with the
+# FETCHCONTENT_MAKEAVAILABLE_SERIAL method (that is the only permitted
+# place to call it outside of the FetchContent module).
+function(FetchContent_SetPopulated contentName)
cmake_parse_arguments(PARSE_ARGV 1 arg
""
@@ -1488,7 +1555,8 @@ function(FetchContent_Populate contentName)
if(${contentNameLower}_POPULATED)
if("${${contentNameLower}_SOURCE_DIR}" STREQUAL "")
message(FATAL_ERROR
- "Content ${contentName} already populated by find_package()"
+ "Content ${contentName} already populated by find_package() or a "
+ "dependency provider"
)
else()
message(FATAL_ERROR
@@ -1592,7 +1660,7 @@ function(FetchContent_Populate contentName)
)
endif()
- __FetchContent_setPopulated(
+ FetchContent_SetPopulated(
${contentName}
SOURCE_DIR "${${contentNameLower}_SOURCE_DIR}"
BINARY_DIR "${${contentNameLower}_BINARY_DIR}"
@@ -1662,13 +1730,85 @@ endfunction()
# calls will be available to the caller.
macro(FetchContent_MakeAvailable)
+ get_property(__cmake_providerCommand GLOBAL PROPERTY
+ __FETCHCONTENT_MAKEAVAILABLE_SERIAL_PROVIDER
+ )
foreach(__cmake_contentName IN ITEMS ${ARGV})
string(TOLOWER ${__cmake_contentName} __cmake_contentNameLower)
# If user specified FETCHCONTENT_SOURCE_DIR_... for this dependency, that
- # overrides everything else and we shouldn't try to use find_package().
+ # overrides everything else and we shouldn't try to use find_package() or
+ # a dependency provider.
string(TOUPPER ${__cmake_contentName} __cmake_contentNameUpper)
if("${FETCHCONTENT_SOURCE_DIR_${__cmake_contentNameUpper}}" STREQUAL "")
+ # Dependency provider gets first opportunity, but prevent infinite
+ # recursion if we are called again for the same thing
+ if(NOT "${__cmake_providerCommand}" STREQUAL "" AND
+ NOT DEFINED __cmake_fcProvider_${__cmake_contentNameLower})
+ message(VERBOSE
+ "Trying FETCHCONTENT_MAKEAVAILABLE_SERIAL dependency provider for "
+ "${__cmake_contentName}"
+ )
+ # It's still valid if there are no saved details. The project may have
+ # been written to assume a dependency provider is always set and will
+ # provide dependencies without having any declared details for them.
+ __FetchContent_getSavedDetails(${__cmake_contentName} __cmake_contentDetails)
+ set(__cmake_providerArgs
+ "FETCHCONTENT_MAKEAVAILABLE_SERIAL"
+ "${__cmake_contentName}"
+ )
+ # Empty arguments must be preserved because of things like
+ # GIT_SUBMODULES (see CMP0097)
+ foreach(__cmake_item IN LISTS __cmake_contentDetails)
+ string(APPEND __cmake_providerArgs " [==[${__cmake_item}]==]")
+ endforeach()
+
+ # This property might be defined but empty. As long as it is defined,
+ # find_package() can be called.
+ get_property(__cmake_addfpargs GLOBAL PROPERTY
+ _FetchContent_${contentNameLower}_find_package_args
+ DEFINED
+ )
+ if(__cmake_addfpargs)
+ get_property(__cmake_fpargs GLOBAL PROPERTY
+ _FetchContent_${contentNameLower}_find_package_args
+ )
+ string(APPEND __cmake_providerArgs " FIND_PACKAGE_ARGS")
+ foreach(__cmake_item IN LISTS __cmake_fpargs)
+ string(APPEND __cmake_providerArgs " [==[${__cmake_item}]==]")
+ endforeach()
+ endif()
+
+ # Calling the provider could lead to FetchContent_MakeAvailable() being
+ # called for a nested dependency. That nested call may occur in the
+ # current variable scope. We have to save and restore the variables we
+ # need preserved.
+ list(APPEND __cmake_fcCurrentVarsStack
+ ${__cmake_contentName}
+ ${__cmake_contentNameLower}
+ )
+
+ set(__cmake_fcProvider_${__cmake_contentNameLower} YES)
+ cmake_language(EVAL CODE "${__cmake_providerCommand}(${__cmake_providerArgs})")
+ unset(__cmake_fcProvider_${__cmake_contentNameLower})
+
+ list(POP_BACK __cmake_fcCurrentVarsStack
+ __cmake_contentNameLower
+ __cmake_contentName
+ )
+
+ unset(__cmake_providerArgs)
+ unset(__cmake_addfpargs)
+ unset(__cmake_fpargs)
+ unset(__cmake_item)
+ unset(__cmake_contentDetails)
+
+ FetchContent_GetProperties(${__cmake_contentName})
+ if(${__cmake_contentNameLower}_POPULATED)
+ continue()
+ endif()
+ endif()
+
# Check if we've been asked to try find_package() first, even if we
# have already populated this dependency. If we previously tried to
# use find_package() for this and it succeeded, those things might
@@ -1698,7 +1838,7 @@ macro(FetchContent_MakeAvailable)
unset(__cmake_fpArgs)
if(${__cmake_contentName}_FOUND)
- __FetchContent_setPopulated(${__cmake_contentName})
+ FetchContent_SetPopulated(${__cmake_contentName})
FetchContent_GetProperties(${__cmake_contentName})
continue()
endif()
@@ -1745,5 +1885,6 @@ macro(FetchContent_MakeAvailable)
unset(__cmake_contentName)
unset(__cmake_contentNameLower)
unset(__cmake_contentNameUpper)
+ unset(__cmake_providerCommand)
endmacro()