Merge topic 'doc-macro'

67171a99be Help: Fix inaccurate use of "function" in macro docs

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Juan Ramos <juan.ramos@kitware.com>
Merge-request: !9534
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a3d959c..e2e0959 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -821,6 +821,16 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
+b:macos-arm64-curl:
+    extends:
+        - .macos_arm64_curl
+        - .cmake_build_macos
+        - .cmake_build_artifacts
+        - .macos_arm64_tags
+        - .run_manually
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 b:macos-arm64-pch:
     extends:
         - .macos_arm64_pch
@@ -874,6 +884,19 @@
         CMAKE_CI_JOB_NIGHTLY: "true"
         CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
 
+t:macos-arm64-curl:
+    extends:
+        - .macos_arm64_curl
+        - .cmake_test_macos
+        - .macos_arm64_tags
+        - .run_dependent
+    dependencies:
+        - b:macos-arm64-curl
+    needs:
+        - b:macos-arm64-curl
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 b:macos-x86_64-makefiles:
     extends:
         - .macos_x86_64_makefiles
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore
index 552c02c..0558700 100644
--- a/.gitlab/.gitignore
+++ b/.gitlab/.gitignore
@@ -10,6 +10,7 @@
 /mingw
 /msvc*
 /ninja*
+/nuget
 /openmp
 /open-watcom*
 /orangec
diff --git a/.gitlab/ci/configure_debian12_aarch64_ninja.cmake b/.gitlab/ci/configure_debian12_aarch64_ninja.cmake
index 24be975..0ebf604 100644
--- a/.gitlab/ci/configure_debian12_aarch64_ninja.cmake
+++ b/.gitlab/ci/configure_debian12_aarch64_ninja.cmake
@@ -98,6 +98,7 @@
 set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
 set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
 set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
 
diff --git a/.gitlab/ci/configure_debian12_ninja_common.cmake b/.gitlab/ci/configure_debian12_ninja_common.cmake
index 98ae771..0e2ecce 100644
--- a/.gitlab/ci/configure_debian12_ninja_common.cmake
+++ b/.gitlab/ci/configure_debian12_ninja_common.cmake
@@ -106,6 +106,7 @@
 set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
 set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
 
 if (NOT "$ENV{SWIFTC}" STREQUAL "")
diff --git a/.gitlab/ci/configure_fedora40_makefiles.cmake b/.gitlab/ci/configure_fedora40_makefiles.cmake
index 478ba11..21a5be5 100644
--- a/.gitlab/ci/configure_fedora40_makefiles.cmake
+++ b/.gitlab/ci/configure_fedora40_makefiles.cmake
@@ -109,6 +109,7 @@
 endif()
 set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
 set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
 
diff --git a/.gitlab/ci/configure_fedora40_ninja.cmake b/.gitlab/ci/configure_fedora40_ninja.cmake
index b16e928..ae968a3 100644
--- a/.gitlab/ci/configure_fedora40_ninja.cmake
+++ b/.gitlab/ci/configure_fedora40_ninja.cmake
@@ -4,6 +4,7 @@
 endif()
 set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "")
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
 
 # "Release" flags without "-DNDEBUG" so we get assertions.
diff --git a/.gitlab/ci/configure_macos_arm64_curl.cmake b/.gitlab/ci/configure_macos_arm64_curl.cmake
new file mode 100644
index 0000000..af49873
--- /dev/null
+++ b/.gitlab/ci/configure_macos_arm64_curl.cmake
@@ -0,0 +1,12 @@
+# Build with our vendored curl instead of the default system version.
+set(CMAKE_USE_SYSTEM_CURL "OFF" CACHE BOOL "")
+
+set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
+
+# Test that our vendored curl accepts CURL_SSLVERSION_TLSv1_3.  It is passed
+# through to Secure Transport, but macOS does not actually enforce it.
+set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_ninja.cmake b/.gitlab/ci/configure_macos_arm64_ninja.cmake
index 672f5d4..de0ffc0 100644
--- a/.gitlab/ci/configure_macos_arm64_ninja.cmake
+++ b/.gitlab/ci/configure_macos_arm64_ninja.cmake
@@ -7,7 +7,8 @@
 set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
 set(CMake_TEST_GUI "ON" CACHE BOOL "")
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
-set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
index f0bf1c0..43505db 100644
--- a/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
+++ b/.gitlab/ci/configure_macos_x86_64_makefiles.cmake
@@ -7,7 +7,8 @@
   set(CMake_TEST_ISPC "ON" CACHE STRING "")
 endif()
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
-set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_x86_64_ninja.cmake b/.gitlab/ci/configure_macos_x86_64_ninja.cmake
index 8fdaba8..83d1e2c 100644
--- a/.gitlab/ci/configure_macos_x86_64_ninja.cmake
+++ b/.gitlab/ci/configure_macos_x86_64_ninja.cmake
@@ -10,7 +10,8 @@
   set(CMake_TEST_ISPC "ON" CACHE STRING "")
 endif()
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
-set(CMake_TEST_TLS_VERSION "1.3" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake b/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake
index cd9780b..64a8913 100644
--- a/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake
+++ b/.gitlab/ci/configure_windows_arm64_vs2022_ninja.cmake
@@ -4,6 +4,7 @@
 set(CMAKE_PREFIX_PATH "" CACHE STRING "")
 
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_msvc_cxx_modules_common.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
index e449d80..d2064e6 100644
--- a/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
+++ b/.gitlab/ci/configure_windows_vs2022_x64_ninja.cmake
@@ -3,11 +3,13 @@
 
 if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
   set(CMake_TEST_CPACK_INNOSETUP "ON" CACHE STRING "")
+  set(CMake_TEST_CPACK_NUGET "ON" CACHE STRING "")
   set(CMake_TEST_ISPC "ON" CACHE STRING "")
   set(CMake_TEST_Swift "ON" CACHE STRING "")
 endif()
 
 set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_TLS_VERIFY_URL_BAD "https://badtls-expired.kitware.com" CACHE STRING "")
 set(CMake_TEST_TLS_VERSION "1.2" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_msvc_cxx_modules_common.cmake")
diff --git a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1 b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
index ae4a058..fe010eb 100644
--- a/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
+++ b/.gitlab/ci/env_windows_vs2022_x64_ninja.ps1
@@ -1,6 +1,7 @@
 if ("$env:CMAKE_CI_NIGHTLY" -eq "true") {
   . ".gitlab/ci/innosetup-env.ps1"
   . ".gitlab/ci/ispc-env.ps1"
+  . ".gitlab/ci/nuget-env.ps1"
   . ".gitlab/ci/swift-env.ps1"
 }
 
diff --git a/.gitlab/ci/nuget-env.ps1 b/.gitlab/ci/nuget-env.ps1
new file mode 100644
index 0000000..7dee5a0
--- /dev/null
+++ b/.gitlab/ci/nuget-env.ps1
@@ -0,0 +1,4 @@
+$pwdpath = $pwd.Path
+& "$pwsh" -File ".gitlab/ci/nuget.ps1"
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\nuget;$env:PATH"
+nuget | Select -First 1
diff --git a/.gitlab/ci/nuget.ps1 b/.gitlab/ci/nuget.ps1
new file mode 100644
index 0000000..1decb01
--- /dev/null
+++ b/.gitlab/ci/nuget.ps1
@@ -0,0 +1,21 @@
+$erroractionpreference = "stop"
+
+$version = "6.9.1.3"
+$sha256sum = "562A2CE2D570D68DB4472CB82CDF1FC4245D5C73B84BC8361880CBE389702F65"
+$filename = "nuget-$version-win-i386-1"
+$tarball = "$filename.zip"
+
+$outdir = $pwd.Path
+$outdir = "$outdir\.gitlab"
+$ProgressPreference = 'SilentlyContinue'
+# This URL is only visible inside of Kitware's network.
+Invoke-WebRequest -Uri "https://cmake.org/files/dependencies/internal/$tarball" -OutFile "$outdir\$tarball"
+$hash = Get-FileHash "$outdir\$tarball" -Algorithm SHA256
+if ($hash.Hash -ne $sha256sum) {
+    exit 1
+}
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$tarball", "$outdir")
+Move-Item -Path "$outdir\$filename" -Destination "$outdir\nuget"
+Remove-Item "$outdir\$tarball"
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 894d624..2b265ea 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -47,6 +47,13 @@
         CMAKE_CI_IN_SYMLINK_TREE: 1
         CMAKE_CI_BUILD_DIR: "real_work/work/build"
 
+.macos_arm64_curl:
+    extends: .macos_build
+
+    variables:
+        CMAKE_CONFIGURATION: macos_arm64_curl
+        CTEST_NO_WARNINGS_ALLOWED: 1
+
 .macos_arm64_pch:
     extends: .macos_arm64_ninja
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f661a9..081bd7d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -178,6 +178,9 @@
       string(TOLOWER "${util}" lutil)
       set(CMAKE_USE_SYSTEM_${util} "${CMAKE_USE_SYSTEM_LIBRARY_${util}}"
         CACHE BOOL "Use system-installed ${lutil}" FORCE)
+    elseif(util STREQUAL "CURL" AND APPLE)
+      # macOS provides a curl with backends configured by Apple.
+      set(CMAKE_USE_SYSTEM_LIBRARY_${util} ON)
     else()
       set(CMAKE_USE_SYSTEM_LIBRARY_${util} OFF)
     endif()
@@ -215,6 +218,10 @@
   mark_as_advanced(CMAKE_USE_SYSTEM_KWIML)
 
   # Mention to the user what system libraries are being used.
+  if(CMAKE_USE_SYSTEM_CURL)
+    # Avoid messaging about curl-only dependencies.
+    list(REMOVE_ITEM UTILITIES NGHTTP2)
+  endif()
   foreach(util IN LISTS UTILITIES ITEMS KWIML)
     if(CMAKE_USE_SYSTEM_${util})
       message(STATUS "Using system-installed ${util}")
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index c184a96..77357c0 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -269,6 +269,8 @@
   source tree is mentioned as an absolute source file path elsewhere
   in the current directory.
 
+  The output file path may not contain ``<`` or ``>`` characters.
+
   .. versionadded:: 3.20
     Arguments to ``OUTPUT`` may use a restricted set of
     :manual:`generator expressions <cmake-generator-expressions(7)>`.
@@ -280,6 +282,10 @@
     considered private unless they are listed in a non-private file set.
     See policy :policy:`CMP0154`.
 
+  .. versionchanged:: 3.30
+    The output file path may now use ``#`` characters, except
+    when using the :generator:`Borland Makefiles` generator.
+
 ``USES_TERMINAL``
   .. versionadded:: 3.2
 
diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst
index 5021851..9ee1d01 100644
--- a/Help/command/try_compile.rst
+++ b/Help/command/try_compile.rst
@@ -156,8 +156,9 @@
   Specify flags of the form :option:`-DVAR:TYPE=VALUE <cmake -D>` to be passed
   to the :manual:`cmake(1)` command-line used to drive the test build.
   The above example shows how values for variables
-  ``INCLUDE_DIRECTORIES``, ``LINK_DIRECTORIES``, and ``LINK_LIBRARIES``
-  are used.
+  ``COMPILE_DEFINITIONS``, ``INCLUDE_DIRECTORIES``, ``LINK_DIRECTORIES``,
+  ``LINK_LIBRARIES``, and ``LINK_OPTIONS`` are used. Compiler options
+  can be passed in like `CMAKE_FLAGS -DCOMPILE_DEFINITIONS=-Werror`.
 
 ``COMPILE_DEFINITIONS <defs>...``
   Specify ``-Ddefinition`` arguments to pass to :command:`add_definitions`
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 93298c5..35ea34f 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -50,7 +50,8 @@
 
 This UUID may change in future versions of CMake.  Be sure to use the value
 documented here by the source tree of the version of CMake with which you are
-experimenting.
+experimenting.  It must be set before the ``CXX`` toolchain is discovered by
+CMake, usually as part of a :command:`project` call.
 
 When activated, this experimental feature provides the following:
 
diff --git a/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst b/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst
index 77ead4d..82bd007 100644
--- a/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst
+++ b/Help/envvar/CMAKE_MSVCIDE_RUN_PATH.rst
@@ -4,7 +4,7 @@
 .. include:: ENV_VAR.txt
 
 Extra PATH locations for custom commands when using
-:generator:`Visual Studio 9 2008` (or above) generators.
+:generator:`Visual Studio 12 2013` (or above) generators.
 
 The ``CMAKE_MSVCIDE_RUN_PATH`` environment variable sets the default value for
 the :variable:`CMAKE_MSVCIDE_RUN_PATH` variable if not already explicitly set.
diff --git a/Help/generator/Visual Studio 9 2008.rst b/Help/generator/Visual Studio 9 2008.rst
index 1439270..01d5af2 100644
--- a/Help/generator/Visual Studio 9 2008.rst
+++ b/Help/generator/Visual Studio 9 2008.rst
@@ -1,41 +1,9 @@
 Visual Studio 9 2008
 --------------------
 
-Deprecated.  Generates Visual Studio 9 2008 project files.
-
-.. note::
-  This generator is deprecated and will be removed in a future version
-  of CMake.  It will still be possible to build with VS 9 2008 tools
-  using the :generator:`Visual Studio 14 2015` generator (or above,
-  and with VS 10 2010 also installed) with
-  :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v90``,
-  or by using the :generator:`NMake Makefiles` generator.
-
-Platform Selection
-^^^^^^^^^^^^^^^^^^
-
-The default target platform name (architecture) is ``Win32``.
-
-.. versionadded:: 3.1
-  The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :option:`cmake -A` option, to specify a target platform
-  name (architecture).  For example:
-
-  * ``cmake -G "Visual Studio 9 2008" -A Win32``
-  * ``cmake -G "Visual Studio 9 2008" -A x64``
-  * ``cmake -G "Visual Studio 9 2008" -A Itanium``
-  * ``cmake -G "Visual Studio 9 2008" -A <WinCE-SDK>``
-    (Specify a target platform matching a Windows CE SDK name.)
-
-For compatibility with CMake versions prior to 3.1, one may specify
-a target platform name optionally at the end of the generator name.
-This is supported only for:
-
-``Visual Studio 9 2008 Win64``
-  Specify target platform ``x64``.
-
-``Visual Studio 9 2008 IA64``
-  Specify target platform ``Itanium``.
-
-``Visual Studio 9 2008 <WinCE-SDK>``
-  Specify target platform matching a Windows CE SDK name.
+Removed.  This once generated Visual Studio 9 2008 project files, but
+the generator has been removed since CMake 3.30.  It is still possible
+to build with VS 9 2008 tools using the :generator:`Visual Studio 14 2015`
+generator (or above, and with VS 10 2010 also installed) with
+:variable:`CMAKE_GENERATOR_TOOLSET` set to ``v90``, or by using
+the :generator:`NMake Makefiles` generator.
diff --git a/Help/guide/tutorial/Adding Export Configuration.rst b/Help/guide/tutorial/Adding Export Configuration.rst
index 6c83276..c4ab476 100644
--- a/Help/guide/tutorial/Adding Export Configuration.rst
+++ b/Help/guide/tutorial/Adding Export Configuration.rst
@@ -46,7 +46,7 @@
 
   which is prefixed in the source directory.
 
-What CMake is trying to say is that during generating the export information
+CMake is telling you that during the generation of the export information
 it will export a path that is intrinsically tied to the current machine and
 will not be valid on other machines. The solution to this is to update the
 ``MathFunctions`` :command:`target_include_directories` to understand that it
diff --git a/Help/guide/tutorial/Complete/CMakeLists.txt b/Help/guide/tutorial/Complete/CMakeLists.txt
index 3cdaaae..548a82d 100644
--- a/Help/guide/tutorial/Complete/CMakeLists.txt
+++ b/Help/guide/tutorial/Complete/CMakeLists.txt
@@ -89,6 +89,7 @@
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
 set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+set(CPACK_GENERATOR "TGZ")
 set(CPACK_SOURCE_GENERATOR "TGZ")
 include(CPack)
 
diff --git a/Help/guide/tutorial/Packaging an Installer.rst b/Help/guide/tutorial/Packaging an Installer.rst
index 11a1952..4cca679 100644
--- a/Help/guide/tutorial/Packaging an Installer.rst
+++ b/Help/guide/tutorial/Packaging an Installer.rst
@@ -23,8 +23,9 @@
 some CPack variables to where we have stored the license and version
 information for this project. The version information was set earlier in this
 tutorial and the ``License.txt`` has been included in the top-level source
-directory for this step.  The :variable:`CPACK_SOURCE_GENERATOR` variable
-selects a file format for the source package.
+directory for this step.  The :variable:`CPACK_GENERATOR` and
+:variable:`CPACK_SOURCE_GENERATOR` variables select the generators used for
+binary and source installations, respectively.
 
 Finally we include the :module:`CPack module <CPack>` which will use these
 variables and some other properties of the current system to setup an
@@ -38,8 +39,9 @@
 
   cpack
 
-To specify the generator, use the :option:`-G <cpack -G>` option. For multi-config builds,
-use :option:`-C <cpack -C>` to specify the configuration. For example:
+To specify the binary generator, use the :option:`-G <cpack -G>` option. For
+multi-config builds, use :option:`-C <cpack -C>` to specify the configuration.
+For example:
 
 .. code-block:: console
 
diff --git a/Help/guide/tutorial/Step10/CMakeLists.txt b/Help/guide/tutorial/Step10/CMakeLists.txt
index 2dd6db5..40fee8d 100644
--- a/Help/guide/tutorial/Step10/CMakeLists.txt
+++ b/Help/guide/tutorial/Step10/CMakeLists.txt
@@ -72,5 +72,6 @@
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
 set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+set(CPACK_GENERATOR "TGZ")
 set(CPACK_SOURCE_GENERATOR "TGZ")
 include(CPack)
diff --git a/Help/guide/tutorial/Step11/CMakeLists.txt b/Help/guide/tutorial/Step11/CMakeLists.txt
index 046bfc9..9214c88 100644
--- a/Help/guide/tutorial/Step11/CMakeLists.txt
+++ b/Help/guide/tutorial/Step11/CMakeLists.txt
@@ -80,5 +80,6 @@
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
 set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+set(CPACK_GENERATOR "TGZ")
 set(CPACK_SOURCE_GENERATOR "TGZ")
 include(CPack)
diff --git a/Help/guide/tutorial/Step12/CMakeLists.txt b/Help/guide/tutorial/Step12/CMakeLists.txt
index 1ba4e31..a84590f 100644
--- a/Help/guide/tutorial/Step12/CMakeLists.txt
+++ b/Help/guide/tutorial/Step12/CMakeLists.txt
@@ -85,6 +85,8 @@
 set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
 set(CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
 set(CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
+set(CPACK_GENERATOR "TGZ")
+set(CPACK_SOURCE_GENERATOR "TGZ")
 include(CPack)
 
 # install the configuration targets
diff --git a/Help/manual/cmake-buildsystem.7.rst b/Help/manual/cmake-buildsystem.7.rst
index acb1ed7..93d55c7 100644
--- a/Help/manual/cmake-buildsystem.7.rst
+++ b/Help/manual/cmake-buildsystem.7.rst
@@ -314,6 +314,9 @@
 Target Compile Properties
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
+These represent the `build specification <Target Build Specification_>`_
+for compiling a target.
+
 :prop_tgt:`COMPILE_DEFINITIONS`
   List of compile definitions for compiling sources in the target.
   These are passed to the compiler with ``-D`` flags, or equivalent,
@@ -378,6 +381,9 @@
 Target Link Properties
 ^^^^^^^^^^^^^^^^^^^^^^
 
+These represent the `build specification <Target Build Specification_>`_
+for linking a target.
+
 :prop_tgt:`LINK_LIBRARIES`
   List of link libraries for linking the target, if it is an executable,
   shared library, or module library.  Entries for `Normal Libraries`_ are
@@ -510,6 +516,9 @@
 Transitive Compile Properties
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+These represent `usage requirements <Target Usage Requirements_>`_ for
+compiling consumers.
+
 :prop_tgt:`INTERFACE_COMPILE_DEFINITIONS`
   List of compile definitions for compiling sources in the target's consumers.
   Typically these are used by the target's header files.
@@ -561,6 +570,9 @@
 Transitive Link Properties
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+These represent `usage requirements <Target Usage Requirements_>`_ for
+linking consumers.
+
 :prop_tgt:`INTERFACE_LINK_LIBRARIES`
   List of link libraries for linking the target's consumers, for
   those that are executables, shared libraries, or module libraries.
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 1db237c..49d94ef 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -1751,7 +1751,8 @@
 
 .. genex:: $<TARGET_PROPERTY:tgt,prop>
 
-  Value of the property ``prop`` on the target ``tgt``.
+  Value of the property ``prop`` on the target ``tgt``, or empty if
+  the property is not set.
 
   Note that ``tgt`` is not added as a dependency of the target this
   expression is evaluated on.
@@ -1767,9 +1768,10 @@
   :target: TARGET_PROPERTY:prop
 
   Value of the property ``prop`` on the target for which the expression
-  is being evaluated.  Note that for generator expressions in
-  :ref:`Target Usage Requirements` this is the consuming target rather
-  than the target specifying the requirement.
+  is being evaluated, or empty if the property is not set.
+  Note that for generator expressions in :ref:`Target Usage Requirements`
+  this is the consuming target rather than the target specifying the
+  requirement.
 
 The expressions have special evaluation rules for some properties:
 
@@ -1777,26 +1779,32 @@
   :ref:`semicolon-separated list <CMake Language Lists>` representing the union
   of the value on the target itself with the values of the corresponding
   :ref:`Target Usage Requirements` on targets named by the target's
-  :prop_tgt:`LINK_LIBRARIES`.  Evaluation of the usage requirements is
-  transitive over the closure of the linked targets'
-  :prop_tgt:`INTERFACE_LINK_LIBRARIES`.
+  :prop_tgt:`LINK_LIBRARIES`:
+
+  * For :ref:`Target Compile Properties`, evaluation of corresponding usage
+    requirements is transitive over the closure of the linked targets'
+    :prop_tgt:`INTERFACE_LINK_LIBRARIES` *excluding* entries guarded by the
+    :genex:`LINK_ONLY` generator expression.
+
+  * For :ref:`Target Link Properties`, evaluation of corresponding usage
+    requirements is transitive over the closure of the linked targets'
+    :prop_tgt:`INTERFACE_LINK_LIBRARIES` *including* entries guarded by the
+    :genex:`LINK_ONLY` generator expression.  See policy :policy:`CMP0166`.
 
   Evaluation of :prop_tgt:`LINK_LIBRARIES` itself is not transitive.
 
 * :ref:`Target Usage Requirements` evaluate as a
   :ref:`semicolon-separated list <CMake Language Lists>` representing the union
   of the value on the target itself with the values of the same properties on
-  targets named by the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`.
-  Evaluation is transitive over the closure of the target's
-  :prop_tgt:`INTERFACE_LINK_LIBRARIES`:
+  targets named by the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`:
 
-  * For :ref:`Transitive Compile Properties`, the transitive closure
-    *excludes* entries of :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
-    by the :genex:`LINK_ONLY` generator expression.
+  * For :ref:`Transitive Compile Properties`, evaluation is transitive over
+    the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+    *excluding* entries guarded by the :genex:`LINK_ONLY` generator expression.
 
-  * For :ref:`Transitive Link Properties`, the transitive closure is
-    *includes* entries of :prop_tgt:`INTERFACE_LINK_LIBRARIES` guarded
-    by the :genex:`LINK_ONLY` generator expression.
+  * For :ref:`Transitive Link Properties`, evaluation is transitive over
+    the closure of the target's :prop_tgt:`INTERFACE_LINK_LIBRARIES`
+    *including* entries guarded by the :genex:`LINK_ONLY` generator expression.
     See policy :policy:`CMP0166`.
 
   Evaluation of :prop_tgt:`INTERFACE_LINK_LIBRARIES` itself is not transitive.
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 43d54dc..72aedba 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@
 .. toctree::
    :maxdepth: 1
 
+   CMP0168: FetchContent implements steps directly instead of through a sub-build. </policy/CMP0168>
    CMP0167: The FindBoost module is removed. </policy/CMP0167>
    CMP0166: TARGET_PROPERTY evaluates link properties transitively over private dependencies of static libraries. </policy/CMP0166>
    CMP0165: enable_language() must not be called before project(). </policy/CMP0165>
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 8f98f8b..b71e3d9 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -467,6 +467,7 @@
    /variable/CMAKE_LANG_INCLUDE_WHAT_YOU_USE
    /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LANG_LINK_GROUP_USING_FEATURE_SUPPORTED
+   /variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES
    /variable/CMAKE_LANG_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_FLAG
    /variable/CMAKE_LANG_LINK_LIBRARY_USING_FEATURE
@@ -485,6 +486,7 @@
    /variable/CMAKE_LINK_GROUP_USING_FEATURE
    /variable/CMAKE_LINK_GROUP_USING_FEATURE_SUPPORTED
    /variable/CMAKE_LINK_INTERFACE_LIBRARIES
+   /variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES
    /variable/CMAKE_LINK_LIBRARY_FILE_FLAG
    /variable/CMAKE_LINK_LIBRARY_FLAG
    /variable/CMAKE_LINK_LIBRARY_USING_FEATURE
diff --git a/Help/policy/CMP0097.rst b/Help/policy/CMP0097.rst
index 66a7b96..d9141f5 100644
--- a/Help/policy/CMP0097.rst
+++ b/Help/policy/CMP0097.rst
@@ -4,9 +4,7 @@
 .. versionadded:: 3.16
 
 :command:`ExternalProject_Add` with ``GIT_SUBMODULES ""`` initializes no
-submodules.  The policy also applies to :command:`FetchContent_Declare`,
-which uses the same download and update features as
-:command:`ExternalProject_Add`.
+submodules.
 
 The commands provide a ``GIT_SUBMODULES`` option which controls what submodules
 to initialize and update. Starting with CMake 3.16, explicitly setting
@@ -25,4 +23,15 @@
 .. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
 .. include:: STANDARD_ADVICE.txt
 
+.. note::
+
+  This policy also applies to :command:`FetchContent_Declare`,
+  which uses the same download and update features as
+  :command:`ExternalProject_Add`.  However, due to an implementation deficiency
+  present since the policy was first introduced, CMake 3.16 and later always
+  uses the ``NEW`` behavior for :command:`FetchContent_Declare`, regardless of
+  the policy setting. Formally, this forcing of ``NEW`` behavior for
+  :command:`FetchContent_Declare` will continue to apply in future CMake
+  releases.
+
 .. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0168.rst b/Help/policy/CMP0168.rst
new file mode 100644
index 0000000..8317351
--- /dev/null
+++ b/Help/policy/CMP0168.rst
@@ -0,0 +1,64 @@
+CMP0168
+-------
+
+.. versionadded:: 3.30
+
+The :module:`FetchContent` module implements steps directly instead of through
+a sub-build.
+
+CMake 3.29 and below implement FetchContent as a separate sub-build.
+This required configuring that separate project and using a build tool.
+This approach can be very slow with some generators and operating systems.
+CMake 3.30 and above prefer to implement the download, update, and patch
+steps directly as part of the main project.
+
+The ``NEW`` behavior has the following characteristics:
+
+* No sub-build is used. All operations are implemented directly from the
+  main project's CMake configure step. When running in CMake script mode,
+  no build tool needs to be available.
+* Generator expressions and GNU Make variables of the form ``$(SOMEVAR)`` are
+  not supported. They should not be used in any argument to
+  :command:`FetchContent_Declare` or :command:`FetchContent_Populate`.
+* All ``LOG_...`` and ``USES_TERMINAL_...`` options, the ``QUIET`` option, and
+  the :variable:`FETCHCONTENT_QUIET` variable are ignored.
+  :module:`FetchContent` output is always part of the main project's configure
+  output. This also means it now respects the message logging level (see
+  :variable:`CMAKE_MESSAGE_LOG_LEVEL` and
+  :option:`--log-level <cmake --log-level>`). The default message log level
+  should be comparable to using ``QUIET`` with the ``OLD`` policy setting,
+  except that warnings will now be shown.
+* The ``PREFIX``, ``TMP_DIR``, ``STAMP_DIR``, ``LOG_DIR``, and ``DOWNLOAD_DIR``
+  options and their associated directory properties are ignored. The
+  :module:`FetchContent` module controls those locations internally.
+
+The ``OLD`` behavior has the following characteristics:
+
+* A sub-build is always used to implement the download, update, and patch
+  steps. A build tool must be available, even when using
+  :command:`FetchContent_Populate` in CMake script mode.
+* Generator expressions and GNU Make variables of the form ``$(SOMEVAR)`` can
+  be used, although such use is almost always inappropriate. They are evaluated
+  in the sub-build, so they do not see any information from the main build.
+* All logging, terminal control, and directory options related to the download,
+  update, or patch steps are supported.
+* If the ``QUIET`` option is used, or the :variable:`FETCHCONTENT_QUIET`
+  variable is set to true, warnings will not be shown in the output.
+
+There's a reasonably good chance that users can set the
+:variable:`CMAKE_POLICY_DEFAULT_CMP0168 <CMAKE_POLICY_DEFAULT_CMP<NNNN>>`
+variable to ``NEW`` to globally switch to the ``NEW`` behavior while waiting
+for the project and its dependencies to be updated use the ``NEW`` policy
+setting by default. Projects don't typically make use of the features that the
+``NEW`` behavior no longer supports, and even those projects that do will often
+still work fine when those options are ignored. Before setting this behavior
+globally, check whether any :command:`FetchContent_Declare` or
+:command:`FetchContent_Populate` calls use the ignored options in a way that
+would change observable behavior, other than putting temporary or
+internally-generated files in different locations.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 3.30
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst b/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst
index b65db99..c36306d 100644
--- a/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst
+++ b/Help/prop_dir/VS_GLOBAL_SECTION_POST_section.rst
@@ -17,7 +17,7 @@
 solution global section.  Whitespace around key and value is ignored.
 List elements which do not contain an equal sign are skipped.
 
-This property only works for Visual Studio 9 and above; it is ignored
+This property only works for Visual Studio 12 and above; it is ignored
 on other generators.  The property only applies when set on a
 directory whose ``CMakeLists.txt`` contains a :command:`project` command.
 
diff --git a/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst b/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst
index 7f8bf61..c775ad5 100644
--- a/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst
+++ b/Help/prop_dir/VS_GLOBAL_SECTION_PRE_section.rst
@@ -17,6 +17,6 @@
 solution global section.  Whitespace around key and value is ignored.
 List elements which do not contain an equal sign are skipped.
 
-This property only works for Visual Studio 9 and above; it is ignored
+This property only works for Visual Studio 12 and above; it is ignored
 on other generators.  The property only applies when set on a
 directory whose ``CMakeLists.txt`` contains a :command:`project` command.
diff --git a/Help/prop_sf/COMPILE_FLAGS.rst b/Help/prop_sf/COMPILE_FLAGS.rst
index eefe7bf..1d3def4 100644
--- a/Help/prop_sf/COMPILE_FLAGS.rst
+++ b/Help/prop_sf/COMPILE_FLAGS.rst
@@ -5,8 +5,7 @@
 
 The ``COMPILE_FLAGS`` property, managed as a string, sets additional compiler
 flags used that will be added to the list of compile flags when this source
-file builds.  The flags will be added after target-wide flags (except in
-some cases not supported by the :generator:`Visual Studio 9 2008` generator).
+file builds.  The flags will be added after target-wide flags.
 
 Use :prop_sf:`COMPILE_DEFINITIONS` to pass additional preprocessor definitions.
 
diff --git a/Help/prop_sf/COMPILE_OPTIONS.rst b/Help/prop_sf/COMPILE_OPTIONS.rst
index 84c543a..9b00e0e 100644
--- a/Help/prop_sf/COMPILE_OPTIONS.rst
+++ b/Help/prop_sf/COMPILE_OPTIONS.rst
@@ -7,8 +7,7 @@
 
 This property holds a :ref:`semicolon-separated list <CMake Language Lists>`
 of options and will be added to the list of compile flags when this source
-file builds.  The options will be added after target-wide options (except in
-some cases not supported by the :generator:`Visual Studio 9 2008` generator).
+file builds.  The options will be added after target-wide options.
 
 Contents of ``COMPILE_OPTIONS`` may use "generator expressions" with the
 syntax ``$<...>``.  See the :manual:`cmake-generator-expressions(7)` manual
diff --git a/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst b/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst
index f11fe7c..afbee83 100644
--- a/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst
+++ b/Help/prop_tgt/DEPLOYMENT_ADDITIONAL_FILES.rst
@@ -4,7 +4,7 @@
 .. versionadded:: 3.13
 
 Set the WinCE project ``AdditionalFiles`` in ``DeploymentTool`` in ``.vcproj``
-files generated by the :generator:`Visual Studio 9 2008` generator.
+files generated by the :ref:`Visual Studio Generators`.
 This is useful when you want to debug on remote WinCE device.
 Specify additional files that will be copied to the device.
 For example:
diff --git a/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst b/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst
index 0680238..3f691b1 100644
--- a/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst
+++ b/Help/prop_tgt/DEPLOYMENT_REMOTE_DIRECTORY.rst
@@ -5,7 +5,7 @@
 
 Set the WinCE project ``RemoteDirectory`` in ``DeploymentTool`` and
 ``RemoteExecutable`` in ``DebuggerTool`` in ``.vcproj`` files generated
-by the :generator:`Visual Studio 9 2008` generator.
+by the :ref:`Visual Studio Generators`.
 This is useful when you want to debug on remote WinCE device.
 For example:
 
diff --git a/Help/prop_tgt/LINKER_TYPE.rst b/Help/prop_tgt/LINKER_TYPE.rst
index 0a016fe..23e1e4c 100644
--- a/Help/prop_tgt/LINKER_TYPE.rst
+++ b/Help/prop_tgt/LINKER_TYPE.rst
@@ -8,8 +8,7 @@
 
 .. include:: ../variable/LINKER_PREDEFINED_TYPES.txt
 
-This property is not supported on :generator:`Green Hills MULTI` and
-:generator:`Visual Studio 9 2008` generators.
+This property is not supported on :generator:`Green Hills MULTI` generator.
 
 The implementation details for the selected linker will be provided by the
 :variable:`CMAKE_<LANG>_USING_LINKER_<TYPE>` variable. For example:
diff --git a/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst b/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst
index ce49316..072475f 100644
--- a/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst
+++ b/Help/prop_tgt/VS_GLOBAL_KEYWORD.rst
@@ -1,12 +1,9 @@
 VS_GLOBAL_KEYWORD
 -----------------
 
-Visual Studio project keyword for VS 10 (2010) and newer.
+Visual Studio project keyword.
 
 Sets the "keyword" attribute for a generated Visual Studio project.
 Defaults to "Win32Proj".  You may wish to override this value with
 "ManagedCProj", for example, in a Visual Studio managed C++ unit test
 project.
-
-Use the :prop_tgt:`VS_KEYWORD` target property to set the
-keyword for Visual Studio 9 (2008) and older.
diff --git a/Help/prop_tgt/VS_KEYWORD.rst b/Help/prop_tgt/VS_KEYWORD.rst
index f04d109..b2ce78a 100644
--- a/Help/prop_tgt/VS_KEYWORD.rst
+++ b/Help/prop_tgt/VS_KEYWORD.rst
@@ -1,10 +1,9 @@
 VS_KEYWORD
 ----------
 
-Visual Studio project keyword for VS 9 (2008) and older.
-
-Can be set to change the visual studio keyword, for example Qt
-integration works better if this is set to Qt4VSv1.0.
+Removed.  This once specified the Visual Studio project keyword
+for the :generator:`Visual Studio 9 2008` generator, and older,
+but all of those generators have been removed.
 
 Use the :prop_tgt:`VS_GLOBAL_KEYWORD` target property to set the
 keyword for Visual Studio 12 (2013) and newer.
diff --git a/Help/release/dev/FindPython-DEBUG.rst b/Help/release/dev/FindPython-DEBUG.rst
new file mode 100644
index 0000000..2e75e72
--- /dev/null
+++ b/Help/release/dev/FindPython-DEBUG.rst
@@ -0,0 +1,20 @@
+FindPython-DEBUG
+----------------
+
+* The :module:`FindPython`, :module:`FindPython2` and :module:`FindPython3`
+  modules offer, on ``Windows`` platform, a better support of the ``Python``
+  debug version:
+
+  * new variables:
+
+    * ``Python_EXECUTABLE_DEBUG``
+    * ``Python_INTERPRETER``
+    * ``Python_DEBUG_POSTFIX``
+
+  * new targets:
+
+    * ``Python::InterpreterDebug``
+    * ``Python::InterpreterMultiConfig``
+
+  And the ``python_add_library()`` command manage the :prop_tgt:`DEBUG_POSTFIX`
+  target property based on the value of the ``Python_DEBUG_POSTFIX`` variable.
diff --git a/Help/release/dev/GenEx-LINK_LIBRARY-feature-properties.rst b/Help/release/dev/GenEx-LINK_LIBRARY-feature-properties.rst
new file mode 100644
index 0000000..a96eab9
--- /dev/null
+++ b/Help/release/dev/GenEx-LINK_LIBRARY-feature-properties.rst
@@ -0,0 +1,7 @@
+GenEx-LINK_LIBRARY-feature-properties
+-------------------------------------
+
+* Link features, as used with the :genex:`LINK_LIBRARY` generator expression,
+  gained the ability to have properties that describe their behavior by
+  specifying the :variable:`CMAKE_LINK_LIBRARY_<FEATURE>_PROPERTIES` or
+  :variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES` variables.
diff --git a/Help/release/dev/fetchcontent-direct.rst b/Help/release/dev/fetchcontent-direct.rst
new file mode 100644
index 0000000..7cb33ab
--- /dev/null
+++ b/Help/release/dev/fetchcontent-direct.rst
@@ -0,0 +1,9 @@
+fetchcontent-direct
+-------------------
+
+* :module:`FetchContent` now prefers to populate content directly rather
+  than using a separate sub-build. This may significantly improve configure
+  times on some systems (Windows especially, but also on macOS when using
+  the Xcode generator). Policy :policy:`CMP0168` provides backward
+  compatibility for those projects that still rely on using a sub-build for
+  content population.
diff --git a/Help/release/dev/print-configure-generate-time.rst b/Help/release/dev/print-configure-generate-time.rst
new file mode 100644
index 0000000..098ca6e
--- /dev/null
+++ b/Help/release/dev/print-configure-generate-time.rst
@@ -0,0 +1,8 @@
+print-configure-generate-time
+-----------------------------
+
+* The durations printed after "Configuring done" and "Generating done"
+  messages now reflect time spent in generator-specific steps, and
+  in a code model evaluation step at the beginning of generation that
+  was not previously captured.  Printed durations may appear longer
+  than in previous versions of CMake.
diff --git a/Help/release/dev/remove-vs9-generator.rst b/Help/release/dev/remove-vs9-generator.rst
new file mode 100644
index 0000000..2ff805c
--- /dev/null
+++ b/Help/release/dev/remove-vs9-generator.rst
@@ -0,0 +1,4 @@
+remove-vs9-generator
+--------------------
+
+* The :generator:`Visual Studio 9 2008` generator has been removed.
diff --git a/Help/variable/CMAKE_CFG_INTDIR.rst b/Help/variable/CMAKE_CFG_INTDIR.rst
index 3045d91..5a1f9e8 100644
--- a/Help/variable/CMAKE_CFG_INTDIR.rst
+++ b/Help/variable/CMAKE_CFG_INTDIR.rst
@@ -18,7 +18,6 @@
 
 ::
 
-  $(ConfigurationName) = Visual Studio 9
   $(Configuration)     = Visual Studio 12 and above
   $(CONFIGURATION)     = Xcode
   .                    = Make-based tools
diff --git a/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES.rst b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES.rst
new file mode 100644
index 0000000..d8efd0f
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_LINK_LIBRARY_FEATURE_PROPERTIES.rst
@@ -0,0 +1,12 @@
+CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES
+----------------------------------------------
+
+.. versionadded:: 3.30
+
+This variable defines the semantics of the specified ``<FEATURE>`` for the
+language ``<LANG>`` (as described by the
+:variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` or
+:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variables) used for the link
+command generation.
+
+.. include:: CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.rst b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.rst
new file mode 100644
index 0000000..86b4e77
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.rst
@@ -0,0 +1,14 @@
+CMAKE_LINK_LIBRARY_<FEATURE>_PROPERTIES
+---------------------------------------
+
+.. versionadded:: 3.30
+
+This variable defines the semantics of the specified ``<FEATURE>`` (as
+described by the :variable:`CMAKE_<LANG>_LINK_LIBRARY_USING_<FEATURE>` or
+:variable:`CMAKE_LINK_LIBRARY_USING_<FEATURE>` variables) used for the link
+command generation.
+
+This variable will be considered only if the
+ :variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES` is not defined.
+
+.. include:: CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
new file mode 100644
index 0000000..202a7ec
--- /dev/null
+++ b/Help/variable/CMAKE_LINK_LIBRARY_FEATURE_PROPERTIES.txt
@@ -0,0 +1,87 @@
+Feature Properties Definition
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+A feature properties definition is a
+:ref:`semicolon-separated list <CMake Language Lists>` of ``property=value(s)``
+items. In the case of multiple values can be specified, they are separated by
+a comma.
+
+The following properties are supported:
+
+``LIBRARY_TYPE=<library_type-list>``
+  Specify which library types are supported by this feature. The possible
+  values are: ``STATIC``, ``SHARED``, ``MODULE`` or ``EXECUTABLE``.
+
+  If this property is not specified, the default is
+  ``LIBRARY_TYPE=STATIC,SHARED,MODULE,EXECUTABLE``.
+
+  If the feature is used with an unsupported library type, CMake will emit a
+  developer warning and the feature will be ignored.
+
+``OVERRIDE=<feature-list>``
+  Specify which features will be replaced by this one in the event of a
+  conflict. This override mechanism is superseded by any
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE` or
+  :prop_tgt:`LINK_LIBRARY_OVERRIDE_<LIBRARY>` target properties definitions.
+
+  If this property is not specified, the default is an empty list.
+
+``UNICITY=YES|NO|DEFAULT``
+  Manage the strategy of de-duplication for the libraries using this feature.
+
+  ``YES``
+    Libraries are de-duplicated regardless the default strategy applied by
+    CMake.
+
+  ``NO``
+    Libraries are not de-duplicated regardless the default strategy applied
+    by CMake.
+
+  ``DEFAULT``
+    Apply the default CMake strategy.
+
+  If this property is not specified, ``DEFAULT`` will be used.
+
+Example
+^^^^^^^
+
+A common need is the loading of a full archive as part of the creation of a
+shared library or an executable. For that purpose, the ``WHOLE_ARCHIVE``
+feature can be used.
+
+Currently, the associated properties with this feature are defined as follows:
+
+.. code-block:: cmake
+
+  set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC
+                                                  OVERRIDE=DEFAULT
+                                                  UNICITY=YES)
+
+``LIBRARY_TYPE=STATIC``
+  Obviously, this feature is only meaningful for static libraries.
+``OVERRIDE=DEFAULT``
+  The ``DEFAULT`` feature will be overridden by the ``WHOLE_ARCHIVE`` feature
+  because they are compatible and enhance the user's experience: standard
+  library specification and ``$<LINK_LIBRARY:WHOLE_ARCHIVE>`` can be used
+  freely.
+``UNICITY=YES``
+  When this feature is used, all symbols from the static library are loaded
+  by the linker, so there is no need to duplicate the library on the link
+  command.
+
+A typical usage of the ``WHOLE_ARCHIVE`` can be:
+
+.. code-block:: cmake
+
+  add_library(A STATIC ...)
+  add_library(B STATIC ...)
+
+  target_link_libraries(B PUBLIC A)
+  target_link_libraries(A PUBLIC B)
+
+  add_library(global SHARED ...)
+  target_link_libraries(global PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,A>)
+
+The resulting link command will only have one iteration of the ``A`` library
+specified with the needed linker flags to ensure the load of all the symbols
+of the library.
diff --git a/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt b/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
index 4b13b7c..0359f58 100644
--- a/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
+++ b/Help/variable/CMAKE_LINK_LIBRARY_USING_FEATURE.txt
@@ -2,6 +2,9 @@
 and underscores.  Feature names defined in all uppercase are reserved for
 CMake's own built-in features (see `Predefined Features`_ further below).
 
+The feature behavior can be described using the
+:variable:`CMAKE_<LANG>_LINK_LIBRARY_<FEATURE>_PROPERTIES` or
+:variable:`CMAKE_LINK_LIBRARY_<FEATURE>_PROPERTIES` variables.
 
 Feature Definitions
 ^^^^^^^^^^^^^^^^^^^
diff --git a/Help/variable/CMAKE_MODULE_PATH.rst b/Help/variable/CMAKE_MODULE_PATH.rst
index 3021b49..213b75c 100644
--- a/Help/variable/CMAKE_MODULE_PATH.rst
+++ b/Help/variable/CMAKE_MODULE_PATH.rst
@@ -5,4 +5,17 @@
 represented using forward slashes, specifying a search path for CMake modules
 to be loaded by the :command:`include` or :command:`find_package` commands
 before checking the default modules that come with CMake. By default it is
-empty.  It is intended to be set by the project.
+empty. It is intended to be set by the project.
+
+It's fairly common for a project to have a directory containing various
+``*.cmake`` files to assist in development. Adding the directory to the
+:variable:`CMAKE_MODULE_PATH` simplifies loading them. For example, a
+project's top-level ``CMakeLists.txt`` file may contain:
+
+.. code-block:: cmake
+
+  list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+  include(Foo) # Loads ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Foo.cmake
+
+  find_package(Bar) # Loads ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindBar.cmake
diff --git a/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst b/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst
index 721ceaa..d4b256a 100644
--- a/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst
+++ b/Help/variable/CMAKE_MSVCIDE_RUN_PATH.rst
@@ -5,7 +5,7 @@
 
 Extra PATH locations that should be used when executing
 :command:`add_custom_command` or :command:`add_custom_target` when using the
-:generator:`Visual Studio 9 2008` (or above) generator. This allows
+:generator:`Visual Studio 12 2013` (or above) generator. This allows
 for running commands and using dll's that the IDE environment is not aware of.
 
 If not set explicitly the value is initialized by the ``CMAKE_MSVCIDE_RUN_PATH``
diff --git a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
index 155931f..95e09b1 100644
--- a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
+++ b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
@@ -1,7 +1,7 @@
 CMAKE_VS_DEVENV_COMMAND
 -----------------------
 
-The generators for :generator:`Visual Studio 9 2008` and above set this
+The generators for :generator:`Visual Studio 12 2013` and above set this
 variable to the ``devenv.com`` command installed with the corresponding
 Visual Studio version.  Note that this variable may be empty on
 Visual Studio Express editions because they do not provide this tool.
diff --git a/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst b/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst
index ceedf28..4857269 100644
--- a/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst
+++ b/Help/variable/CMAKE_VS_INTEL_Fortran_PROJECT_VERSION.rst
@@ -1,7 +1,7 @@
 CMAKE_VS_INTEL_Fortran_PROJECT_VERSION
 --------------------------------------
 
-When generating for :generator:`Visual Studio 9 2008` or greater with the Intel
+When generating for :generator:`Visual Studio 12 2013` or greater with the Intel
 Fortran plugin installed, this specifies the ``.vfproj`` project file format
 version.  This is intended for internal use by CMake and should not be
 used by project code.
diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake
index 998e476..72fb801 100644
--- a/Modules/CMakeCInformation.cmake
+++ b/Modules/CMakeCInformation.cmake
@@ -91,23 +91,6 @@
   set(CMAKE_USER_MAKE_RULES_OVERRIDE_C "${_override}")
 endif()
 
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_C_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_C_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_C_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
-endif()
-
 set(CMAKE_C_FLAGS_INIT "$ENV{CFLAGS} ${CMAKE_C_FLAGS_INIT}")
 
 cmake_initialize_per_config_variable(CMAKE_C_FLAGS "Flags used by the C compiler")
@@ -129,6 +112,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(C)
 
 # now define the following rule variables
 
@@ -191,16 +175,4 @@
     "<CMAKE_C_COMPILER> <FLAGS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 endif()
 
-if(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG)
-  set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP)
-  set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG)
-  set(CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
-endif()
-
 set(CMAKE_C_INFORMATION_LOADED 1)
diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake
index e774088..66a5faa 100644
--- a/Modules/CMakeCUDAInformation.cmake
+++ b/Modules/CMakeCUDAInformation.cmake
@@ -55,59 +55,6 @@
 endif()
 
 
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG_SEP)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_CUDA_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_EXE_EXPORTS_CUDA_FLAG)
-  set(CMAKE_EXE_EXPORTS_CUDA_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_CUDA_FLAG)
-  set(CMAKE_SHARED_LIBRARY_SONAME_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG)
-  set(CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG_SEP)
-  set(CMAKE_EXECUTABLE_RUNTIME_CUDA_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_CUDA_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RPATH_LINK_CUDA_FLAG)
-  set(CMAKE_EXECUTABLE_RPATH_LINK_CUDA_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_CUDA_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CUDA_WITH_RUNTIME_PATH)
-  set(CMAKE_SHARED_LIBRARY_LINK_CUDA_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
-endif()
-
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_CUDA_FLAGS ${CMAKE_SHARED_LIBRARY_CUDA_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_CUDA_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS})
-endif()
-
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
 
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
@@ -129,6 +76,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(CUDA)
 
 # now define the following rules:
 # CMAKE_CUDA_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake
index 3753d18..e521fb8 100644
--- a/Modules/CMakeCXXInformation.cmake
+++ b/Modules/CMakeCXXInformation.cmake
@@ -91,117 +91,6 @@
 endif()
 
 
-# Create a set of shared library variable specific to C++
-# For 90% of the systems, these are the same flags as the C versions
-# so if these are not set just copy the flags from the c version
-if(NOT CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
-endif()
-
-if(NOT CMAKE_CXX_COMPILE_OPTIONS_PIC)
-  set(CMAKE_CXX_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC})
-endif()
-
-if(NOT CMAKE_CXX_COMPILE_OPTIONS_PIE)
-  set(CMAKE_CXX_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE})
-endif()
-if(NOT CMAKE_CXX_LINK_OPTIONS_PIE)
-  set(CMAKE_CXX_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE})
-endif()
-if(NOT CMAKE_CXX_LINK_OPTIONS_NO_PIE)
-  set(CMAKE_CXX_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE})
-endif()
-
-if(NOT CMAKE_CXX_COMPILE_OPTIONS_DLL)
-  set(CMAKE_CXX_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_CXX_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_EXE_EXPORTS_CXX_FLAG)
-  set(CMAKE_EXE_EXPORTS_CXX_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG)
-  set(CMAKE_SHARED_LIBRARY_SONAME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG)
-  set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP)
-  set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG)
-  set(CMAKE_EXECUTABLE_RPATH_LINK_CXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_CXX_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH)
-  set(CMAKE_SHARED_LIBRARY_LINK_CXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
-endif()
-
-if(NOT CMAKE_INCLUDE_FLAG_CXX)
-  set(CMAKE_INCLUDE_FLAG_CXX ${CMAKE_INCLUDE_FLAG_C})
-endif()
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_CXX_FLAGS})
-endif()
-
-# repeat for modules
-if(NOT CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS)
-  set(CMAKE_SHARED_MODULE_CREATE_CXX_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS})
-endif()
-
-if(NOT CMAKE_SHARED_MODULE_CXX_FLAGS)
-  set(CMAKE_SHARED_MODULE_CXX_FLAGS ${CMAKE_SHARED_MODULE_C_FLAGS})
-endif()
-
-# Initialize CXX link type selection flags from C versions.
-foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE)
-  if(NOT CMAKE_${type}_LINK_STATIC_CXX_FLAGS)
-    set(CMAKE_${type}_LINK_STATIC_CXX_FLAGS
-      ${CMAKE_${type}_LINK_STATIC_C_FLAGS})
-  endif()
-  if(NOT CMAKE_${type}_LINK_DYNAMIC_CXX_FLAGS)
-    set(CMAKE_${type}_LINK_DYNAMIC_CXX_FLAGS
-      ${CMAKE_${type}_LINK_DYNAMIC_C_FLAGS})
-  endif()
-endforeach()
-
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
-
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
@@ -227,6 +116,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(CXX)
 
 # now define the following rules:
 # CMAKE_CXX_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeCommonLanguageInclude.cmake b/Modules/CMakeCommonLanguageInclude.cmake
index b043e18..5511930 100644
--- a/Modules/CMakeCommonLanguageInclude.cmake
+++ b/Modules/CMakeCommonLanguageInclude.cmake
@@ -21,3 +21,112 @@
 mark_as_advanced(
 CMAKE_VERBOSE_MAKEFILE
 )
+
+# The Platform/* modules set a bunch of platform-specific flags expressed
+# for the C toolchain.  Other languages call this to copy them as defaults.
+macro(_cmake_common_language_platform_flags lang)
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS)
+    set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
+  endif()
+
+  if(NOT DEFINED CMAKE_${lang}_COMPILE_OPTIONS_PIC)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC})
+  endif()
+
+  if(NOT DEFINED CMAKE_${lang}_COMPILE_OPTIONS_PIE)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE})
+  endif()
+  if(NOT DEFINED CMAKE_${lang}_LINK_OPTIONS_PIE)
+    set(CMAKE_${lang}_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE})
+  endif()
+  if(NOT DEFINED CMAKE_${lang}_LINK_OPTIONS_NO_PIE)
+    set(CMAKE_${lang}_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE})
+  endif()
+
+  if(NOT DEFINED CMAKE_${lang}_COMPILE_OPTIONS_DLL)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_${lang}_FLAGS)
+    set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS)
+    set(CMAKE_SHARED_LIBRARY_LINK_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG)
+    set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP)
+    set(CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG)
+    set(CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
+  endif()
+
+  if(NOT DEFINED CMAKE_EXE_EXPORTS_${lang}_FLAG)
+    set(CMAKE_EXE_EXPORTS_${lang}_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG)
+    set(CMAKE_SHARED_LIBRARY_SONAME_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG})
+  endif()
+
+  if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG)
+    set(CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG})
+  endif()
+
+  if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG_SEP)
+    set(CMAKE_EXECUTABLE_RUNTIME_${lang}_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_${lang}_FLAG_SEP})
+  endif()
+
+  if(NOT DEFINED CMAKE_EXECUTABLE_RPATH_LINK_${lang}_FLAG)
+    set(CMAKE_EXECUTABLE_RPATH_LINK_${lang}_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_${lang}_FLAG})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_${lang}_WITH_RUNTIME_PATH)
+    set(CMAKE_SHARED_LIBRARY_LINK_${lang}_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
+  endif()
+
+  if(NOT DEFINED CMAKE_INCLUDE_FLAG_${lang})
+    set(CMAKE_INCLUDE_FLAG_${lang} ${CMAKE_INCLUDE_FLAG_C})
+  endif()
+
+  # for most systems a module is the same as a shared library
+  # so unless the variable CMAKE_MODULE_EXISTS is set just
+  # copy the values from the LIBRARY variables
+  if(NOT CMAKE_MODULE_EXISTS)
+    set(CMAKE_SHARED_MODULE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_${lang}_FLAGS})
+    set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS})
+  endif()
+
+  if(NOT DEFINED CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS)
+    set(CMAKE_SHARED_MODULE_CREATE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
+  endif()
+  if(NOT DEFINED CMAKE_SHARED_MODULE_${lang}_FLAGS)
+    set(CMAKE_SHARED_MODULE_${lang}_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS})
+  endif()
+
+  foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE)
+    if(NOT DEFINED CMAKE_${type}_LINK_STATIC_${lang}_FLAGS)
+      set(CMAKE_${type}_LINK_STATIC_${lang}_FLAGS
+        ${CMAKE_${type}_LINK_STATIC_C_FLAGS})
+    endif()
+    if(NOT DEFINED CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS)
+      set(CMAKE_${type}_LINK_DYNAMIC_${lang}_FLAGS
+        ${CMAKE_${type}_LINK_DYNAMIC_C_FLAGS})
+    endif()
+  endforeach()
+
+  if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
+    if(NOT DEFINED CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG)
+      set(CMAKE_${lang}_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
+    endif()
+    if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
+      set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
+    endif()
+  endif()
+endmacro()
diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake
index e364755..984a39d 100644
--- a/Modules/CMakeFortranInformation.cmake
+++ b/Modules/CMakeFortranInformation.cmake
@@ -67,105 +67,6 @@
   set(CMAKE_USER_MAKE_RULES_OVERRIDE_Fortran "${_override}")
 endif()
 
-if(NOT CMAKE_Fortran_COMPILE_OPTIONS_PIC)
-  set(CMAKE_Fortran_COMPILE_OPTIONS_PIC ${CMAKE_C_COMPILE_OPTIONS_PIC})
-endif()
-
-if(NOT CMAKE_Fortran_COMPILE_OPTIONS_PIE)
-  set(CMAKE_Fortran_COMPILE_OPTIONS_PIE ${CMAKE_C_COMPILE_OPTIONS_PIE})
-endif()
-if(NOT CMAKE_Fortran_LINK_OPTIONS_PIE)
-  set(CMAKE_Fortran_LINK_OPTIONS_PIE ${CMAKE_C_LINK_OPTIONS_PIE})
-endif()
-if(NOT CMAKE_Fortran_LINK_OPTIONS_NO_PIE)
-  set(CMAKE_Fortran_LINK_OPTIONS_NO_PIE ${CMAKE_C_LINK_OPTIONS_NO_PIE})
-endif()
-
-if(NOT CMAKE_Fortran_COMPILE_OPTIONS_DLL)
-  set(CMAKE_Fortran_COMPILE_OPTIONS_DLL ${CMAKE_C_COMPILE_OPTIONS_DLL})
-endif()
-
-# Create a set of shared library variable specific to Fortran
-# For 90% of the systems, these are the same flags as the C versions
-# so if these are not set just copy the flags from the c version
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_Fortran_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_C_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_LINK_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_EXE_EXPORTS_Fortran_FLAG)
-  set(CMAKE_EXE_EXPORTS_Fortran_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG)
-  set(CMAKE_SHARED_LIBRARY_SONAME_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG})
-endif()
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_Fortran_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_Fortran_FLAGS})
-endif()
-
-# repeat for modules
-if(NOT DEFINED CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS)
-  set(CMAKE_SHARED_MODULE_CREATE_Fortran_FLAGS ${CMAKE_SHARED_MODULE_CREATE_C_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_MODULE_Fortran_FLAGS)
-  set(CMAKE_SHARED_MODULE_Fortran_FLAGS ${CMAKE_SHARED_MODULE_C_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG)
-  set(CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP)
-  set(CMAKE_EXECUTABLE_RUNTIME_Fortran_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_Fortran_FLAG_SEP})
-endif()
-
-if(NOT DEFINED CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG)
-  set(CMAKE_EXECUTABLE_RPATH_LINK_Fortran_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_Fortran_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH)
-  set(CMAKE_SHARED_LIBRARY_LINK_Fortran_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
-endif()
-
-if(NOT CMAKE_INCLUDE_FLAG_Fortran)
-  set(CMAKE_INCLUDE_FLAG_Fortran ${CMAKE_INCLUDE_FLAG_C})
-endif()
-
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
-
 set(CMAKE_VERBOSE_MAKEFILE FALSE CACHE BOOL "If this value is on, makefiles will be generated without the .SILENT directive, and all commands will be echoed to the console during the make.  This is useful for debugging only. With Visual Studio IDE projects all commands are done without /nologo.")
 
 set(CMAKE_Fortran_FLAGS_INIT "$ENV{FFLAGS} ${CMAKE_Fortran_FLAGS_INIT}")
@@ -178,6 +79,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(Fortran)
 
 # now define the following rule variables
 # CMAKE_Fortran_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeHIPInformation.cmake b/Modules/CMakeHIPInformation.cmake
index 3995c36..dc76c63 100644
--- a/Modules/CMakeHIPInformation.cmake
+++ b/Modules/CMakeHIPInformation.cmake
@@ -36,60 +36,6 @@
 endif()
 
 
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG_SEP)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_HIP_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_EXE_EXPORTS_HIP_FLAG)
-  set(CMAKE_EXE_EXPORTS_HIP_FLAG ${CMAKE_EXE_EXPORTS_C_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_HIP_FLAG)
-  set(CMAKE_SHARED_LIBRARY_SONAME_HIP_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_C_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG)
-  set(CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG_SEP)
-  set(CMAKE_EXECUTABLE_RUNTIME_HIP_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_HIP_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RPATH_LINK_HIP_FLAG)
-  set(CMAKE_EXECUTABLE_RPATH_LINK_HIP_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_HIP_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_HIP_WITH_RUNTIME_PATH)
-  set(CMAKE_SHARED_LIBRARY_LINK_HIP_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_C_WITH_RUNTIME_PATH})
-endif()
-
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_HIP_FLAGS ${CMAKE_SHARED_LIBRARY_HIP_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_HIP_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_HIP_FLAGS})
-endif()
-
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
-
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
@@ -110,6 +56,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(HIP)
 
 # now define the following rules:
 # CMAKE_HIP_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeOBJCInformation.cmake b/Modules/CMakeOBJCInformation.cmake
index 8b8f10e..bcb8b11 100644
--- a/Modules/CMakeOBJCInformation.cmake
+++ b/Modules/CMakeOBJCInformation.cmake
@@ -91,24 +91,6 @@
   set(CMAKE_USER_MAKE_RULES_OVERRIDE_OBJC "${_override}")
 endif()
 
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_OBJC_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_OBJC_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
-
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS})
-endif()
-
 set(CMAKE_OBJC_FLAGS_INIT "$ENV{OBJCFLAGS} ${CMAKE_OBJC_FLAGS_INIT}")
 
 cmake_initialize_per_config_variable(CMAKE_OBJC_FLAGS "Flags used by the Objective-C compiler")
@@ -130,6 +112,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(OBJC)
 
 # now define the following rule variables
 
diff --git a/Modules/CMakeOBJCXXInformation.cmake b/Modules/CMakeOBJCXXInformation.cmake
index da1d6c6..4fcd469 100644
--- a/Modules/CMakeOBJCXXInformation.cmake
+++ b/Modules/CMakeOBJCXXInformation.cmake
@@ -87,117 +87,6 @@
 endif()
 
 
-# Create a set of shared library variable specific to Objective-C++
-# For 90% of the systems, these are the same flags as the Objective-C versions
-# so if these are not set just copy the flags from the Objective-C version
-if(NOT CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJC_FLAGS})
-endif()
-
-if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIC)
-  set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIC ${CMAKE_OBJC_COMPILE_OPTIONS_PIC})
-endif()
-
-if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_PIE)
-  set(CMAKE_OBJCXX_COMPILE_OPTIONS_PIE ${CMAKE_OBJC_COMPILE_OPTIONS_PIE})
-endif()
-if(NOT CMAKE_OBJCXX_LINK_OPTIONS_PIE)
-  set(CMAKE_OBJCXX_LINK_OPTIONS_PIE ${CMAKE_OBJC_LINK_OPTIONS_PIE})
-endif()
-if(NOT CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE)
-  set(CMAKE_OBJCXX_LINK_OPTIONS_NO_PIE ${CMAKE_OBJC_LINK_OPTIONS_NO_PIE})
-endif()
-
-if(NOT CMAKE_OBJCXX_COMPILE_OPTIONS_DLL)
-  set(CMAKE_OBJCXX_COMPILE_OPTIONS_DLL ${CMAKE_OBJC_COMPILE_OPTIONS_DLL})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJC_FLAGS})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS)
-  set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_LINK_OBJC_FLAGS})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP)
-  set(CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJC_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG)
-  set(CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJC_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_EXE_EXPORTS_OBJCXX_FLAG)
-  set(CMAKE_EXE_EXPORTS_OBJCXX_FLAG ${CMAKE_EXE_EXPORTS_OBJC_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG)
-  set(CMAKE_SHARED_LIBRARY_SONAME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_SONAME_OBJC_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG)
-  set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP)
-  set(CMAKE_EXECUTABLE_RUNTIME_OBJCXX_FLAG_SEP ${CMAKE_SHARED_LIBRARY_RUNTIME_OBJCXX_FLAG_SEP})
-endif()
-
-if(NOT CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG)
-  set(CMAKE_EXECUTABLE_RPATH_LINK_OBJCXX_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_OBJCXX_FLAG})
-endif()
-
-if(NOT DEFINED CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH)
-  set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_WITH_RUNTIME_PATH ${CMAKE_SHARED_LIBRARY_LINK_OBJC_WITH_RUNTIME_PATH})
-endif()
-
-if(NOT CMAKE_INCLUDE_FLAG_OBJCXX)
-  set(CMAKE_INCLUDE_FLAG_OBJCXX ${CMAKE_INCLUDE_FLAG_C})
-endif()
-
-# for most systems a module is the same as a shared library
-# so unless the variable CMAKE_MODULE_EXISTS is set just
-# copy the values from the LIBRARY variables
-if(NOT CMAKE_MODULE_EXISTS)
-  set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_OBJCXX_FLAGS})
-  set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_LIBRARY_CREATE_OBJCXX_FLAGS})
-endif()
-
-# repeat for modules
-if(NOT CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS)
-  set(CMAKE_SHARED_MODULE_CREATE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_CREATE_OBJC_FLAGS})
-endif()
-
-if(NOT CMAKE_SHARED_MODULE_OBJCXX_FLAGS)
-  set(CMAKE_SHARED_MODULE_OBJCXX_FLAGS ${CMAKE_SHARED_MODULE_OBJC_FLAGS})
-endif()
-
-# Initialize OBJCXX link type selection flags from OBJC versions.
-foreach(type IN ITEMS SHARED_LIBRARY SHARED_MODULE EXE)
-  if(NOT CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS)
-    set(CMAKE_${type}_LINK_STATIC_OBJCXX_FLAGS
-      ${CMAKE_${type}_LINK_STATIC_OBJC_FLAGS})
-  endif()
-  if(NOT CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS)
-    set(CMAKE_${type}_LINK_DYNAMIC_OBJCXX_FLAGS
-      ${CMAKE_${type}_LINK_DYNAMIC_OBJC_FLAGS})
-  endif()
-endforeach()
-
-if(CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
-  if(NOT DEFINED CMAKE_OBJCXX_LINK_WHAT_YOU_USE_FLAG)
-    set(CMAKE_OBJCXX_LINK_WHAT_YOU_USE_FLAG "LINKER:--no-as-needed")
-  endif()
-  if(NOT DEFINED CMAKE_LINK_WHAT_YOU_USE_CHECK)
-    set(CMAKE_LINK_WHAT_YOU_USE_CHECK ldd -u -r)
-  endif()
-endif()
-
 # add the flags to the cache based
 # on the initial values computed in the platform/*.cmake files
 # use _INIT variables so that this only happens the first time
@@ -223,6 +112,7 @@
 endif()
 
 include(CMakeCommonLanguageInclude)
+_cmake_common_language_platform_flags(OBJCXX)
 
 # now define the following rules:
 # CMAKE_OBJCXX_CREATE_SHARED_LIBRARY
diff --git a/Modules/CMakeTestSwiftCompiler.cmake b/Modules/CMakeTestSwiftCompiler.cmake
index c7df912..d89e606 100644
--- a/Modules/CMakeTestSwiftCompiler.cmake
+++ b/Modules/CMakeTestSwiftCompiler.cmake
@@ -24,7 +24,10 @@
   # Clear result from normal variable.
   unset(CMAKE_Swift_COMPILER_WORKS)
   # Puts test result in cache variable.
-  set(__CMAKE_Swift_TEST_SOURCE "print(\"CMake\")\n")
+  string(CONCAT __CMAKE_Swift_TEST_SOURCE
+  "public struct CMakeStruct {"
+  "  let x: Int"
+  "}")
   try_compile(CMAKE_Swift_COMPILER_WORKS
     SOURCE_FROM_VAR main.swift __CMAKE_Swift_TEST_SOURCE
     OUTPUT_VARIABLE __CMAKE_Swift_COMPILER_OUTPUT)
diff --git a/Modules/CheckIPOSupported.cmake b/Modules/CheckIPOSupported.cmake
index de682b7..fe27295 100644
--- a/Modules/CheckIPOSupported.cmake
+++ b/Modules/CheckIPOSupported.cmake
@@ -257,11 +257,6 @@
     endif()
   endforeach()
 
-  if(CMAKE_GENERATOR MATCHES "^Visual Studio 9 ")
-    _ipo_not_supported("CMake doesn't support IPO for current generator")
-    return()
-  endif()
-
   foreach(x ${languages})
     _ipo_run_language_check(${x})
   endforeach()
diff --git a/Modules/CheckIPOSupported/foo.c b/Modules/CheckIPOSupported/foo.c
index 1e56597..6a64a99 100644
--- a/Modules/CheckIPOSupported/foo.c
+++ b/Modules/CheckIPOSupported/foo.c
@@ -1,4 +1,4 @@
-int foo()
+int foo(void)
 {
   return 0x42;
 }
diff --git a/Modules/CheckIPOSupported/main.c b/Modules/CheckIPOSupported/main.c
index 8d2ab01..db0956c 100644
--- a/Modules/CheckIPOSupported/main.c
+++ b/Modules/CheckIPOSupported/main.c
@@ -1,4 +1,4 @@
-int foo();
+int foo(void);
 
 int main(void)
 {
diff --git a/Modules/Compiler/CMakeCommonCompilerMacros.cmake b/Modules/Compiler/CMakeCommonCompilerMacros.cmake
index 75a9da2..ffd02ec 100644
--- a/Modules/Compiler/CMakeCommonCompilerMacros.cmake
+++ b/Modules/Compiler/CMakeCommonCompilerMacros.cmake
@@ -250,7 +250,7 @@
     _cmake_supported_import_std_experimental)
   if (NOT _cmake_supported_import_std_experimental)
     set("${variable}"
-      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Experimental `import std` support not enabled when detecting toolchain\")\n"
+      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled (usually a `project()` call)\")\n"
       PARENT_SCOPE)
     return ()
   endif ()
diff --git a/Modules/Compiler/Clang-CXX-CXXImportStd.cmake b/Modules/Compiler/Clang-CXX-CXXImportStd.cmake
index 55dbfb8..f58f17e 100644
--- a/Modules/Compiler/Clang-CXX-CXXImportStd.cmake
+++ b/Modules/Compiler/Clang-CXX-CXXImportStd.cmake
@@ -1,5 +1,8 @@
 function (_cmake_cxx_import_std std variable)
   if (NOT CMAKE_CXX_STANDARD_LIBRARY STREQUAL "libc++")
+    set("${variable}"
+      "set(CMAKE_CXX${std}_COMPILER_IMPORT_STD_NOT_FOUND_MESSAGE \"Only `libc++` is supported\")\n"
+      PARENT_SCOPE)
     return ()
   endif ()
 
diff --git a/Modules/Compiler/IntelLLVM-CXX.cmake b/Modules/Compiler/IntelLLVM-CXX.cmake
index 989a1d4..45e5d66 100644
--- a/Modules/Compiler/IntelLLVM-CXX.cmake
+++ b/Modules/Compiler/IntelLLVM-CXX.cmake
@@ -49,11 +49,21 @@
 
   set(CMAKE_CXX_STANDARD_LATEST 20)
 
-  if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2021.2.0)
+  if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0)
+    set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-std=c++23")
+    set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++23")
+    set(CMAKE_CXX_STANDARD_LATEST 23)
+  elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2021.2.0)
     set(CMAKE_CXX23_STANDARD_COMPILE_OPTION  "-std=c++2b")
     set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b")
     set(CMAKE_CXX_STANDARD_LATEST 23)
   endif()
+
+  if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0)
+    set(CMAKE_CXX26_STANDARD_COMPILE_OPTION "-std=c++26")
+    set(CMAKE_CXX26_EXTENSION_COMPILE_OPTION "-std=gnu++26")
+    set(CMAKE_CXX_STANDARD_LATEST 26)
+  endif()
 else()
   set(CMAKE_CXX98_STANDARD_COMPILE_OPTION  "")
   set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION "")
@@ -72,11 +82,22 @@
 
   set(CMAKE_CXX_STANDARD_LATEST 20)
 
-  if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2021.2.0)
+  if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0)
+    set(CMAKE_CXX23_STANDARD_COMPILE_OPTION "-Qstd=c++23")
+    set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-Qstd=c++23")
+    set(CMAKE_CXX_STANDARD_LATEST 23)
+  elseif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2021.2.0)
     set(CMAKE_CXX23_STANDARD_COMPILE_OPTION  "-Qstd:c++2b")
     set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-Qstd:c++2b")
     set(CMAKE_CXX_STANDARD_LATEST 23)
   endif()
+
+  if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 2024.0.0)
+    set(CMAKE_CXX26_STANDARD_COMPILE_OPTION "-Qstd=c++26")
+    set(CMAKE_CXX26_EXTENSION_COMPILE_OPTION "-Qstd=c++26")
+    set(CMAKE_CXX_STANDARD_LATEST 26)
+  endif()
+
 endif()
 
 __compiler_check_default_language_standard(CXX 2020 14)
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 3323b18..86d83f5 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -1296,492 +1296,12 @@
 cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced
 cmake_policy(SET CMP0057 NEW) # if() supports IN_LIST
 
-macro(_ep_get_hash_algos out_var)
-  set(${out_var}
-    MD5
-    SHA1
-    SHA224
-    SHA256
-    SHA384
-    SHA512
-    SHA3_224
-    SHA3_256
-    SHA3_384
-    SHA3_512
-  )
-endmacro()
-
-macro(_ep_get_hash_regex out_var)
-  _ep_get_hash_algos(${out_var})
-  list(JOIN ${out_var} "|" ${out_var})
-  set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$")
-endmacro()
-
-function(_ep_parse_arguments
-  f
-  keywords
-  name
-  ns
-  args
-)
-  # Transfer the arguments to this function into target properties for the
-  # new custom target we just added so that we can set up all the build steps
-  # correctly based on target properties.
-  #
-  # Because some keywords can be repeated, we can't use cmake_parse_arguments().
-  # Instead, we loop through ARGN and consider the namespace starting with an
-  # upper-case letter followed by at least two more upper-case letters,
-  # numbers or underscores to be keywords.
-
-  set(key)
-
-  foreach(arg IN LISTS args)
-    set(is_value 1)
-
-    if(arg MATCHES "^[A-Z][A-Z0-9_][A-Z0-9_]+$" AND
-        NOT (("x${arg}x" STREQUAL "x${key}x") AND
-             ("x${key}x" STREQUAL "xCOMMANDx")) AND
-        NOT arg MATCHES "^(TRUE|FALSE)$")
-      if(arg IN_LIST keywords)
-        set(is_value 0)
-      endif()
-    endif()
-
-    if(is_value)
-      if(key)
-        # Value
-        if(NOT arg STREQUAL "")
-          set_property(TARGET ${name} APPEND PROPERTY ${ns}${key} "${arg}")
-        else()
-          get_property(have_key TARGET ${name} PROPERTY ${ns}${key} SET)
-          if(have_key)
-            get_property(value TARGET ${name} PROPERTY ${ns}${key})
-            set_property(TARGET ${name} PROPERTY ${ns}${key} "${value};${arg}")
-          else()
-            set_property(TARGET ${name} PROPERTY ${ns}${key} "${arg}")
-          endif()
-        endif()
-      else()
-        # Missing Keyword
-        message(AUTHOR_WARNING
-          "value '${arg}' with no previous keyword in ${f}"
-        )
-      endif()
-    else()
-      set(key "${arg}")
-    endif()
-  endforeach()
-endfunction()
-
-
 define_property(DIRECTORY PROPERTY "EP_BASE" INHERITED)
 define_property(DIRECTORY PROPERTY "EP_PREFIX" INHERITED)
 define_property(DIRECTORY PROPERTY "EP_STEP_TARGETS" INHERITED)
 define_property(DIRECTORY PROPERTY "EP_INDEPENDENT_STEP_TARGETS" INHERITED)
 define_property(DIRECTORY PROPERTY "EP_UPDATE_DISCONNECTED" INHERITED)
 
-function(_ep_get_tls_version name tls_version_var)
-  set(tls_version_regex "^1\\.[0-3]$")
-  get_property(tls_version TARGET ${name} PROPERTY _EP_TLS_VERSION)
-  if(NOT "x${tls_version}" STREQUAL "x")
-    if(NOT tls_version MATCHES "${tls_version_regex}")
-      message(FATAL_ERROR "TLS_VERSION '${tls_version}' not known")
-    endif()
-  elseif(NOT "x${CMAKE_TLS_VERSION}" STREQUAL "x")
-    set(tls_version "${CMAKE_TLS_VERSION}")
-    if(NOT tls_version MATCHES "${tls_version_regex}")
-      message(FATAL_ERROR "CMAKE_TLS_VERSION '${tls_version}' not known")
-    endif()
-  elseif(NOT "x$ENV{CMAKE_TLS_VERSION}" STREQUAL "x")
-    set(tls_version "$ENV{CMAKE_TLS_VERSION}")
-    if(NOT tls_version MATCHES "${tls_version_regex}")
-      message(FATAL_ERROR "ENV{CMAKE_TLS_VERSION} '${tls_version}' not known")
-    endif()
-  endif()
-  set("${tls_version_var}" "${tls_version}" PARENT_SCOPE)
-endfunction()
-
-function(_ep_get_tls_verify name tls_verify_var)
-  get_property(tls_verify TARGET ${name} PROPERTY _EP_TLS_VERIFY)
-  if("x${tls_verify}" STREQUAL "x")
-    if(NOT "x${CMAKE_TLS_VERIFY}" STREQUAL "x")
-      set(tls_verify "${CMAKE_TLS_VERIFY}")
-    elseif(NOT "x$ENV{CMAKE_TLS_VERIFY}" STREQUAL "x")
-      set(tls_verify "$ENV{CMAKE_TLS_VERIFY}")
-    endif()
-  endif()
-  set("${tls_verify_var}" "${tls_verify}" PARENT_SCOPE)
-endfunction()
-
-function(_ep_get_tls_cainfo name tls_cainfo_var)
-  get_property(tls_cainfo TARGET ${name} PROPERTY _EP_TLS_CAINFO)
-  if("x${tls_cainfo}" STREQUAL "x" AND DEFINED CMAKE_TLS_CAINFO)
-    set(tls_cainfo "${CMAKE_TLS_CAINFO}")
-  endif()
-  set("${tls_cainfo_var}" "${tls_cainfo}" PARENT_SCOPE)
-endfunction()
-
-function(_ep_get_netrc name netrc_var)
-  get_property(netrc TARGET ${name} PROPERTY _EP_NETRC)
-  if("x${netrc}" STREQUAL "x" AND DEFINED CMAKE_NETRC)
-    set(netrc "${CMAKE_NETRC}")
-  endif()
-  set("${netrc_var}" "${netrc}" PARENT_SCOPE)
-endfunction()
-
-function(_ep_get_netrc_file name netrc_file_var)
-  get_property(netrc_file TARGET ${name} PROPERTY _EP_NETRC_FILE)
-  if("x${netrc_file}" STREQUAL "x" AND DEFINED CMAKE_NETRC_FILE)
-    set(netrc_file "${CMAKE_NETRC_FILE}")
-  endif()
-  set("${netrc_file_var}" "${netrc_file}" PARENT_SCOPE)
-endfunction()
-
-function(_ep_write_gitclone_script
-  script_filename
-  source_dir
-  git_EXECUTABLE
-  git_repository
-  git_tag
-  git_remote_name
-  init_submodules
-  git_submodules_recurse
-  git_submodules
-  git_shallow
-  git_progress
-  git_config
-  src_name
-  work_dir
-  gitclone_infofile
-  gitclone_stampfile
-  tls_version
-  tls_verify
-)
-
-  if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5)
-    # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path.
-    set(git_checkout_explicit-- "--")
-  else()
-    # Use `git checkout <branch>` even though this risks ambiguity with a
-    # local path.  Unfortunately we cannot use `git checkout <tree-ish> --`
-    # because that will not search for remote branch names, a common use case.
-    set(git_checkout_explicit-- "")
-  endif()
-  if("${git_tag}" STREQUAL "")
-    message(FATAL_ERROR "Tag for git checkout should not be empty.")
-  endif()
-
-  if(GIT_VERSION_STRING VERSION_LESS 2.20 OR
-     2.21 VERSION_LESS_EQUAL GIT_VERSION_STRING)
-    set(git_clone_options "--no-checkout")
-  else()
-    set(git_clone_options)
-  endif()
-  if(git_shallow)
-    if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10)
-      list(APPEND git_clone_options "--depth 1 --no-single-branch")
-    else()
-      list(APPEND git_clone_options "--depth 1")
-    endif()
-  endif()
-  if(git_progress)
-    list(APPEND git_clone_options --progress)
-  endif()
-  foreach(config IN LISTS git_config)
-    list(APPEND git_clone_options --config \"${config}\")
-  endforeach()
-  if(NOT ${git_remote_name} STREQUAL "origin")
-    list(APPEND git_clone_options --origin \"${git_remote_name}\")
-  endif()
-
-  # The clone config option is sticky, it will apply to all subsequent git
-  # update operations. The submodules config option is not sticky, because
-  # git doesn't provide any way to do that. Thus, we will have to pass the
-  # same config option in the update step too for submodules, but not for
-  # the main git repo.
-  set(git_submodules_config_options "")
-  if(NOT "x${tls_version}" STREQUAL "x")
-    list(APPEND git_clone_options -c http.sslVersion=tlsv${tls_version})
-    list(APPEND git_submodules_config_options -c http.sslVersion=tlsv${tls_version})
-  endif()
-  if(NOT "x${tls_verify}" STREQUAL "x")
-    if(tls_verify)
-      # Default git behavior is "true", but the user might have changed the
-      # global default to "false". Since TLS_VERIFY was given, ensure we honor
-      # the specified setting regardless of what the global default might be.
-      list(APPEND git_clone_options -c http.sslVerify=true)
-      list(APPEND git_submodules_config_options -c http.sslVerify=true)
-    else()
-      list(APPEND git_clone_options -c http.sslVerify=false)
-      list(APPEND git_submodules_config_options -c http.sslVerify=false)
-    endif()
-  endif()
-
-  string (REPLACE ";" " " git_clone_options "${git_clone_options}")
-
-  configure_file(
-    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitclone.cmake.in
-    ${script_filename}
-    @ONLY
-  )
-endfunction()
-
-function(_ep_write_hgclone_script
-  script_filename
-  source_dir
-  hg_EXECUTABLE
-  hg_repository
-  hg_tag
-  src_name
-  work_dir
-  hgclone_infofile
-  hgclone_stampfile
-)
-
-  if("${hg_tag}" STREQUAL "")
-    message(FATAL_ERROR "Tag for hg checkout should not be empty.")
-  endif()
-
-  configure_file(
-    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/hgclone.cmake.in
-    ${script_filename}
-    @ONLY
-  )
-endfunction()
-
-
-function(_ep_write_gitupdate_script
-  script_filename
-  git_EXECUTABLE
-  git_tag
-  git_remote_name
-  init_submodules
-  git_submodules_recurse
-  git_submodules
-  git_repository
-  work_dir
-  git_update_strategy
-  tls_version
-  tls_verify
-)
-
-  if("${git_tag}" STREQUAL "")
-    message(FATAL_ERROR "Tag for git checkout should not be empty.")
-  endif()
-  set(git_stash_save_options --quiet)
-  if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7)
-    # This avoids stashing files covered by .gitignore
-    list(APPEND git_stash_save_options --include-untracked)
-  elseif(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.6)
-    # Untracked files, but also ignored files, so potentially slower
-    list(APPEND git_stash_save_options --all)
-  endif()
-
-  # The submodules config option is not sticky, git doesn't provide any way
-  # to do that. We have to pass this config option for the update step too.
-  # We don't need to set it for the non-submodule update because it gets
-  # recorded as part of the clone operation in a sticky manner.
-  set(git_submodules_config_options "")
-  if(NOT "x${tls_version}" STREQUAL "x")
-    list(APPEND git_submodules_config_options -c http.sslVersion=tlsv${tls_version})
-  endif()
-  if(NOT "x${tls_verify}" STREQUAL "x")
-    if(tls_verify)
-      # Default git behavior is "true", but the user might have changed the
-      # global default to "false". Since TLS_VERIFY was given, ensure we honor
-      # the specified setting regardless of what the global default might be.
-      list(APPEND git_submodules_config_options -c http.sslVerify=true)
-    else()
-      list(APPEND git_submodules_config_options -c http.sslVerify=false)
-    endif()
-  endif()
-
-  configure_file(
-      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/gitupdate.cmake.in"
-      "${script_filename}"
-      @ONLY
-  )
-endfunction()
-
-function(_ep_write_downloadfile_script
-  script_filename
-  REMOTE
-  LOCAL
-  timeout
-  inactivity_timeout
-  no_progress
-  hash
-  tls_version
-  tls_verify
-  tls_cainfo
-  userpwd
-  http_headers
-  netrc
-  netrc_file
-)
-  if("x${REMOTE}" STREQUAL "x")
-    message(FATAL_ERROR "REMOTE can't be empty")
-  endif()
-  if("x${LOCAL}" STREQUAL "x")
-    message(FATAL_ERROR "LOCAL can't be empty")
-  endif()
-
-  # REMOTE could contain special characters that parse as separate arguments.
-  # Things like parentheses are legitimate characters in a URL, but would be
-  # seen as the start of a new unquoted argument by the cmake language parser.
-  # Avoid those special cases by preparing quoted strings for direct inclusion
-  # in the foreach() call that iterates over the set of URLs in REMOTE.
-  set(REMOTE "[====[${REMOTE}]====]")
-  string(REPLACE ";" "]====] [====[" REMOTE "${REMOTE}")
-
-  if(timeout)
-    set(TIMEOUT_ARGS TIMEOUT ${timeout})
-    set(TIMEOUT_MSG "${timeout} seconds")
-  else()
-    set(TIMEOUT_ARGS "# no TIMEOUT")
-    set(TIMEOUT_MSG "none")
-  endif()
-  if(inactivity_timeout)
-    set(INACTIVITY_TIMEOUT_ARGS INACTIVITY_TIMEOUT ${inactivity_timeout})
-    set(INACTIVITY_TIMEOUT_MSG "${inactivity_timeout} seconds")
-  else()
-    set(INACTIVITY_TIMEOUT_ARGS "# no INACTIVITY_TIMEOUT")
-    set(INACTIVITY_TIMEOUT_MSG "none")
-  endif()
-
-  if(no_progress)
-    set(SHOW_PROGRESS "")
-  else()
-    set(SHOW_PROGRESS "SHOW_PROGRESS")
-  endif()
-
-  _ep_get_hash_regex(_ep_hash_regex)
-  if("${hash}" MATCHES "${_ep_hash_regex}")
-    set(ALGO "${CMAKE_MATCH_1}")
-    string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE)
-  else()
-    set(ALGO "")
-    set(EXPECT_VALUE "")
-  endif()
-
-  set(TLS_VERSION_CODE "")
-  if(NOT "x${tls_version}" STREQUAL "x")
-    set(TLS_VERSION_CODE "set(CMAKE_TLS_VERSION \"${tls_version}\")")
-  endif()
-
-  set(TLS_VERIFY_CODE "")
-  if(NOT "x${tls_verify}" STREQUAL "x")
-    set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY \"${tls_verify}\")")
-  endif()
-
-  set(TLS_CAINFO_CODE "")
-  if(NOT "x${tls_cainfo}" STREQUAL "x")
-    set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${tls_cainfo}\")")
-  endif()
-
-  set(NETRC_CODE "")
-  if(NOT "x${netrc}" STREQUAL "x")
-    set(NETRC_CODE "set(CMAKE_NETRC \"${netrc}\")")
-  endif()
-
-  set(NETRC_FILE_CODE "")
-  if(NOT "x${netrc_file}" STREQUAL "x")
-    set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${netrc_file}\")")
-  endif()
-
-  if(userpwd STREQUAL ":")
-    set(USERPWD_ARGS)
-  else()
-    set(USERPWD_ARGS USERPWD "${userpwd}")
-  endif()
-
-  set(HTTP_HEADERS_ARGS "")
-  if(NOT http_headers STREQUAL "")
-    foreach(header IN LISTS http_headers)
-      string(PREPEND HTTP_HEADERS_ARGS
-        "HTTPHEADER \"${header}\"\n        "
-      )
-    endforeach()
-  endif()
-
-  # Used variables:
-  # * TLS_VERSION_CODE
-  # * TLS_VERIFY_CODE
-  # * TLS_CAINFO_CODE
-  # * ALGO
-  # * EXPECT_VALUE
-  # * REMOTE
-  # * LOCAL
-  # * SHOW_PROGRESS
-  # * TIMEOUT_ARGS
-  # * TIMEOUT_MSG
-  # * USERPWD_ARGS
-  # * HTTP_HEADERS_ARGS
-  configure_file(
-    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/download.cmake.in"
-    "${script_filename}"
-    @ONLY
-  )
-endfunction()
-
-function(_ep_write_verifyfile_script
-  script_filename
-  LOCAL
-  hash
-)
-  _ep_get_hash_regex(_ep_hash_regex)
-  if("${hash}" MATCHES "${_ep_hash_regex}")
-    set(ALGO "${CMAKE_MATCH_1}")
-    string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE)
-  else()
-    set(ALGO "")
-    set(EXPECT_VALUE "")
-  endif()
-
-  # Used variables:
-  # * ALGO
-  # * EXPECT_VALUE
-  # * LOCAL
-  configure_file(
-    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/verify.cmake.in"
-    "${script_filename}"
-    @ONLY
-  )
-endfunction()
-
-
-function(_ep_write_extractfile_script
-  script_filename
-  name
-  filename
-  directory options
-)
-  set(args "")
-
-  if(filename MATCHES
-     "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$")
-    set(args xfz)
-  endif()
-
-  if(filename MATCHES "(\\.|=)tar$")
-    set(args xf)
-  endif()
-
-  if(args STREQUAL "")
-    message(FATAL_ERROR
-      "Do not know how to extract '${filename}' -- known types are: "
-      ".7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip"
-    )
-  endif()
-
-  configure_file(
-    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/extractfile.cmake.in"
-    "${script_filename}"
-    @ONLY
-  )
-endfunction()
-
-
 function(_ep_set_directories name)
   get_property(prefix TARGET ${name} PROPERTY _EP_PREFIX)
   if(NOT prefix)
@@ -1834,6 +1354,7 @@
     endif()
     file(TO_CMAKE_PATH "${${var}_dir}" ${var}_dir)
     set_property(TARGET ${name} PROPERTY _EP_${VAR}_DIR "${${var}_dir}")
+    set(_EP_${VAR}_DIR "${${var}_dir}" PARENT_SCOPE)
   endforeach()
 
   # Special case for default log directory based on stamp directory.
@@ -1846,10 +1367,12 @@
   endif()
   file(TO_CMAKE_PATH "${log_dir}" log_dir)
   set_property(TARGET ${name} PROPERTY _EP_LOG_DIR "${log_dir}")
+  set(_EP_LOG_DIR "${log_dir}" PARENT_SCOPE)
 
   get_property(source_subdir TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR)
   if(NOT source_subdir)
     set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "")
+    set(_EP_SOURCE_SUBDIR "" PARENT_SCOPE)
   elseif(IS_ABSOLUTE "${source_subdir}")
     message(FATAL_ERROR
       "External project ${name} has non-relative SOURCE_SUBDIR!"
@@ -1859,6 +1382,7 @@
     # behaves as expected.
     file(TO_CMAKE_PATH "${source_subdir}" source_subdir)
     set_property(TARGET ${name} PROPERTY _EP_SOURCE_SUBDIR "/${source_subdir}")
+    set(_EP_SOURCE_SUBDIR "/${source_subdir}" PARENT_SCOPE)
   endif()
   if(build_in_source)
     get_property(source_dir TARGET ${name} PROPERTY _EP_SOURCE_DIR)
@@ -1866,10 +1390,12 @@
       set_property(TARGET ${name} PROPERTY
         _EP_BINARY_DIR "${source_dir}/${source_subdir}"
       )
+      set(_EP_BINARY_DIR "${source_dir}/${source_subdir}" PARENT_SCOPE)
     else()
       set_property(TARGET ${name} PROPERTY
         _EP_BINARY_DIR "${source_dir}"
       )
+      set(_EP_BINARY_DIR "${source_dir}" PARENT_SCOPE)
     endif()
   endif()
 
@@ -2128,6 +1654,7 @@
   set(${cmd_var} "${cmd}" PARENT_SCOPE)
 endfunction()
 
+
 function(_ep_write_log_script
   name
   step
@@ -2291,13 +1818,23 @@
   set(${cmd_var} "${command}" PARENT_SCOPE)
 endfunction()
 
+
 # On multi-config generators, provide a placeholder for a per-config subdir.
 # On single-config generators, this is empty.
 function(_ep_get_configuration_subdir_genex suffix_var)
   set(suffix "")
   get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
   if(_isMultiConfig)
-    set(suffix "/$<CONFIG>")
+    if(CMAKE_GENERATOR STREQUAL "Xcode")
+      # The Xcode generator does not support per-config sources,
+      # so use the underlying build system's placeholder instead.
+      # FIXME(#23652): We have no test for the use case requiring
+      # CMAKE_CFG_INTDIR for XCODE_EMIT_EFFECTIVE_PLATFORM_NAME,
+      # but $<CONFIG> does not work.
+      set(suffix "/${CMAKE_CFG_INTDIR}")
+    else()
+      set(suffix "/$<CONFIG>")
+    endif()
   endif()
   set(${suffix_var} "${suffix}" PARENT_SCOPE)
 endfunction()
@@ -2477,6 +2014,10 @@
 endfunction()
 
 
+# While this function is referenced in shared_internal_commands.cmake in a few
+# places, all of those code paths will only be reached by calling one of the
+# functions defined in this file. Keep it here, since it is part of the public
+# interface of ExternalProject.
 function(ExternalProject_Add_Step name step)
   get_property(cmp0114 TARGET ${name} PROPERTY _EP_CMP0114)
   _ep_get_complete_stampfile(${name} complete_stamp_file)
@@ -2866,908 +2407,6 @@
 endfunction()
 
 
-function(_ep_is_dir_empty dir empty_var)
-  file(GLOB gr "${dir}/*")
-  if("${gr}" STREQUAL "")
-    set(${empty_var} 1 PARENT_SCOPE)
-  else()
-    set(${empty_var} 0 PARENT_SCOPE)
-  endif()
-endfunction()
-
-function(_ep_get_git_submodules_recurse git_submodules_recurse)
-  # Checks for GIT_SUBMODULES_RECURSE property. Default is ON, which sets
-  # git_submodules_recurse output variable to "--recursive". Otherwise, the
-  # output variable is set to an empty value "".
-  get_property(git_submodules_recurse_set
-    TARGET ${name}
-    PROPERTY _EP_GIT_SUBMODULES_RECURSE
-    SET
-  )
-  if(NOT git_submodules_recurse_set)
-    set(recurseFlag "--recursive")
-  else()
-    get_property(git_submodules_recurse_value
-      TARGET ${name}
-      PROPERTY _EP_GIT_SUBMODULES_RECURSE
-    )
-    if(git_submodules_recurse_value)
-      set(recurseFlag "--recursive")
-    else()
-      set(recurseFlag "")
-    endif()
-  endif()
-  set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE)
-
-  # The git submodule update '--recursive' flag requires git >= v1.6.5
-  if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5)
-    message(FATAL_ERROR
-      "git version 1.6.5 or later required for --recursive flag with "
-      "'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'"
-    )
-  endif()
-endfunction()
-
-
-function(_ep_add_download_command name)
-  ExternalProject_Get_Property(${name}
-    source_dir
-    stamp_dir
-    download_dir
-    tmp_dir
-  )
-
-  get_property(cmd_set TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND SET)
-  get_property(cmd TARGET ${name} PROPERTY _EP_DOWNLOAD_COMMAND)
-  get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY)
-  get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY)
-  get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY)
-  get_property(hg_repository  TARGET ${name} PROPERTY _EP_HG_REPOSITORY )
-  get_property(url TARGET ${name} PROPERTY _EP_URL)
-  get_property(fname TARGET ${name} PROPERTY _EP_DOWNLOAD_NAME)
-
-  # TODO: Perhaps file:// should be copied to download dir before extraction.
-  string(REGEX REPLACE "file://" "" url "${url}")
-
-  set(depends)
-  set(comment)
-  set(work_dir)
-  set(extra_repo_info)
-
-  if(cmd_set)
-    set(work_dir ${download_dir})
-    set(method custom)
-  elseif(cvs_repository)
-    set(method cvs)
-    find_package(CVS QUIET)
-    if(NOT CVS_EXECUTABLE)
-      message(FATAL_ERROR "error: could not find cvs for checkout of ${name}")
-    endif()
-
-    get_target_property(cvs_module ${name} _EP_CVS_MODULE)
-    if(NOT cvs_module)
-      message(FATAL_ERROR "error: no CVS_MODULE")
-    endif()
-
-    get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG)
-    get_filename_component(src_name "${source_dir}" NAME)
-    get_filename_component(work_dir "${source_dir}" PATH)
-    set(comment "Performing download step (CVS checkout) for '${name}'")
-    set(cmd
-      ${CVS_EXECUTABLE}
-      -d ${cvs_repository}
-      -q
-      co ${cvs_tag}
-      -d ${src_name}
-      ${cvs_module}
-    )
-
-  elseif(svn_repository)
-    set(method svn)
-    find_package(Subversion QUIET)
-    if(NOT Subversion_SVN_EXECUTABLE)
-      message(FATAL_ERROR "error: could not find svn for checkout of ${name}")
-    endif()
-
-    get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION)
-    get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME)
-    get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD)
-    get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT)
-    get_property(uses_terminal
-      TARGET ${name}
-      PROPERTY _EP_USES_TERMINAL_DOWNLOAD
-    )
-    # The --trust-server-cert option requires --non-interactive
-    if(uses_terminal AND NOT svn_trust_cert)
-      set(svn_interactive_args "")
-    else()
-      set(svn_interactive_args "--non-interactive")
-    endif()
-
-    get_filename_component(src_name "${source_dir}" NAME)
-    get_filename_component(work_dir "${source_dir}" PATH)
-    set(comment "Performing download step (SVN checkout) for '${name}'")
-    set(svn_user_pw_args "")
-    if(DEFINED svn_username)
-      set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}")
-    endif()
-    if(DEFINED svn_password)
-      set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}")
-    endif()
-    if(svn_trust_cert)
-      set(svn_trust_cert_args --trust-server-cert)
-    endif()
-    set(cmd
-      ${Subversion_SVN_EXECUTABLE}
-      co
-      ${svn_repository}
-      ${svn_revision}
-      ${svn_interactive_args}
-      ${svn_trust_cert_args}
-      ${svn_user_pw_args}
-      ${src_name}
-    )
-
-  elseif(git_repository)
-    set(method git)
-    # FetchContent gives us these directly, so don't try to recompute them
-    if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING)
-      unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
-      find_package(Git QUIET)
-      if(NOT GIT_EXECUTABLE)
-        message(FATAL_ERROR "error: could not find git for clone of ${name}")
-      endif()
-    endif()
-
-    _ep_get_git_submodules_recurse(git_submodules_recurse)
-
-    get_property(git_tag TARGET ${name} PROPERTY _EP_GIT_TAG)
-    if(NOT git_tag)
-      set(git_tag "master")
-    endif()
-
-    set(git_init_submodules TRUE)
-    get_property(git_submodules_set
-      TARGET ${name}
-      PROPERTY _EP_GIT_SUBMODULES SET
-    )
-    if(git_submodules_set)
-      get_property(git_submodules TARGET ${name} PROPERTY _EP_GIT_SUBMODULES)
-      if(git_submodules  STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
-        set(git_init_submodules FALSE)
-      endif()
-    endif()
-
-    get_property(git_remote_name TARGET ${name} PROPERTY _EP_GIT_REMOTE_NAME)
-    if(NOT git_remote_name)
-      set(git_remote_name "origin")
-    endif()
-
-    _ep_get_tls_version(${name} tls_version)
-    _ep_get_tls_verify(${name} tls_verify)
-    get_property(git_shallow TARGET ${name} PROPERTY _EP_GIT_SHALLOW)
-    get_property(git_progress TARGET ${name} PROPERTY _EP_GIT_PROGRESS)
-    get_property(git_config TARGET ${name} PROPERTY _EP_GIT_CONFIG)
-
-    # If git supports it, make checkouts quiet when checking out a git hash.
-    # This avoids the very noisy detached head message.
-    if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7)
-      list(PREPEND git_config advice.detachedHead=false)
-    endif()
-
-    # The command doesn't expose any details, so we need to record additional
-    # information in the RepositoryInfo.txt file. For the download step, only
-    # the things specifically affecting the clone operation should be recorded.
-    # If the repo changes, the clone script should be run again.
-    # But if only the tag changes, avoid running the clone script again.
-    # Let the 'always' running update step checkout the new tag.
-    #
-    set(extra_repo_info
-"repository=${git_repository}
-remote=${git_remote_name}
-init_submodules=${git_init_submodules}
-recurse_submodules=${git_submodules_recurse}
-submodules=${git_submodules}
-CMP0097=${_EP_CMP0097}
-")
-    get_filename_component(src_name "${source_dir}" NAME)
-    get_filename_component(work_dir "${source_dir}" PATH)
-
-    # Since git clone doesn't succeed if the non-empty source_dir exists,
-    # create a cmake script to invoke as download command.
-    # The script will delete the source directory and then call git clone.
-    #
-    _ep_write_gitclone_script(
-      ${tmp_dir}/${name}-gitclone.cmake
-      ${source_dir}
-      ${GIT_EXECUTABLE}
-      ${git_repository}
-      ${git_tag}
-      ${git_remote_name}
-      ${git_init_submodules}
-      "${git_submodules_recurse}"
-      "${git_submodules}"
-      "${git_shallow}"
-      "${git_progress}"
-      "${git_config}"
-      ${src_name}
-      ${work_dir}
-      ${stamp_dir}/${name}-gitinfo.txt
-      ${stamp_dir}/${name}-gitclone-lastrun.txt
-      "${tls_version}"
-      "${tls_verify}"
-    )
-    set(comment "Performing download step (git clone) for '${name}'")
-    set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-gitclone.cmake)
-
-  elseif(hg_repository)
-    set(method hg)
-    find_package(Hg QUIET)
-    if(NOT HG_EXECUTABLE)
-      message(FATAL_ERROR "error: could not find hg for clone of ${name}")
-    endif()
-
-    get_property(hg_tag TARGET ${name} PROPERTY _EP_HG_TAG)
-    if(NOT hg_tag)
-      set(hg_tag "tip")
-    endif()
-
-    # The command doesn't expose any details, so we need to record additional
-    # information in the RepositoryInfo.txt file. For the download step, only
-    # the things specifically affecting the clone operation should be recorded.
-    # If the repo changes, the clone script should be run again.
-    # But if only the tag changes, avoid running the clone script again.
-    # Let the 'always' running update step checkout the new tag.
-    #
-    set(extra_repo_info "repository=${hg_repository}")
-    get_filename_component(src_name "${source_dir}" NAME)
-    get_filename_component(work_dir "${source_dir}" PATH)
-
-    # Since hg clone doesn't succeed if the non-empty source_dir exists,
-    # create a cmake script to invoke as download command.
-    # The script will delete the source directory and then call hg clone.
-    #
-    _ep_write_hgclone_script(
-      ${tmp_dir}/${name}-hgclone.cmake
-      ${source_dir}
-      ${HG_EXECUTABLE}
-      ${hg_repository}
-      ${hg_tag}
-      ${src_name}
-      ${work_dir}
-      ${stamp_dir}/${name}-hginfo.txt
-      ${stamp_dir}/${name}-hgclone-lastrun.txt
-    )
-    set(comment "Performing download step (hg clone) for '${name}'")
-    set(cmd ${CMAKE_COMMAND} -P ${tmp_dir}/${name}-hgclone.cmake)
-
-  elseif(url)
-    set(method url)
-    get_filename_component(work_dir "${source_dir}" PATH)
-    get_property(hash TARGET ${name} PROPERTY _EP_URL_HASH)
-    _ep_get_hash_regex(_ep_hash_regex)
-    if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}")
-      _ep_get_hash_algos(_ep_hash_algos)
-      list(JOIN _ep_hash_algos "|" _ep_hash_algos)
-      message(FATAL_ERROR
-        "URL_HASH is set to\n"
-        "  ${hash}\n"
-        "but must be ALGO=value where ALGO is\n"
-        "  ${_ep_hash_algos}\n"
-        "and value is a hex string."
-      )
-    endif()
-    get_property(md5 TARGET ${name} PROPERTY _EP_URL_MD5)
-    if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}")
-      message(FATAL_ERROR
-        "URL_MD5 is set to\n"
-        "  ${md5}\n"
-        "but must be a hex string."
-      )
-    endif()
-    if(md5 AND NOT hash)
-      set(hash "MD5=${md5}")
-    endif()
-    set(extra_repo_info
-"url(s)=${url}
-hash=${hash}
-")
-
-    list(LENGTH url url_list_length)
-    if(NOT "${url_list_length}" STREQUAL "1")
-      foreach(entry IN LISTS url)
-        if(NOT "${entry}" MATCHES "^[a-z]+://")
-          message(FATAL_ERROR
-            "At least one entry of URL is a path (invalid in a list)"
-          )
-        endif()
-      endforeach()
-      if("x${fname}" STREQUAL "x")
-        list(GET url 0 fname)
-      endif()
-    endif()
-
-    if(IS_DIRECTORY "${url}")
-      get_filename_component(abs_dir "${url}" ABSOLUTE)
-      set(comment "Performing download step (DIR copy) for '${name}'")
-      set(cmd
-        ${CMAKE_COMMAND} -E rm -rf ${source_dir}
-        COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}
-      )
-    else()
-      get_property(no_extract
-        TARGET "${name}"
-        PROPERTY _EP_DOWNLOAD_NO_EXTRACT
-      )
-      string(APPEND extra_repo_info "no_extract=${no_extract}\n")
-      if("${url}" MATCHES "^[a-z]+://")
-        # TODO: Should download and extraction be different steps?
-        if("x${fname}" STREQUAL "x")
-          set(fname "${url}")
-        endif()
-        set(ext_regex [[7z|tar|tar\.bz2|tar\.gz|tar\.xz|tbz2|tgz|txz|zip]])
-        if("${fname}" MATCHES "([^/\\?#]+(\\.|=)(${ext_regex}))([/?#].*)?$")
-          set(fname "${CMAKE_MATCH_1}")
-        elseif(no_extract)
-          get_filename_component(fname "${fname}" NAME)
-        else()
-          # Fall back to a default file name.  The actual file name does not
-          # matter because it is used only internally and our extraction tool
-          # inspects the file content directly.  If it turns out the wrong URL
-          # was given that will be revealed during the build which is an easier
-          # place for users to diagnose than an error here anyway.
-          set(fname "archive.tar")
-        endif()
-        string(REPLACE ";" "-" fname "${fname}")
-        set(file ${download_dir}/${fname})
-        get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT)
-        get_property(inactivity_timeout
-          TARGET ${name}
-          PROPERTY _EP_INACTIVITY_TIMEOUT
-        )
-        get_property(no_progress
-          TARGET ${name}
-          PROPERTY _EP_DOWNLOAD_NO_PROGRESS
-        )
-        _ep_get_tls_version(${name} tls_version)
-        _ep_get_tls_verify(${name} tls_verify)
-        _ep_get_tls_cainfo(${name} tls_cainfo)
-        _ep_get_netrc(${name} netrc)
-        _ep_get_netrc_file(${name} netrc_file)
-        get_property(http_username TARGET ${name} PROPERTY _EP_HTTP_USERNAME)
-        get_property(http_password TARGET ${name} PROPERTY _EP_HTTP_PASSWORD)
-        get_property(http_headers TARGET ${name} PROPERTY _EP_HTTP_HEADER)
-        set(download_script "${stamp_dir}/download-${name}.cmake")
-        _ep_write_downloadfile_script(
-          "${download_script}"
-          "${url}"
-          "${file}"
-          "${timeout}"
-          "${inactivity_timeout}"
-          "${no_progress}"
-          "${hash}"
-          "${tls_version}"
-          "${tls_verify}"
-          "${tls_cainfo}"
-          "${http_username}:${http_password}"
-          "${http_headers}"
-          "${netrc}"
-          "${netrc_file}"
-        )
-        set(cmd
-          ${CMAKE_COMMAND} -P "${download_script}"
-          COMMAND
-        )
-        if (no_extract)
-          set(steps "download and verify")
-        else ()
-          set(steps "download, verify and extract")
-        endif ()
-        set(comment "Performing download step (${steps}) for '${name}'")
-        # already verified by 'download_script'
-        file(WRITE "${stamp_dir}/verify-${name}.cmake" "")
-
-        # Rather than adding everything to the RepositoryInfo.txt file, it is
-        # more robust to just depend on the download script. That way, we will
-        # re-download if any aspect of the download changes.
-        list(APPEND depends "${download_script}")
-      else()
-        set(file "${url}")
-        if (no_extract)
-          set(steps "verify")
-        else ()
-          set(steps "verify and extract")
-        endif ()
-        set(comment "Performing download step (${steps}) for '${name}'")
-        _ep_write_verifyfile_script(
-          "${stamp_dir}/verify-${name}.cmake"
-          "${file}"
-          "${hash}"
-        )
-      endif()
-      list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake)
-      get_target_property(extract_timestamp ${name}
-        _EP_DOWNLOAD_EXTRACT_TIMESTAMP
-      )
-      if(no_extract)
-        if(NOT extract_timestamp STREQUAL "extract_timestamp-NOTFOUND")
-          message(FATAL_ERROR
-            "Cannot specify DOWNLOAD_EXTRACT_TIMESTAMP when using "
-            "DOWNLOAD_NO_EXTRACT TRUE"
-          )
-        endif()
-        set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file})
-      else()
-        if(extract_timestamp STREQUAL "extract_timestamp-NOTFOUND")
-          # Default depends on policy CMP0135
-          if(_EP_CMP0135 STREQUAL "")
-            message(AUTHOR_WARNING
-              "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy "
-              "CMP0135 is not set. The policy's OLD behavior will be used. "
-              "When using a URL download, the timestamps of extracted files "
-              "should preferably be that of the time of extraction, otherwise "
-              "code that depends on the extracted contents might not be "
-              "rebuilt if the URL changes. The OLD behavior preserves the "
-              "timestamps from the archive instead, but this is usually not "
-              "what you want. Update your project to the NEW behavior or "
-              "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of "
-              "true to avoid this robustness issue."
-            )
-            set(extract_timestamp TRUE)
-          elseif(_EP_CMP0135 STREQUAL "NEW")
-            set(extract_timestamp FALSE)
-          else()
-            set(extract_timestamp TRUE)
-          endif()
-        endif()
-        if(extract_timestamp)
-          set(options "")
-        else()
-          set(options "--touch")
-        endif()
-        _ep_write_extractfile_script(
-          "${stamp_dir}/extract-${name}.cmake"
-          "${name}"
-          "${file}"
-          "${source_dir}"
-          "${options}"
-        )
-        list(APPEND cmd
-          COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake
-        )
-      endif ()
-    endif()
-  else()
-    set(method source_dir)
-    _ep_is_dir_empty("${source_dir}" empty)
-    if(${empty})
-      message(FATAL_ERROR
-        "No download info given for '${name}' and its source directory:\n"
-        " ${source_dir}\n"
-        "is not an existing non-empty directory.  Please specify one of:\n"
-        " * SOURCE_DIR with an existing non-empty directory\n"
-        " * DOWNLOAD_COMMAND\n"
-        " * URL\n"
-        " * GIT_REPOSITORY\n"
-        " * SVN_REPOSITORY\n"
-        " * HG_REPOSITORY\n"
-        " * CVS_REPOSITORY and CVS_MODULE"
-      )
-    endif()
-  endif()
-
-  # We use configure_file() to write the repo_info_file so that the file's
-  # timestamp is not updated if we don't change the contents
-
-  set(repo_info_file ${stamp_dir}/${name}-${method}info.txt)
-  list(APPEND depends ${repo_info_file})
-  configure_file(
-    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/RepositoryInfo.txt.in"
-    "${repo_info_file}"
-    @ONLY
-  )
-
-  get_property(log
-    TARGET ${name}
-    PROPERTY _EP_LOG_DOWNLOAD
-  )
-  if(log)
-    set(log LOG 1)
-  else()
-    set(log "")
-  endif()
-
-  get_property(uses_terminal
-    TARGET ${name}
-    PROPERTY _EP_USES_TERMINAL_DOWNLOAD
-  )
-  if(uses_terminal)
-    set(uses_terminal USES_TERMINAL 1)
-  else()
-    set(uses_terminal "")
-  endif()
-
-  set(__cmdQuoted)
-  foreach(__item IN LISTS cmd)
-    string(APPEND __cmdQuoted " [==[${__item}]==]")
-  endforeach()
-  cmake_language(EVAL CODE "
-    ExternalProject_Add_Step(\${name} download
-      INDEPENDENT TRUE
-      COMMENT \${comment}
-      COMMAND ${__cmdQuoted}
-      WORKING_DIRECTORY \${work_dir}
-      DEPENDS \${depends}
-      DEPENDEES mkdir
-      ${log}
-      ${uses_terminal}
-    )"
-  )
-endfunction()
-
-function(_ep_get_update_disconnected var name)
-  get_property(update_disconnected_set
-    TARGET ${name}
-    PROPERTY _EP_UPDATE_DISCONNECTED
-    SET
-  )
-  if(update_disconnected_set)
-    get_property(update_disconnected
-      TARGET ${name}
-      PROPERTY _EP_UPDATE_DISCONNECTED
-    )
-  else()
-    get_property(update_disconnected
-      DIRECTORY
-      PROPERTY EP_UPDATE_DISCONNECTED
-    )
-  endif()
-  set(${var} "${update_disconnected}" PARENT_SCOPE)
-endfunction()
-
-function(_ep_add_update_command name)
-  ExternalProject_Get_Property(${name} source_dir stamp_dir tmp_dir)
-
-  get_property(cmd_set TARGET ${name} PROPERTY _EP_UPDATE_COMMAND SET)
-  get_property(cmd TARGET ${name} PROPERTY _EP_UPDATE_COMMAND)
-  get_property(cvs_repository TARGET ${name} PROPERTY _EP_CVS_REPOSITORY)
-  get_property(svn_repository TARGET ${name} PROPERTY _EP_SVN_REPOSITORY)
-  get_property(git_repository TARGET ${name} PROPERTY _EP_GIT_REPOSITORY)
-  get_property(hg_repository  TARGET ${name} PROPERTY _EP_HG_REPOSITORY )
-
-  _ep_get_update_disconnected(update_disconnected ${name})
-
-  set(work_dir)
-  set(comment)
-  set(always)
-  set(file_deps)
-
-  if(cmd_set)
-    set(work_dir ${source_dir})
-    if(NOT "x${cmd}" STREQUAL "x")
-      set(always 1)
-    endif()
-  elseif(cvs_repository)
-    if(NOT CVS_EXECUTABLE)
-      message(FATAL_ERROR "error: could not find cvs for update of ${name}")
-    endif()
-    set(work_dir ${source_dir})
-    set(comment "Performing update step (CVS update) for '${name}'")
-    get_property(cvs_tag TARGET ${name} PROPERTY _EP_CVS_TAG)
-    set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag})
-    set(always 1)
-  elseif(svn_repository)
-    if(NOT Subversion_SVN_EXECUTABLE)
-      message(FATAL_ERROR "error: could not find svn for update of ${name}")
-    endif()
-    set(work_dir ${source_dir})
-    set(comment "Performing update step (SVN update) for '${name}'")
-    get_property(svn_revision TARGET ${name} PROPERTY _EP_SVN_REVISION)
-    get_property(svn_username TARGET ${name} PROPERTY _EP_SVN_USERNAME)
-    get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD)
-    get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT)
-    get_property(uses_terminal TARGET ${name} PROPERTY _EP_USES_TERMINAL_UPDATE)
-    # The --trust-server-cert option requires --non-interactive
-    if(uses_terminal AND NOT svn_trust_cert)
-      set(svn_interactive_args "")
-    else()
-      set(svn_interactive_args "--non-interactive")
-    endif()
-    set(svn_user_pw_args "")
-    if(DEFINED svn_username)
-      set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}")
-    endif()
-    if(DEFINED svn_password)
-      set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}")
-    endif()
-    if(svn_trust_cert)
-      set(svn_trust_cert_args --trust-server-cert)
-    endif()
-    set(cmd
-      ${Subversion_SVN_EXECUTABLE}
-      up
-      ${svn_revision}
-      ${svn_interactive_args}
-      ${svn_trust_cert_args}
-      ${svn_user_pw_args}
-    )
-    set(always 1)
-  elseif(git_repository)
-    # FetchContent gives us these directly, so don't try to recompute them
-    if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING)
-      unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
-      find_package(Git QUIET)
-      if(NOT GIT_EXECUTABLE)
-        message(FATAL_ERROR "error: could not find git for fetch of ${name}")
-      endif()
-    endif()
-    set(work_dir ${source_dir})
-    set(comment "Performing update step for '${name}'")
-    set(comment_disconnected "Performing disconnected update step for '${name}'")
-
-    get_property(git_tag
-      TARGET ${name}
-      PROPERTY _EP_GIT_TAG
-    )
-    if(NOT git_tag)
-      set(git_tag "master")
-    endif()
-
-    get_property(git_remote_name
-      TARGET ${name}
-      PROPERTY _EP_GIT_REMOTE_NAME
-    )
-    if(NOT git_remote_name)
-      set(git_remote_name "origin")
-    endif()
-
-    set(git_init_submodules TRUE)
-    get_property(git_submodules_set
-      TARGET ${name}
-      PROPERTY _EP_GIT_SUBMODULES
-      SET
-    )
-    if(git_submodules_set)
-      get_property(git_submodules
-        TARGET ${name}
-        PROPERTY _EP_GIT_SUBMODULES
-      )
-      if(git_submodules  STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
-        set(git_init_submodules FALSE)
-      endif()
-    endif()
-
-    get_property(git_update_strategy
-      TARGET ${name}
-      PROPERTY _EP_GIT_REMOTE_UPDATE_STRATEGY
-    )
-    if(NOT git_update_strategy)
-      set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}")
-    endif()
-    if(NOT git_update_strategy)
-      set(git_update_strategy REBASE)
-    endif()
-    set(strategies CHECKOUT REBASE REBASE_CHECKOUT)
-    if(NOT git_update_strategy IN_LIST strategies)
-      message(FATAL_ERROR
-        "'${git_update_strategy}' is not one of the supported strategies: "
-        "${strategies}"
-      )
-    endif()
-
-    _ep_get_git_submodules_recurse(git_submodules_recurse)
-
-    _ep_get_tls_version(${name} tls_version)
-    _ep_get_tls_verify(${name} tls_verify)
-
-    set(update_script "${tmp_dir}/${name}-gitupdate.cmake")
-    list(APPEND file_deps ${update_script})
-    _ep_write_gitupdate_script(
-      "${update_script}"
-      "${GIT_EXECUTABLE}"
-      "${git_tag}"
-      "${git_remote_name}"
-      "${git_init_submodules}"
-      "${git_submodules_recurse}"
-      "${git_submodules}"
-      "${git_repository}"
-      "${work_dir}"
-      "${git_update_strategy}"
-      "${tls_version}"
-      "${tls_verify}"
-    )
-    set(cmd              ${CMAKE_COMMAND} -Dcan_fetch=YES -P ${update_script})
-    set(cmd_disconnected ${CMAKE_COMMAND} -Dcan_fetch=NO  -P ${update_script})
-    set(always 1)
-  elseif(hg_repository)
-    if(NOT HG_EXECUTABLE)
-      message(FATAL_ERROR "error: could not find hg for pull of ${name}")
-    endif()
-    set(work_dir ${source_dir})
-    set(comment "Performing update step (hg pull) for '${name}'")
-    set(comment_disconnected "Performing disconnected update step for '${name}'")
-
-    get_property(hg_tag
-      TARGET ${name}
-      PROPERTY _EP_HG_TAG
-    )
-    if(NOT hg_tag)
-      set(hg_tag "tip")
-    endif()
-
-    if("${HG_VERSION_STRING}" STREQUAL "2.1")
-      set(notesAnchor
-        "#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X"
-      )
-      message(WARNING
-"Mercurial 2.1 does not distinguish an empty pull from a failed pull:
- http://mercurial.selenic.com/wiki/UpgradeNotes${notesAnchor}
- http://thread.gmane.org/gmane.comp.version-control.mercurial.devel/47656
-Update to Mercurial >= 2.1.1.
-")
-    endif()
-
-    set(cmd
-      ${HG_EXECUTABLE} pull
-      COMMAND ${HG_EXECUTABLE} update ${hg_tag}
-    )
-    set(cmd_disconnected ${HG_EXECUTABLE} update ${hg_tag})
-    set(always 1)
-  endif()
-
-  # We use configure_file() to write the update_info_file so that the file's
-  # timestamp is not updated if we don't change the contents
-  if(NOT DEFINED cmd_disconnected)
-    set(cmd_disconnected "${cmd}")
-  endif()
-  set(update_info_file ${stamp_dir}/${name}-update-info.txt)
-  list(APPEND file_deps ${update_info_file})
-  configure_file(
-    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/UpdateInfo.txt.in"
-    "${update_info_file}"
-    @ONLY
-  )
-
-  get_property(log
-    TARGET ${name}
-    PROPERTY _EP_LOG_UPDATE
-  )
-  if(log)
-    set(log LOG 1)
-  else()
-    set(log "")
-  endif()
-
-  get_property(uses_terminal
-    TARGET ${name}
-    PROPERTY _EP_USES_TERMINAL_UPDATE
-  )
-  if(uses_terminal)
-    set(uses_terminal USES_TERMINAL 1)
-  else()
-    set(uses_terminal "")
-  endif()
-
-  set(__cmdQuoted)
-  foreach(__item IN LISTS cmd)
-    string(APPEND __cmdQuoted " [==[${__item}]==]")
-  endforeach()
-  cmake_language(EVAL CODE "
-    ExternalProject_Add_Step(${name} update
-      INDEPENDENT TRUE
-      COMMENT \${comment}
-      COMMAND ${__cmdQuoted}
-      ALWAYS \${always}
-      EXCLUDE_FROM_MAIN \${update_disconnected}
-      WORKING_DIRECTORY \${work_dir}
-      DEPENDEES download
-      DEPENDS \${file_deps}
-      ${log}
-      ${uses_terminal}
-    )"
-  )
-  if(update_disconnected)
-    if(NOT DEFINED comment_disconnected)
-      set(comment_disconnected "${comment}")
-    endif()
-    set(__cmdQuoted)
-    foreach(__item IN LISTS cmd_disconnected)
-      string(APPEND __cmdQuoted " [==[${__item}]==]")
-    endforeach()
-
-    cmake_language(EVAL CODE "
-      ExternalProject_Add_Step(${name} update_disconnected
-        INDEPENDENT TRUE
-        COMMENT \${comment_disconnected}
-        COMMAND ${__cmdQuoted}
-        WORKING_DIRECTORY \${work_dir}
-        DEPENDEES download
-        DEPENDS \${file_deps}
-        ${log}
-        ${uses_terminal}
-      )"
-    )
-  endif()
-
-endfunction()
-
-
-function(_ep_add_patch_command name)
-  ExternalProject_Get_Property(${name} source_dir stamp_dir)
-
-  get_property(cmd_set TARGET ${name} PROPERTY _EP_PATCH_COMMAND SET)
-  get_property(cmd TARGET ${name} PROPERTY _EP_PATCH_COMMAND)
-
-  set(work_dir)
-
-  if(cmd_set)
-    set(work_dir ${source_dir})
-  endif()
-
-  # We use configure_file() to write the patch_info_file so that the file's
-  # timestamp is not updated if we don't change the contents
-  set(patch_info_file ${stamp_dir}/${name}-patch-info.txt)
-  configure_file(
-    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ExternalProject/PatchInfo.txt.in"
-    "${patch_info_file}"
-    @ONLY
-  )
-
-  get_property(log
-    TARGET ${name}
-    PROPERTY _EP_LOG_PATCH
-  )
-  if(log)
-    set(log LOG 1)
-  else()
-    set(log "")
-  endif()
-
-  get_property(uses_terminal
-    TARGET ${name}
-    PROPERTY _EP_USES_TERMINAL_PATCH
-  )
-  if(uses_terminal)
-    set(uses_terminal USES_TERMINAL 1)
-  else()
-    set(uses_terminal "")
-  endif()
-
-  _ep_get_update_disconnected(update_disconnected ${name})
-
-  set(__cmdQuoted)
-  foreach(__item IN LISTS cmd)
-    string(APPEND __cmdQuoted " [==[${__item}]==]")
-  endforeach()
-  cmake_language(EVAL CODE "
-    ExternalProject_Add_Step(${name} patch
-      INDEPENDENT TRUE
-      COMMAND ${__cmdQuoted}
-      WORKING_DIRECTORY \${work_dir}
-      EXCLUDE_FROM_MAIN \${update_disconnected}
-      DEPENDEES update
-      DEPENDS \${patch_info_file}
-      ${log}
-      ${uses_terminal}
-    )"
-  )
-
-  if(update_disconnected)
-    cmake_language(EVAL CODE "
-      ExternalProject_Add_Step(${name} patch_disconnected
-        INDEPENDENT TRUE
-        COMMAND ${__cmdQuoted}
-        WORKING_DIRECTORY \${work_dir}
-        DEPENDEES update_disconnected
-        DEPENDS \${patch_info_file}
-        ${log}
-        ${uses_terminal}
-      )"
-    )
-  endif()
-
-endfunction()
-
 function(_ep_get_file_deps var name)
   set(file_deps)
 
@@ -3795,6 +2434,7 @@
   set("${var}" "${file_deps}" PARENT_SCOPE)
 endfunction()
 
+
 function(_ep_extract_configure_command var name)
   get_property(cmd_set
     TARGET ${name}
@@ -3950,6 +2590,7 @@
   set("${var}" "${cmd}" PARENT_SCOPE)
 endfunction()
 
+
 # TODO: Make sure external projects use the proper compiler
 function(_ep_add_configure_command name)
   ExternalProject_Get_Property(${name} binary_dir tmp_dir)
@@ -4318,144 +2959,7 @@
     _EP_CMP0114 "${cmp0114}"
   )
 
-  set(keywords
-    #
-    # Directory options
-    #
-    PREFIX
-    TMP_DIR
-    STAMP_DIR
-    LOG_DIR
-    DOWNLOAD_DIR
-    SOURCE_DIR
-    BINARY_DIR
-    INSTALL_DIR
-    #
-    # Download step options
-    #
-    DOWNLOAD_COMMAND
-    #
-    URL
-    URL_HASH
-    URL_MD5
-    DOWNLOAD_NAME
-    DOWNLOAD_EXTRACT_TIMESTAMP
-    DOWNLOAD_NO_EXTRACT
-    DOWNLOAD_NO_PROGRESS
-    TIMEOUT
-    INACTIVITY_TIMEOUT
-    HTTP_USERNAME
-    HTTP_PASSWORD
-    HTTP_HEADER
-    TLS_VERSION    # Also used for git clone operations
-    TLS_VERIFY     # Also used for git clone operations
-    TLS_CAINFO
-    NETRC
-    NETRC_FILE
-    #
-    GIT_REPOSITORY
-    GIT_TAG
-    GIT_REMOTE_NAME
-    GIT_SUBMODULES
-    GIT_SUBMODULES_RECURSE
-    GIT_SHALLOW
-    GIT_PROGRESS
-    GIT_CONFIG
-    GIT_REMOTE_UPDATE_STRATEGY
-    #
-    SVN_REPOSITORY
-    SVN_REVISION
-    SVN_USERNAME
-    SVN_PASSWORD
-    SVN_TRUST_CERT
-    #
-    HG_REPOSITORY
-    HG_TAG
-    #
-    CVS_REPOSITORY
-    CVS_MODULE
-    CVS_TAG
-    #
-    # Update step options
-    #
-    UPDATE_COMMAND
-    UPDATE_DISCONNECTED
-    #
-    # Patch step options
-    #
-    PATCH_COMMAND
-    #
-    # Configure step options
-    #
-    CONFIGURE_COMMAND
-    CMAKE_COMMAND
-    CMAKE_GENERATOR
-    CMAKE_GENERATOR_PLATFORM
-    CMAKE_GENERATOR_TOOLSET
-    CMAKE_GENERATOR_INSTANCE
-    CMAKE_ARGS
-    CMAKE_CACHE_ARGS
-    CMAKE_CACHE_DEFAULT_ARGS
-    SOURCE_SUBDIR
-    CONFIGURE_HANDLED_BY_BUILD
-    #
-    # Build step options
-    #
-    BUILD_COMMAND
-    BUILD_IN_SOURCE
-    BUILD_ALWAYS
-    BUILD_BYPRODUCTS
-    BUILD_JOB_SERVER_AWARE
-    #
-    # Install step options
-    #
-    INSTALL_COMMAND
-    INSTALL_BYPRODUCTS
-    #
-    # Test step options
-    #
-    TEST_COMMAND
-    TEST_BEFORE_INSTALL
-    TEST_AFTER_INSTALL
-    TEST_EXCLUDE_FROM_MAIN
-    #
-    # Logging options
-    #
-    LOG_DOWNLOAD
-    LOG_UPDATE
-    LOG_PATCH
-    LOG_CONFIGURE
-    LOG_BUILD
-    LOG_INSTALL
-    LOG_TEST
-    LOG_MERGED_STDOUTERR
-    LOG_OUTPUT_ON_FAILURE
-    #
-    # Terminal access options
-    #
-    USES_TERMINAL_DOWNLOAD
-    USES_TERMINAL_UPDATE
-    USES_TERMINAL_PATCH
-    USES_TERMINAL_CONFIGURE
-    USES_TERMINAL_BUILD
-    USES_TERMINAL_INSTALL
-    USES_TERMINAL_TEST
-    #
-    # Target options
-    #
-    DEPENDS
-    EXCLUDE_FROM_ALL
-    STEP_TARGETS
-    INDEPENDENT_STEP_TARGETS
-    #
-    # Miscellaneous options
-    #
-    LIST_SEPARATOR
-    #
-    # Internal options (undocumented)
-    #
-    EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR
-  )
+  _ep_get_add_keywords(keywords)
   _ep_parse_arguments(
     ExternalProject_Add
     "${keywords}"
@@ -4485,6 +2989,7 @@
     get_filename_component(work_dir "${source_dir}" PATH)
     _ep_resolve_git_remote(resolved_git_repository "${repo}" "${cmp0150}" "${work_dir}")
     set_property(TARGET ${name} PROPERTY _EP_GIT_REPOSITORY ${resolved_git_repository})
+    set(_EP_GIT_REPOSITORY "${resolved_git_repository}")
   endif()
 
   # The 'complete' step depends on all other steps and creates a
diff --git a/Modules/ExternalProject/download.cmake.in b/Modules/ExternalProject/download.cmake.in
index f21a91a..77d43d7 100644
--- a/Modules/ExternalProject/download.cmake.in
+++ b/Modules/ExternalProject/download.cmake.in
@@ -21,14 +21,14 @@
 
   set("${has_hash}" TRUE PARENT_SCOPE)
 
-  message(STATUS "verifying file...
+  message(VERBOSE "verifying file...
        file='@LOCAL@'")
 
   file("@ALGO@" "@LOCAL@" actual_value)
 
   if(NOT "${actual_value}" STREQUAL "@EXPECT_VALUE@")
     set("${hash_is_good}" FALSE PARENT_SCOPE)
-    message(STATUS "@ALGO@ hash of
+    message(VERBOSE "@ALGO@ hash of
     @LOCAL@
   does not match expected value
     expected: '@EXPECT_VALUE@'
@@ -44,7 +44,7 @@
   endif()
 
   if(attempt EQUAL 1)
-    message(STATUS "Retrying...")
+    message(VERBOSE "Retrying...")
     return()
   endif()
 
@@ -66,7 +66,7 @@
     set(sleep_seconds 1200)
   endif()
 
-  message(STATUS "Retry after ${sleep_seconds} seconds (attempt #${attempt}) ...")
+  message(VERBOSE "Retry after ${sleep_seconds} seconds (attempt #${attempt}) ...")
 
   execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep "${sleep_seconds}")
 endfunction()
@@ -75,17 +75,17 @@
   check_file_hash(has_hash hash_is_good)
   if(has_hash)
     if(hash_is_good)
-      message(STATUS "File already exists and hash match (skip download):
+      message(VERBOSE "File already exists and hash match (skip download):
   file='@LOCAL@'
   @ALGO@='@EXPECT_VALUE@'"
       )
       return()
     else()
-      message(STATUS "File already exists but hash mismatch. Removing...")
+      message(VERBOSE "File already exists but hash mismatch. Removing...")
       file(REMOVE "@LOCAL@")
     endif()
   else()
-    message(STATUS "File already exists but no hash specified (use URL_HASH):
+    message(VERBOSE "File already exists but no hash specified (use URL_HASH):
   file='@LOCAL@'
 Old file will be removed and new file downloaded from URL."
     )
@@ -95,7 +95,7 @@
 
 set(retry_number 5)
 
-message(STATUS "Downloading...
+message(VERBOSE "Downloading...
    dst='@LOCAL@'
    timeout='@TIMEOUT_MSG@'
    inactivity timeout='@INACTIVITY_TIMEOUT_MSG@'"
@@ -109,7 +109,7 @@
   endif()
   foreach(url IN ITEMS @REMOTE@)
     if(NOT url IN_LIST skip_url_list)
-      message(STATUS "Using src='${url}'")
+      message(VERBOSE "Using src='${url}'")
 
       @TLS_VERSION_CODE@
       @TLS_VERIFY_CODE@
@@ -135,10 +135,10 @@
       if(status_code EQUAL 0)
         check_file_hash(has_hash hash_is_good)
         if(has_hash AND NOT hash_is_good)
-          message(STATUS "Hash mismatch, removing...")
+          message(VERBOSE "Hash mismatch, removing...")
           file(REMOVE "@LOCAL@")
         else()
-          message(STATUS "Downloading... done")
+          message(VERBOSE "Downloading... done")
           return()
         endif()
       else()
diff --git a/Modules/ExternalProject/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in
index 984565b..39daaff 100644
--- a/Modules/ExternalProject/extractfile.cmake.in
+++ b/Modules/ExternalProject/extractfile.cmake.in
@@ -8,7 +8,7 @@
 get_filename_component(filename "@filename@" ABSOLUTE)
 get_filename_component(directory "@directory@" ABSOLUTE)
 
-message(STATUS "extracting...
+message(VERBOSE "extracting...
      src='${filename}'
      dst='${directory}'"
 )
@@ -28,21 +28,21 @@
 
 # Extract it:
 #
-message(STATUS "extracting... [tar @args@]")
+message(VERBOSE "extracting... [tar @args@]")
 execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename} @options@
   WORKING_DIRECTORY ${ut_dir}
   RESULT_VARIABLE rv
 )
 
 if(NOT rv EQUAL 0)
-  message(STATUS "extracting... [error clean up]")
+  message(VERBOSE "extracting... [error clean up]")
   file(REMOVE_RECURSE "${ut_dir}")
   message(FATAL_ERROR "Extract of '${filename}' failed")
 endif()
 
 # Analyze what came out of the tar file:
 #
-message(STATUS "extracting... [analysis]")
+message(VERBOSE "extracting... [analysis]")
 file(GLOB contents "${ut_dir}/*")
 list(REMOVE_ITEM contents "${ut_dir}/.DS_Store")
 list(LENGTH contents n)
@@ -52,14 +52,14 @@
 
 # Move "the one" directory to the final directory:
 #
-message(STATUS "extracting... [rename]")
+message(VERBOSE "extracting... [rename]")
 file(REMOVE_RECURSE ${directory})
 get_filename_component(contents ${contents} ABSOLUTE)
 file(RENAME ${contents} ${directory})
 
 # Clean up:
 #
-message(STATUS "extracting... [clean up]")
+message(VERBOSE "extracting... [clean up]")
 file(REMOVE_RECURSE "${ut_dir}")
 
-message(STATUS "extracting... done")
+message(VERBOSE "extracting... done")
diff --git a/Modules/ExternalProject/gitclone.cmake.in b/Modules/ExternalProject/gitclone.cmake.in
index 94b329a..93424ed 100644
--- a/Modules/ExternalProject/gitclone.cmake.in
+++ b/Modules/ExternalProject/gitclone.cmake.in
@@ -5,16 +5,26 @@
 
 if(EXISTS "@gitclone_stampfile@" AND EXISTS "@gitclone_infofile@" AND
   "@gitclone_stampfile@" IS_NEWER_THAN "@gitclone_infofile@")
-  message(STATUS
+  message(VERBOSE
     "Avoiding repeated git clone, stamp file is up to date: "
     "'@gitclone_stampfile@'"
   )
   return()
 endif()
 
+# Even at VERBOSE level, we don't want to see the commands executed, but
+# enabling them to be shown for DEBUG may be useful to help diagnose problems.
+cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level)
+if(active_log_level MATCHES "DEBUG|TRACE")
+  set(maybe_show_command "COMMAND_ECHO STDOUT")
+else()
+  set(maybe_show_command "")
+endif()
+
 execute_process(
   COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to remove directory: '@source_dir@'")
@@ -29,11 +39,12 @@
             clone @git_clone_options@ "@git_repository@" "@src_name@"
     WORKING_DIRECTORY "@work_dir@"
     RESULT_VARIABLE error_code
+    ${maybe_show_command}
   )
   math(EXPR number_of_tries "${number_of_tries} + 1")
 endwhile()
 if(number_of_tries GREATER 1)
-  message(STATUS "Had to git clone more than once: ${number_of_tries} times.")
+  message(NOTICE "Had to git clone more than once: ${number_of_tries} times.")
 endif()
 if(error_code)
   message(FATAL_ERROR "Failed to clone repository: '@git_repository@'")
@@ -44,6 +55,7 @@
           checkout "@git_tag@" @git_checkout_explicit--@
   WORKING_DIRECTORY "@work_dir@/@src_name@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to checkout tag: '@git_tag@'")
@@ -56,6 +68,7 @@
             submodule update @git_submodules_recurse@ --init @git_submodules@
     WORKING_DIRECTORY "@work_dir@/@src_name@"
     RESULT_VARIABLE error_code
+    ${maybe_show_command}
   )
 endif()
 if(error_code)
@@ -67,6 +80,7 @@
 execute_process(
   COMMAND ${CMAKE_COMMAND} -E copy "@gitclone_infofile@" "@gitclone_stampfile@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@gitclone_stampfile@'")
diff --git a/Modules/ExternalProject/gitupdate.cmake.in b/Modules/ExternalProject/gitupdate.cmake.in
index 171aa7b..4e3fba6 100644
--- a/Modules/ExternalProject/gitupdate.cmake.in
+++ b/Modules/ExternalProject/gitupdate.cmake.in
@@ -3,12 +3,22 @@
 
 cmake_minimum_required(VERSION 3.5)
 
+# Even at VERBOSE level, we don't want to see the commands executed, but
+# enabling them to be shown for DEBUG may be useful to help diagnose problems.
+cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level)
+if(active_log_level MATCHES "DEBUG|TRACE")
+  set(maybe_show_command "COMMAND_ECHO STDOUT")
+else()
+  set(maybe_show_command "")
+endif()
+
 function(do_fetch)
   message(VERBOSE "Fetching latest from the remote @git_remote_name@")
   execute_process(
     COMMAND "@git_EXECUTABLE@" --git-dir=.git fetch --tags --force "@git_remote_name@"
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL LAST
+    ${maybe_show_command}
   )
 endfunction()
 
@@ -34,6 +44,9 @@
   message(FATAL_ERROR "Failed to get the hash for HEAD:\n${error_msg}")
 endif()
 
+if("${can_fetch}" STREQUAL "")
+  set(can_fetch "@can_fetch_default@")
+endif()
 
 execute_process(
   COMMAND "@git_EXECUTABLE@" --git-dir=.git show-ref "@git_tag@"
@@ -57,7 +70,7 @@
   # FIXME: We should provide an option to always fetch for this case
   get_hash_for_ref("@git_tag@" tag_sha error_msg)
   if(tag_sha STREQUAL head_sha)
-    message(VERBOSE "Already at requested tag: ${tag_sha}")
+    message(VERBOSE "Already at requested tag: @git_tag@")
     return()
   endif()
 
@@ -97,7 +110,7 @@
     # because it can be confusing for users to see a failed git command.
     # That failure is being handled here, so it isn't an error.
     if(NOT error_msg STREQUAL "")
-      message(VERBOSE "${error_msg}")
+      message(DEBUG "${error_msg}")
     endif()
     do_fetch()
     set(checkout_name "@git_tag@")
@@ -181,6 +194,7 @@
     COMMAND "@git_EXECUTABLE@" --git-dir=.git stash save @git_stash_save_options@
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
+    ${maybe_show_command}
   )
 endif()
 
@@ -189,6 +203,7 @@
     COMMAND "@git_EXECUTABLE@" --git-dir=.git checkout "${checkout_name}"
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
+    ${maybe_show_command}
   )
 else()
   execute_process(
@@ -203,6 +218,7 @@
     execute_process(
       COMMAND "@git_EXECUTABLE@" --git-dir=.git rebase --abort
       WORKING_DIRECTORY "@work_dir@"
+      ${maybe_show_command}
     )
 
     if(NOT git_update_strategy STREQUAL "REBASE_CHECKOUT")
@@ -211,6 +227,7 @@
         execute_process(
           COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet
           WORKING_DIRECTORY "@work_dir@"
+          ${maybe_show_command}
           )
       endif()
       message(FATAL_ERROR "\nFailed to rebase in: '@work_dir@'."
@@ -236,12 +253,14 @@
               ${tag_name}
       WORKING_DIRECTORY "@work_dir@"
       COMMAND_ERROR_IS_FATAL ANY
+      ${maybe_show_command}
     )
 
     execute_process(
       COMMAND "@git_EXECUTABLE@" --git-dir=.git checkout "${checkout_name}"
       WORKING_DIRECTORY "@work_dir@"
       COMMAND_ERROR_IS_FATAL ANY
+      ${maybe_show_command}
     )
   endif()
 endif()
@@ -252,27 +271,32 @@
     COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet
     WORKING_DIRECTORY "@work_dir@"
     RESULT_VARIABLE error_code
+    ${maybe_show_command}
     )
   if(error_code)
     # Stash pop --index failed: Try again dropping the index
     execute_process(
       COMMAND "@git_EXECUTABLE@" --git-dir=.git reset --hard --quiet
       WORKING_DIRECTORY "@work_dir@"
+      ${maybe_show_command}
     )
     execute_process(
       COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --quiet
       WORKING_DIRECTORY "@work_dir@"
       RESULT_VARIABLE error_code
+      ${maybe_show_command}
     )
     if(error_code)
       # Stash pop failed: Restore previous state.
       execute_process(
         COMMAND "@git_EXECUTABLE@" --git-dir=.git reset --hard --quiet ${head_sha}
         WORKING_DIRECTORY "@work_dir@"
+        ${maybe_show_command}
       )
       execute_process(
         COMMAND "@git_EXECUTABLE@" --git-dir=.git stash pop --index --quiet
         WORKING_DIRECTORY "@work_dir@"
+        ${maybe_show_command}
       )
       message(FATAL_ERROR "\nFailed to unstash changes in: '@work_dir@'."
                           "\nYou will have to resolve the conflicts manually")
@@ -288,5 +312,6 @@
             submodule update @git_submodules_recurse@ --init @git_submodules@
     WORKING_DIRECTORY "@work_dir@"
     COMMAND_ERROR_IS_FATAL ANY
+    ${maybe_show_command}
   )
 endif()
diff --git a/Modules/ExternalProject/hgclone.cmake.in b/Modules/ExternalProject/hgclone.cmake.in
index e2b55ba..993ab7f 100644
--- a/Modules/ExternalProject/hgclone.cmake.in
+++ b/Modules/ExternalProject/hgclone.cmake.in
@@ -5,16 +5,26 @@
 
 if(EXISTS "@hgclone_stampfile@" AND EXISTS "@hgclone_infofile@" AND
   "@hgclone_stampfile@" IS_NEWER_THAN "@hgclone_infofile@")
-  message(STATUS
+  message(VERBOSE
     "Avoiding repeated hg clone, stamp file is up to date: "
     "'@hgclone_stampfile@'"
   )
   return()
 endif()
 
+# Even at VERBOSE level, we don't want to see the commands executed, but
+# enabling them to be shown for DEBUG may be useful to help diagnose problems.
+cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level)
+if(active_log_level MATCHES "DEBUG|TRACE")
+  set(maybe_show_command "COMMAND_ECHO STDOUT")
+else()
+  set(maybe_show_command "")
+endif()
+
 execute_process(
   COMMAND ${CMAKE_COMMAND} -E rm -rf "@source_dir@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to remove directory: '@source_dir@'")
@@ -24,6 +34,7 @@
   COMMAND "@hg_EXECUTABLE@" clone -U "@hg_repository@" "@src_name@"
   WORKING_DIRECTORY "@work_dir@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to clone repository: '@hg_repository@'")
@@ -33,6 +44,7 @@
   COMMAND "@hg_EXECUTABLE@" update @hg_tag@
   WORKING_DIRECTORY "@work_dir@/@src_name@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to checkout tag: '@hg_tag@'")
@@ -43,6 +55,7 @@
 execute_process(
   COMMAND ${CMAKE_COMMAND} -E copy "@hgclone_infofile@" "@hgclone_stampfile@"
   RESULT_VARIABLE error_code
+  ${maybe_show_command}
 )
 if(error_code)
   message(FATAL_ERROR "Failed to copy script-last-run stamp file: '@hgclone_stampfile@'")
diff --git a/Modules/ExternalProject/shared_internal_commands.cmake b/Modules/ExternalProject/shared_internal_commands.cmake
index ca3cd9f..6c4f61a 100644
--- a/Modules/ExternalProject/shared_internal_commands.cmake
+++ b/Modules/ExternalProject/shared_internal_commands.cmake
@@ -75,6 +75,7 @@
   set("${output_variable}" "${git_remote_url}" PARENT_SCOPE)
 endfunction()
 
+
 function(_ep_is_relative_git_remote output_variable remote_url)
   if(remote_url MATCHES "^\\.\\./")
     set("${output_variable}" TRUE PARENT_SCOPE)
@@ -83,6 +84,7 @@
   endif()
 endfunction()
 
+
 # Return an absolute remote URL given an existing remote URL and relative path.
 # The output_variable will be set to an empty string if an absolute URL
 # could not be computed (no error message is output).
@@ -133,6 +135,7 @@
   set("${output_variable}" "${protocol}${auth}${host}${separator}${path}" PARENT_SCOPE)
 endfunction()
 
+
 # The output_variable will be set to the original git_repository if it
 # could not be resolved (no error message is output). The original value is
 # also returned if it doesn't need to be resolved.
@@ -180,3 +183,1812 @@
 
   set("${output_variable}" "${cmp0150_old_base_dir}/${git_repository}" PARENT_SCOPE)
 endfunction()
+
+
+macro(_ep_get_hash_algos out_var)
+  set(${out_var}
+    MD5
+    SHA1
+    SHA224
+    SHA256
+    SHA384
+    SHA512
+    SHA3_224
+    SHA3_256
+    SHA3_384
+    SHA3_512
+  )
+endmacro()
+
+
+macro(_ep_get_hash_regex out_var)
+  _ep_get_hash_algos(${out_var})
+  list(JOIN ${out_var} "|" ${out_var})
+  set(${out_var} "^(${${out_var}})=([0-9A-Fa-f]+)$")
+endmacro()
+
+
+function(_ep_parse_arguments_to_vars
+  f
+  keywords
+  name
+  ns
+  args
+)
+  # Transfer the arguments into variables in the calling scope.
+  # Because some keywords can be repeated, we can't use cmake_parse_arguments().
+  # Instead, we loop through the args and consider the namespace starting with
+  # an upper-case letter followed by at least two more upper-case letters,
+  # numbers or underscores to be keywords.
+
+  foreach(key IN LISTS keywords)
+    unset(${ns}${key})
+  endforeach()
+
+  set(key)
+
+  foreach(arg IN LISTS args)
+    set(is_value 1)
+
+    if(arg MATCHES "^[A-Z][A-Z0-9_][A-Z0-9_]+$" AND
+      NOT (("x${arg}x" STREQUAL "x${key}x") AND
+    ("x${key}x" STREQUAL "xCOMMANDx")) AND
+      NOT arg MATCHES "^(TRUE|FALSE|YES)$")
+      if(arg IN_LIST keywords)
+        set(is_value 0)
+      endif()
+    endif()
+
+    if(is_value)
+      if(key)
+        # Value
+        list(APPEND ${ns}${key} "${arg}")
+      else()
+        # Missing Keyword
+        message(AUTHOR_WARNING
+          "value '${arg}' with no previous keyword in ${f}"
+        )
+      endif()
+    else()
+      set(key "${arg}")
+    endif()
+  endforeach()
+
+  foreach(key IN LISTS keywords)
+    if(DEFINED ${ns}${key})
+      set(${ns}${key} "${${ns}${key}}" PARENT_SCOPE)
+    else()
+      unset(${ns}${key} PARENT_SCOPE)
+    endif()
+  endforeach()
+
+endfunction()
+
+
+# NOTE: This cannot be a macro because that will evaluate anything that looks
+#       like a CMake variable in any of the args.
+function(_ep_parse_arguments
+  f
+  keywords
+  name
+  ns
+  args
+)
+  _ep_parse_arguments_to_vars(
+    "${f}"
+    "${keywords}"
+    ${name}
+    ${ns}
+    "${args}"
+  )
+
+  foreach(key IN LISTS keywords)
+    if(DEFINED ${ns}${key})
+      set(${ns}${key} "${${ns}${key}}" PARENT_SCOPE)
+    else()
+      unset(${ns}${key} PARENT_SCOPE)
+    endif()
+  endforeach()
+
+  # Transfer the arguments to the target as target properties. These are
+  # read by the various steps, potentially from different scopes.
+  foreach(key IN LISTS keywords)
+    if(DEFINED ${ns}${key})
+      set_property(TARGET ${name} PROPERTY ${ns}${key} "${${ns}${key}}")
+    endif()
+  endforeach()
+
+endfunction()
+
+
+function(_ep_get_tls_version name tls_version_var)
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  set(tls_version_regex "^1\\.[0-3]$")
+  set(tls_version "${_EP_TLS_VERSION}")
+  if(NOT "x${tls_version}" STREQUAL "x")
+    if(NOT tls_version MATCHES "${tls_version_regex}")
+      message(FATAL_ERROR "TLS_VERSION '${tls_version}' not known")
+    endif()
+  elseif(NOT "x${CMAKE_TLS_VERSION}" STREQUAL "x")
+    set(tls_version "${CMAKE_TLS_VERSION}")
+    if(NOT tls_version MATCHES "${tls_version_regex}")
+      message(FATAL_ERROR "CMAKE_TLS_VERSION '${tls_version}' not known")
+    endif()
+  elseif(NOT "x$ENV{CMAKE_TLS_VERSION}" STREQUAL "x")
+    set(tls_version "$ENV{CMAKE_TLS_VERSION}")
+    if(NOT tls_version MATCHES "${tls_version_regex}")
+      message(FATAL_ERROR "ENV{CMAKE_TLS_VERSION} '${tls_version}' not known")
+    endif()
+  endif()
+  set("${tls_version_var}" "${tls_version}" PARENT_SCOPE)
+endfunction()
+
+
+function(_ep_get_tls_verify name tls_verify_var)
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  set(tls_verify "${_EP_TLS_VERIFY}")
+  if("x${tls_verify}" STREQUAL "x")
+    if(NOT "x${CMAKE_TLS_VERIFY}" STREQUAL "x")
+      set(tls_verify "${CMAKE_TLS_VERIFY}")
+    elseif(NOT "x$ENV{CMAKE_TLS_VERIFY}" STREQUAL "x")
+      set(tls_verify "$ENV{CMAKE_TLS_VERIFY}")
+    endif()
+  endif()
+  set("${tls_verify_var}" "${tls_verify}" PARENT_SCOPE)
+endfunction()
+
+
+function(_ep_get_tls_cainfo name tls_cainfo_var)
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  set(tls_cainfo "${_EP_TLS_CAINFO}")
+  if("x${tls_cainfo}" STREQUAL "x" AND DEFINED CMAKE_TLS_CAINFO)
+    set(tls_cainfo "${CMAKE_TLS_CAINFO}")
+  endif()
+  set("${tls_cainfo_var}" "${tls_cainfo}" PARENT_SCOPE)
+endfunction()
+
+
+function(_ep_get_netrc name netrc_var)
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  set(netrc "${_EP_NETRC}")
+  if("x${netrc}" STREQUAL "x" AND DEFINED CMAKE_NETRC)
+    set(netrc "${CMAKE_NETRC}")
+  endif()
+  set("${netrc_var}" "${netrc}" PARENT_SCOPE)
+endfunction()
+
+
+function(_ep_get_netrc_file name netrc_file_var)
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  set(netrc_file "${_EP_NETRC_FILE}")
+  if("x${netrc_file}" STREQUAL "x" AND DEFINED CMAKE_NETRC_FILE)
+    set(netrc_file "${CMAKE_NETRC_FILE}")
+  endif()
+  set("${netrc_file_var}" "${netrc_file}" PARENT_SCOPE)
+endfunction()
+
+
+function(_ep_write_gitclone_script
+  script_filename
+  source_dir
+  git_EXECUTABLE
+  git_repository
+  git_tag
+  git_remote_name
+  init_submodules
+  git_submodules_recurse
+  git_submodules
+  git_shallow
+  git_progress
+  git_config
+  src_name
+  work_dir
+  gitclone_infofile
+  gitclone_stampfile
+  tls_version
+  tls_verify
+)
+
+  if(NOT GIT_VERSION_STRING VERSION_LESS 1.8.5)
+    # Use `git checkout <tree-ish> --` to avoid ambiguity with a local path.
+    set(git_checkout_explicit-- "--")
+  else()
+    # Use `git checkout <branch>` even though this risks ambiguity with a
+    # local path.  Unfortunately we cannot use `git checkout <tree-ish> --`
+    # because that will not search for remote branch names, a common use case.
+    set(git_checkout_explicit-- "")
+  endif()
+  if("${git_tag}" STREQUAL "")
+    message(FATAL_ERROR "Tag for git checkout should not be empty.")
+  endif()
+
+  if(GIT_VERSION_STRING VERSION_LESS 2.20 OR
+    2.21 VERSION_LESS_EQUAL GIT_VERSION_STRING)
+    set(git_clone_options "--no-checkout")
+  else()
+    set(git_clone_options)
+  endif()
+  if(git_shallow)
+    if(NOT GIT_VERSION_STRING VERSION_LESS 1.7.10)
+      list(APPEND git_clone_options "--depth 1 --no-single-branch")
+    else()
+      list(APPEND git_clone_options "--depth 1")
+    endif()
+  endif()
+  if(git_progress)
+    list(APPEND git_clone_options --progress)
+  endif()
+  foreach(config IN LISTS git_config)
+    list(APPEND git_clone_options --config \"${config}\")
+  endforeach()
+  if(NOT ${git_remote_name} STREQUAL "origin")
+    list(APPEND git_clone_options --origin \"${git_remote_name}\")
+  endif()
+
+  # The clone config option is sticky, it will apply to all subsequent git
+  # update operations. The submodules config option is not sticky, because
+  # git doesn't provide any way to do that. Thus, we will have to pass the
+  # same config option in the update step too for submodules, but not for
+  # the main git repo.
+  set(git_submodules_config_options "")
+  if(NOT "x${tls_version}" STREQUAL "x")
+    list(APPEND git_clone_options -c http.sslVersion=tlsv${tls_version})
+    list(APPEND git_submodules_config_options -c http.sslVersion=tlsv${tls_version})
+  endif()
+  if(NOT "x${tls_verify}" STREQUAL "x")
+    if(tls_verify)
+      # Default git behavior is "true", but the user might have changed the
+      # global default to "false". Since TLS_VERIFY was given, ensure we honor
+      # the specified setting regardless of what the global default might be.
+      list(APPEND git_clone_options -c http.sslVerify=true)
+      list(APPEND git_submodules_config_options -c http.sslVerify=true)
+    else()
+      list(APPEND git_clone_options -c http.sslVerify=false)
+      list(APPEND git_submodules_config_options -c http.sslVerify=false)
+    endif()
+  endif()
+
+  string (REPLACE ";" " " git_clone_options "${git_clone_options}")
+
+  configure_file(
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/gitclone.cmake.in
+    ${script_filename}
+    @ONLY
+  )
+endfunction()
+
+
+function(_ep_write_hgclone_script
+  script_filename
+  source_dir
+  hg_EXECUTABLE
+  hg_repository
+  hg_tag
+  src_name
+  work_dir
+  hgclone_infofile
+  hgclone_stampfile
+)
+
+  if("${hg_tag}" STREQUAL "")
+    message(FATAL_ERROR "Tag for hg checkout should not be empty.")
+  endif()
+
+  configure_file(
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/hgclone.cmake.in
+    ${script_filename}
+    @ONLY
+  )
+endfunction()
+
+
+function(_ep_write_gitupdate_script
+  script_filename
+  git_EXECUTABLE
+  git_tag
+  git_remote_name
+  init_submodules
+  git_submodules_recurse
+  git_submodules
+  git_repository
+  work_dir
+  git_update_strategy
+  tls_version
+  tls_verify
+)
+
+  if("${git_tag}" STREQUAL "")
+    message(FATAL_ERROR "Tag for git checkout should not be empty.")
+  endif()
+  set(git_stash_save_options --quiet)
+  if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7)
+    # This avoids stashing files covered by .gitignore
+    list(APPEND git_stash_save_options --include-untracked)
+  elseif(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.6)
+    # Untracked files, but also ignored files, so potentially slower
+    list(APPEND git_stash_save_options --all)
+  endif()
+
+  # The submodules config option is not sticky, git doesn't provide any way
+  # to do that. We have to pass this config option for the update step too.
+  # We don't need to set it for the non-submodule update because it gets
+  # recorded as part of the clone operation in a sticky manner.
+  set(git_submodules_config_options "")
+  if(NOT "x${tls_version}" STREQUAL "x")
+    list(APPEND git_submodules_config_options -c http.sslVersion=tlsv${tls_version})
+  endif()
+  if(NOT "x${tls_verify}" STREQUAL "x")
+    if(tls_verify)
+      # Default git behavior is "true", but the user might have changed the
+      # global default to "false". Since TLS_VERIFY was given, ensure we honor
+      # the specified setting regardless of what the global default might be.
+      list(APPEND git_submodules_config_options -c http.sslVerify=true)
+    else()
+      list(APPEND git_submodules_config_options -c http.sslVerify=false)
+    endif()
+  endif()
+
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/gitupdate.cmake.in"
+    "${script_filename}"
+    @ONLY
+  )
+endfunction()
+
+
+function(_ep_write_downloadfile_script
+  script_filename
+  REMOTE
+  LOCAL
+  timeout
+  inactivity_timeout
+  no_progress
+  hash
+  tls_version
+  tls_verify
+  tls_cainfo
+  userpwd
+  http_headers
+  netrc
+  netrc_file
+)
+  if("x${REMOTE}" STREQUAL "x")
+    message(FATAL_ERROR "REMOTE can't be empty")
+  endif()
+  if("x${LOCAL}" STREQUAL "x")
+    message(FATAL_ERROR "LOCAL can't be empty")
+  endif()
+
+  # REMOTE could contain special characters that parse as separate arguments.
+  # Things like parentheses are legitimate characters in a URL, but would be
+  # seen as the start of a new unquoted argument by the cmake language parser.
+  # Avoid those special cases by preparing quoted strings for direct inclusion
+  # in the foreach() call that iterates over the set of URLs in REMOTE.
+  set(REMOTE "[====[${REMOTE}]====]")
+  string(REPLACE ";" "]====] [====[" REMOTE "${REMOTE}")
+
+  if(timeout)
+    set(TIMEOUT_ARGS TIMEOUT ${timeout})
+    set(TIMEOUT_MSG "${timeout} seconds")
+  else()
+    set(TIMEOUT_ARGS "# no TIMEOUT")
+    set(TIMEOUT_MSG "none")
+  endif()
+  if(inactivity_timeout)
+    set(INACTIVITY_TIMEOUT_ARGS INACTIVITY_TIMEOUT ${inactivity_timeout})
+    set(INACTIVITY_TIMEOUT_MSG "${inactivity_timeout} seconds")
+  else()
+    set(INACTIVITY_TIMEOUT_ARGS "# no INACTIVITY_TIMEOUT")
+    set(INACTIVITY_TIMEOUT_MSG "none")
+  endif()
+
+  if(no_progress)
+    set(SHOW_PROGRESS "")
+  else()
+    set(SHOW_PROGRESS "SHOW_PROGRESS")
+  endif()
+
+  _ep_get_hash_regex(_ep_hash_regex)
+  if("${hash}" MATCHES "${_ep_hash_regex}")
+    set(ALGO "${CMAKE_MATCH_1}")
+    string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE)
+  else()
+    set(ALGO "")
+    set(EXPECT_VALUE "")
+  endif()
+
+  set(TLS_VERSION_CODE "")
+  if(NOT "x${tls_version}" STREQUAL "x")
+    set(TLS_VERSION_CODE "set(CMAKE_TLS_VERSION \"${tls_version}\")")
+  endif()
+
+  set(TLS_VERIFY_CODE "")
+  if(NOT "x${tls_verify}" STREQUAL "x")
+    set(TLS_VERIFY_CODE "set(CMAKE_TLS_VERIFY \"${tls_verify}\")")
+  endif()
+
+  set(TLS_CAINFO_CODE "")
+  if(NOT "x${tls_cainfo}" STREQUAL "x")
+    set(TLS_CAINFO_CODE "set(CMAKE_TLS_CAINFO \"${tls_cainfo}\")")
+  endif()
+
+  set(NETRC_CODE "")
+  if(NOT "x${netrc}" STREQUAL "x")
+    set(NETRC_CODE "set(CMAKE_NETRC \"${netrc}\")")
+  endif()
+
+  set(NETRC_FILE_CODE "")
+  if(NOT "x${netrc_file}" STREQUAL "x")
+    set(NETRC_FILE_CODE "set(CMAKE_NETRC_FILE \"${netrc_file}\")")
+  endif()
+
+  if(userpwd STREQUAL ":")
+    set(USERPWD_ARGS)
+  else()
+    set(USERPWD_ARGS USERPWD "${userpwd}")
+  endif()
+
+  set(HTTP_HEADERS_ARGS "")
+  if(NOT http_headers STREQUAL "")
+    foreach(header IN LISTS http_headers)
+      string(PREPEND HTTP_HEADERS_ARGS
+        "HTTPHEADER \"${header}\"\n        "
+      )
+    endforeach()
+  endif()
+
+  # Used variables:
+  # * TLS_VERSION_CODE
+  # * TLS_VERIFY_CODE
+  # * TLS_CAINFO_CODE
+  # * ALGO
+  # * EXPECT_VALUE
+  # * REMOTE
+  # * LOCAL
+  # * SHOW_PROGRESS
+  # * TIMEOUT_ARGS
+  # * TIMEOUT_MSG
+  # * USERPWD_ARGS
+  # * HTTP_HEADERS_ARGS
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/download.cmake.in"
+    "${script_filename}"
+    @ONLY
+  )
+endfunction()
+
+
+function(_ep_write_verifyfile_script
+  script_filename
+  LOCAL
+  hash
+)
+  _ep_get_hash_regex(_ep_hash_regex)
+  if("${hash}" MATCHES "${_ep_hash_regex}")
+    set(ALGO "${CMAKE_MATCH_1}")
+    string(TOLOWER "${CMAKE_MATCH_2}" EXPECT_VALUE)
+  else()
+    set(ALGO "")
+    set(EXPECT_VALUE "")
+  endif()
+
+  # Used variables:
+  # * ALGO
+  # * EXPECT_VALUE
+  # * LOCAL
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/verify.cmake.in"
+    "${script_filename}"
+    @ONLY
+  )
+endfunction()
+
+
+function(_ep_write_extractfile_script
+  script_filename
+  name
+  filename
+  directory
+  options
+)
+  set(args "")
+
+  if(filename MATCHES
+    "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$")
+    set(args xfz)
+  endif()
+
+  if(filename MATCHES "(\\.|=)tar$")
+    set(args xf)
+  endif()
+
+  if(args STREQUAL "")
+    message(FATAL_ERROR
+      "Do not know how to extract '${filename}' -- known types are: "
+      ".7z, .tar, .tar.bz2, .tar.gz, .tar.xz, .tbz2, .tgz, .txz and .zip"
+    )
+  endif()
+
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/extractfile.cmake.in"
+    "${script_filename}"
+    @ONLY
+  )
+endfunction()
+
+
+function(_ep_is_dir_empty dir empty_var)
+  file(GLOB gr "${dir}/*")
+  if("${gr}" STREQUAL "")
+    set(${empty_var} 1 PARENT_SCOPE)
+  else()
+    set(${empty_var} 0 PARENT_SCOPE)
+  endif()
+endfunction()
+
+function(_ep_get_git_submodules_recurse git_submodules_recurse)
+  # Checks for GIT_SUBMODULES_RECURSE argument. Default is ON, which sets
+  # git_submodules_recurse output variable to "--recursive". Otherwise, the
+  # output variable is set to an empty value "".
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  if(NOT DEFINED _EP_GIT_SUBMODULES_RECURSE)
+    set(recurseFlag "--recursive")
+  else()
+    if(_EP_GIT_SUBMODULES_RECURSE)
+      set(recurseFlag "--recursive")
+    else()
+      set(recurseFlag "")
+    endif()
+  endif()
+  set(${git_submodules_recurse} "${recurseFlag}" PARENT_SCOPE)
+
+  # The git submodule update '--recursive' flag requires git >= v1.6.5
+  if(recurseFlag AND GIT_VERSION_STRING VERSION_LESS 1.6.5)
+    message(FATAL_ERROR
+      "git version 1.6.5 or later required for --recursive flag with "
+      "'git submodule ...': GIT_VERSION_STRING='${GIT_VERSION_STRING}'"
+    )
+  endif()
+endfunction()
+
+
+function(_ep_add_script_commands script_var work_dir cmd)
+  # We only support a subset of what ep_replace_location_tags() handles
+  set(location_tags
+    SOURCE_DIR
+    SOURCE_SUBDIR
+    BINARY_DIR
+    TMP_DIR
+    DOWNLOAD_DIR
+    DOWNLOADED_FILE
+  )
+
+  # There can be multiple COMMANDs, but we have to split those up to
+  # one command per call to execute_process()
+  set(execute_process_cmd
+    "execute_process(\n"
+    "  WORKING_DIRECTORY \"${work_dir}\"\n"
+    "  COMMAND_ERROR_IS_FATAL LAST\n"
+  )
+  cmake_language(GET_MESSAGE_LOG_LEVEL active_log_level)
+  if(active_log_level MATCHES "VERBOSE|DEBUG|TRACE")
+    string(APPEND execute_process_cmd "  COMMAND_ECHO STDOUT\n")
+  endif()
+  string(APPEND execute_process_cmd "  COMMAND ")
+
+  string(APPEND ${script_var} "${execute_process_cmd}")
+
+  foreach(cmd_arg IN LISTS cmd)
+    if(cmd_arg STREQUAL "COMMAND")
+      string(APPEND ${script_var} "\n)\n${execute_process_cmd}")
+    else()
+      if(_EP_LIST_SEPARATOR)
+        string(REPLACE "${_EP_LIST_SEPARATOR}" "\\;" cmd_arg "${cmd_arg}")
+      endif()
+      foreach(dir IN LISTS location_tags)
+        string(REPLACE "<${dir}>" "${_EP_${dir}}" cmd_arg "${cmd_arg}")
+      endforeach()
+      string(APPEND ${script_var} " [====[${cmd_arg}]====]")
+    endif()
+  endforeach()
+
+  string(APPEND ${script_var} "\n)")
+  set(${script_var} "${${script_var}}" PARENT_SCOPE)
+endfunction()
+
+
+function(_ep_add_download_command name)
+  set(noValueOptions )
+  set(singleValueOptions
+    SCRIPT_FILE        # These should only be used by FetchContent
+    DEPENDS_VARIABLE   #
+  )
+  set(multiValueOptions )
+  cmake_parse_arguments(PARSE_ARGV 1 arg
+    "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}"
+  )
+
+  # The various _EP_... variables mentioned here and throughout this function
+  # are expected to already have been set by the caller via a call to
+  # _ep_parse_arguments() or ep_parse_arguments_to_vars(). Other variables
+  # with different names are assigned to for historical reasons only to keep
+  # the code more readable and minimize change.
+
+  set(source_dir     "${_EP_SOURCE_DIR}")
+  set(stamp_dir      "${_EP_STAMP_DIR}")
+  set(download_dir   "${_EP_DOWNLOAD_DIR}")
+  set(tmp_dir        "${_EP_TMP_DIR}")
+
+  set(cmd            "${_EP_DOWNLOAD_COMMAND}")
+  set(cvs_repository "${_EP_CVS_REPOSITORY}")
+  set(svn_repository "${_EP_SVN_REPOSITORY}")
+  set(git_repository "${_EP_GIT_REPOSITORY}")
+  set(hg_repository  "${_EP_HG_REPOSITORY}")
+  set(url            "${_EP_URL}")
+  set(fname          "${_EP_DOWNLOAD_NAME}")
+
+  # TODO: Perhaps file:// should be copied to download dir before extraction.
+  string(REGEX REPLACE "file://" "" url "${url}")
+
+  set(step_script_contents)
+  set(depends)
+  set(comment)
+  set(work_dir)
+  set(extra_repo_info)
+
+  if(DEFINED _EP_DOWNLOAD_COMMAND)
+    set(work_dir ${download_dir})
+    set(method custom)
+    if(NOT "x${cmd}" STREQUAL "x" AND arg_SCRIPT_FILE)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+
+  elseif(cvs_repository)
+    set(method cvs)
+    find_package(CVS QUIET)
+    if(NOT CVS_EXECUTABLE)
+      message(FATAL_ERROR "error: could not find cvs for checkout of ${name}")
+    endif()
+
+    set(cvs_module "${_EP_CVS_MODULE}")
+    if(NOT cvs_module)
+      message(FATAL_ERROR "error: no CVS_MODULE")
+    endif()
+
+    set(cvs_tag "${_EP_CVS_TAG}")
+    get_filename_component(src_name "${source_dir}" NAME)
+    get_filename_component(work_dir "${source_dir}" PATH)
+    set(comment "Performing download step (CVS checkout) for '${name}'")
+    set(cmd
+      ${CVS_EXECUTABLE}
+      -d ${cvs_repository}
+      -q
+      co ${cvs_tag}
+      -d ${src_name}
+      ${cvs_module}
+    )
+    if(arg_SCRIPT_FILE)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+
+  elseif(svn_repository)
+    set(method svn)
+    find_package(Subversion QUIET)
+    if(NOT Subversion_SVN_EXECUTABLE)
+      message(FATAL_ERROR "error: could not find svn for checkout of ${name}")
+    endif()
+
+    set(svn_revision   "${_EP_SVN_REVISION}")
+    set(svn_username   "${_EP_SVN_USERNAME}")
+    set(svn_password   "${_EP_SVN_PASSWORD}")
+    set(svn_trust_cert "${_EP_SVN_TRUST_CERT}")
+    set(uses_terminal  "${_EP_USES_TERMINAL_DOWNLOAD}")
+    # The --trust-server-cert option requires --non-interactive
+    if(uses_terminal AND NOT svn_trust_cert)
+      set(svn_interactive_args "")
+    else()
+      set(svn_interactive_args "--non-interactive")
+    endif()
+
+    get_filename_component(src_name "${source_dir}" NAME)
+    get_filename_component(work_dir "${source_dir}" PATH)
+    set(comment "Performing download step (SVN checkout) for '${name}'")
+    set(svn_user_pw_args "")
+    if(DEFINED _EP_SVN_USERNAME)
+      set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}")
+    endif()
+    if(DEFINED _EP_SVN_PASSWORD)
+      set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}")
+    endif()
+    if(svn_trust_cert)
+      set(svn_trust_cert_args --trust-server-cert)
+    endif()
+    set(cmd
+      ${Subversion_SVN_EXECUTABLE}
+      co
+      ${svn_repository}
+      ${svn_revision}
+      ${svn_interactive_args}
+      ${svn_trust_cert_args}
+      ${svn_user_pw_args}
+      ${src_name}
+    )
+    if(arg_SCRIPT_FILE)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+
+  elseif(git_repository)
+    set(method git)
+    # FetchContent gives us these directly, so don't try to recompute them
+    if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING)
+      unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
+      find_package(Git QUIET)
+      if(NOT GIT_EXECUTABLE)
+        message(FATAL_ERROR "error: could not find git for clone of ${name}")
+      endif()
+    endif()
+
+    _ep_get_git_submodules_recurse(git_submodules_recurse)
+
+    set(git_tag "${_EP_GIT_TAG}")
+    if(NOT git_tag)
+      set(git_tag "master")
+    endif()
+
+    set(git_init_submodules TRUE)
+    if(DEFINED _EP_GIT_SUBMODULES)
+      set(git_submodules "${_EP_GIT_SUBMODULES}")
+      if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
+        set(git_init_submodules FALSE)
+      endif()
+    endif()
+
+    set(git_remote_name "${_EP_GIT_REMOTE_NAME}")
+    if(NOT git_remote_name)
+      set(git_remote_name "origin")
+    endif()
+
+    _ep_get_tls_version(${name} tls_version)
+    _ep_get_tls_verify(${name} tls_verify)
+    set(git_shallow  "${_EP_GIT_SHALLOW}")
+    set(git_progress "${_EP_GIT_PROGRESS}")
+    set(git_config   "${_EP_GIT_CONFIG}")
+
+    # If git supports it, make checkouts quiet when checking out a git hash.
+    # This avoids the very noisy detached head message.
+    if(GIT_VERSION_STRING VERSION_GREATER_EQUAL 1.7.7)
+      list(PREPEND git_config advice.detachedHead=false)
+    endif()
+
+    # The command doesn't expose any details, so we need to record additional
+    # information in the RepositoryInfo.txt file. For the download step, only
+    # the things specifically affecting the clone operation should be recorded.
+    # If the repo changes, the clone script should be run again.
+    # But if only the tag changes, avoid running the clone script again.
+    # Let the 'always' running update step checkout the new tag.
+    #
+    set(extra_repo_info
+      "repository=${git_repository}
+remote=${git_remote_name}
+init_submodules=${git_init_submodules}
+recurse_submodules=${git_submodules_recurse}
+submodules=${git_submodules}
+CMP0097=${_EP_CMP0097}
+      ")
+    get_filename_component(src_name "${source_dir}" NAME)
+    get_filename_component(work_dir "${source_dir}" PATH)
+
+    # Since git clone doesn't succeed if the non-empty source_dir exists,
+    # create a cmake script to invoke as download command.
+    # The script will delete the source directory and then call git clone.
+    #
+    set(clone_script ${tmp_dir}/${name}-gitclone.cmake)
+    _ep_write_gitclone_script(
+      ${clone_script}
+      ${source_dir}
+      ${GIT_EXECUTABLE}
+      ${git_repository}
+      ${git_tag}
+      ${git_remote_name}
+      ${git_init_submodules}
+      "${git_submodules_recurse}"
+      "${git_submodules}"
+      "${git_shallow}"
+      "${git_progress}"
+      "${git_config}"
+      ${src_name}
+      ${work_dir}
+      ${stamp_dir}/${name}-gitinfo.txt
+      ${stamp_dir}/${name}-gitclone-lastrun.txt
+      "${tls_version}"
+      "${tls_verify}"
+    )
+    set(comment "Performing download step (git clone) for '${name}'")
+    set(cmd ${CMAKE_COMMAND}
+      -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+      -P ${clone_script}
+    )
+
+    if(arg_SCRIPT_FILE)
+      set(step_script_contents "include(\"${clone_script}\")")
+      list(APPEND depends ${clone_script})
+    endif()
+
+  elseif(hg_repository)
+    set(method hg)
+    find_package(Hg QUIET)
+    if(NOT HG_EXECUTABLE)
+      message(FATAL_ERROR "error: could not find hg for clone of ${name}")
+    endif()
+
+    set(hg_tag "${_EP_HG_TAG}")
+    if(NOT hg_tag)
+      set(hg_tag "tip")
+    endif()
+
+    # The command doesn't expose any details, so we need to record additional
+    # information in the RepositoryInfo.txt file. For the download step, only
+    # the things specifically affecting the clone operation should be recorded.
+    # If the repo changes, the clone script should be run again.
+    # But if only the tag changes, avoid running the clone script again.
+    # Let the 'always' running update step checkout the new tag.
+    #
+    set(extra_repo_info "repository=${hg_repository}")
+    get_filename_component(src_name "${source_dir}" NAME)
+    get_filename_component(work_dir "${source_dir}" PATH)
+
+    # Since hg clone doesn't succeed if the non-empty source_dir exists,
+    # create a cmake script to invoke as download command.
+    # The script will delete the source directory and then call hg clone.
+    #
+    set(clone_script ${tmp_dir}/${name}-hgclone.cmake)
+    _ep_write_hgclone_script(
+      ${clone_script}
+      ${source_dir}
+      ${HG_EXECUTABLE}
+      ${hg_repository}
+      ${hg_tag}
+      ${src_name}
+      ${work_dir}
+      ${stamp_dir}/${name}-hginfo.txt
+      ${stamp_dir}/${name}-hgclone-lastrun.txt
+    )
+    set(comment "Performing download step (hg clone) for '${name}'")
+    set(cmd ${CMAKE_COMMAND}
+      -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+      -P ${clone_script}
+    )
+
+    if(arg_SCRIPT_FILE)
+      set(step_script_contents "include(\"${clone_script}\")")
+      list(APPEND depends ${clone_script})
+    endif()
+
+  elseif(url)
+    set(method url)
+    get_filename_component(work_dir "${source_dir}" PATH)
+    set(hash "${_EP_URL_HASH}")
+    _ep_get_hash_regex(_ep_hash_regex)
+    if(hash AND NOT "${hash}" MATCHES "${_ep_hash_regex}")
+      _ep_get_hash_algos(_ep_hash_algos)
+      list(JOIN _ep_hash_algos "|" _ep_hash_algos)
+      message(FATAL_ERROR
+        "URL_HASH is set to\n"
+        "  ${hash}\n"
+        "but must be ALGO=value where ALGO is\n"
+        "  ${_ep_hash_algos}\n"
+        "and value is a hex string."
+      )
+    endif()
+    set(md5 "${_EP_URL_MD5}")
+    if(md5 AND NOT "MD5=${md5}" MATCHES "${_ep_hash_regex}")
+      message(FATAL_ERROR
+        "URL_MD5 is set to\n"
+        "  ${md5}\n"
+        "but must be a hex string."
+      )
+    endif()
+    if(md5 AND NOT hash)
+      set(hash "MD5=${md5}")
+    endif()
+    set(extra_repo_info
+      "url(s)=${url}
+hash=${hash}
+      ")
+
+    list(LENGTH url url_list_length)
+    if(NOT "${url_list_length}" STREQUAL "1")
+      foreach(entry IN LISTS url)
+        if(NOT "${entry}" MATCHES "^[a-z]+://")
+          message(FATAL_ERROR
+            "At least one entry of URL is a path (invalid in a list)"
+          )
+        endif()
+      endforeach()
+      if("x${fname}" STREQUAL "x")
+        list(GET url 0 fname)
+      endif()
+    endif()
+
+    if(IS_DIRECTORY "${url}")
+      get_filename_component(abs_dir "${url}" ABSOLUTE)
+      set(comment "Performing download step (DIR copy) for '${name}'")
+      set(cmd
+        ${CMAKE_COMMAND} -E rm -rf ${source_dir}
+        COMMAND ${CMAKE_COMMAND} -E copy_directory ${abs_dir} ${source_dir}
+      )
+      if(arg_SCRIPT_FILE)
+        # While it may be tempting to implement the two operations directly
+        # with file(), the behavior is different. file(COPY) preserves input
+        # file timestamps, which we don't want. Therefore, still use the same
+        # external commands so that we get the same behavior.
+        _ep_add_script_commands(
+          step_script_contents
+          "${work_dir}"
+          "${cmd}"   # Must be a single quoted argument
+        )
+      endif()
+    else()
+      set(no_extract "${_EP_DOWNLOAD_NO_EXTRACT}")
+      string(APPEND extra_repo_info "no_extract=${no_extract}\n")
+      set(verify_script "${stamp_dir}/verify-${name}.cmake")
+      if("${url}" MATCHES "^[a-z]+://")
+        # TODO: Should download and extraction be different steps?
+        if("x${fname}" STREQUAL "x")
+          set(fname "${url}")
+        endif()
+        set(ext_regex [[7z|tar|tar\.bz2|tar\.gz|tar\.xz|tbz2|tgz|txz|zip]])
+        if("${fname}" MATCHES "([^/\\?#]+(\\.|=)(${ext_regex}))([/?#].*)?$")
+          set(fname "${CMAKE_MATCH_1}")
+        elseif(no_extract)
+          get_filename_component(fname "${fname}" NAME)
+        else()
+          # Fall back to a default file name.  The actual file name does not
+          # matter because it is used only internally and our extraction tool
+          # inspects the file content directly.  If it turns out the wrong URL
+          # was given that will be revealed during the build which is an easier
+          # place for users to diagnose than an error here anyway.
+          set(fname "archive.tar")
+        endif()
+        string(REPLACE ";" "-" fname "${fname}")
+        set(file ${download_dir}/${fname})
+        set(timeout "${_EP_TIMEOUT}")
+        set(inactivity_timeout "${_EP_INACTIVITY_TIMEOUT}")
+        set(no_progress "${_EP_DOWNLOAD_NO_PROGRESS}")
+        _ep_get_tls_version(${name} tls_version)
+        _ep_get_tls_verify(${name} tls_verify)
+        _ep_get_tls_cainfo(${name} tls_cainfo)
+        _ep_get_netrc(${name} netrc)
+        _ep_get_netrc_file(${name} netrc_file)
+        set(http_username "${_EP_HTTP_USERNAME}")
+        set(http_password "${_EP_HTTP_PASSWORD}")
+        set(http_headers  "${_EP_HTTP_HEADER}")
+        set(download_script "${stamp_dir}/download-${name}.cmake")
+        _ep_write_downloadfile_script(
+          "${download_script}"
+          "${url}"
+          "${file}"
+          "${timeout}"
+          "${inactivity_timeout}"
+          "${no_progress}"
+          "${hash}"
+          "${tls_version}"
+          "${tls_verify}"
+          "${tls_cainfo}"
+          "${http_username}:${http_password}"
+          "${http_headers}"
+          "${netrc}"
+          "${netrc_file}"
+        )
+        set(cmd
+          ${CMAKE_COMMAND}
+            -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+            -P "${download_script}"
+          COMMAND
+        )
+        if(arg_SCRIPT_FILE)
+          set(step_script_contents "include(\"${download_script}\")\n")
+        endif()
+
+        if (no_extract)
+          set(steps "download and verify")
+        else ()
+          set(steps "download, verify and extract")
+        endif ()
+        set(comment "Performing download step (${steps}) for '${name}'")
+        # already verified by 'download_script'
+        file(WRITE "${verify_script}" "")
+
+        # Rather than adding everything to the RepositoryInfo.txt file, it is
+        # more robust to just depend on the download script. That way, we will
+        # re-download if any aspect of the download changes.
+        list(APPEND depends "${download_script}")
+      else()
+        set(file "${url}")
+        if (no_extract)
+          set(steps "verify")
+        else ()
+          set(steps "verify and extract")
+        endif ()
+        set(comment "Performing download step (${steps}) for '${name}'")
+        _ep_write_verifyfile_script(
+          "${verify_script}"
+          "${file}"
+          "${hash}"
+        )
+      endif()
+      list(APPEND cmd ${CMAKE_COMMAND}
+        -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+        -P ${verify_script}
+      )
+      if(arg_SCRIPT_FILE)
+        string(APPEND step_script_contents "include(\"${verify_script}\")\n")
+        list(APPEND depends ${verify_script})
+      endif()
+      set(extract_timestamp "${_EP_DOWNLOAD_EXTRACT_TIMESTAMP}")
+      if(no_extract)
+        if(DEFINED _EP_DOWNLOAD_EXTRACT_TIMESTAMP)
+          message(FATAL_ERROR
+            "Cannot specify DOWNLOAD_EXTRACT_TIMESTAMP when using "
+            "DOWNLOAD_NO_EXTRACT TRUE"
+          )
+        endif()
+        if(arg_SCRIPT_FILE)
+          # There's no target to record the location of the downloaded file.
+          # Instead, we copy it to the source directory within the script,
+          # which is what FetchContent always does in this situation.
+          cmake_path(SET safe_file NORMALIZE "${file}")
+          cmake_path(GET safe_file FILENAME filename)
+          string(APPEND step_script_contents
+            "file(COPY_FILE\n"
+            "  \"${file}\"\n"
+            "  \"${source_dir}/${filename}\"\n"
+            "  ONLY_IF_DIFFERENT\n"
+            "  INPUT_MAY_BE_RECENT\n"
+            ")"
+          )
+          list(APPEND depends ${source_dir}/${filename})
+        else()
+          set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file})
+        endif()
+      else()
+        if(NOT DEFINED _EP_DOWNLOAD_EXTRACT_TIMESTAMP)
+          # Default depends on policy CMP0135
+          if(_EP_CMP0135 STREQUAL "")
+            message(AUTHOR_WARNING
+              "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy "
+              "CMP0135 is not set. The policy's OLD behavior will be used. "
+              "When using a URL download, the timestamps of extracted files "
+              "should preferably be that of the time of extraction, otherwise "
+              "code that depends on the extracted contents might not be "
+              "rebuilt if the URL changes. The OLD behavior preserves the "
+              "timestamps from the archive instead, but this is usually not "
+              "what you want. Update your project to the NEW behavior or "
+              "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of "
+              "true to avoid this robustness issue."
+            )
+            set(extract_timestamp TRUE)
+          elseif(_EP_CMP0135 STREQUAL "NEW")
+            set(extract_timestamp FALSE)
+          else()
+            set(extract_timestamp TRUE)
+          endif()
+        endif()
+        if(extract_timestamp)
+          set(options "")
+        else()
+          set(options "--touch")
+        endif()
+        set(extract_script "${stamp_dir}/extract-${name}.cmake")
+        _ep_write_extractfile_script(
+          "${extract_script}"
+          "${name}"
+          "${file}"
+          "${source_dir}"
+          "${options}"
+        )
+        list(APPEND cmd
+          COMMAND ${CMAKE_COMMAND}
+            -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+            -P ${extract_script}
+        )
+        if(arg_SCRIPT_FILE)
+          string(APPEND step_script_contents "include(\"${extract_script}\")\n")
+          list(APPEND depends ${extract_script})
+        endif()
+      endif ()
+    endif()
+  else()
+    set(method source_dir)
+    _ep_is_dir_empty("${source_dir}" empty)
+    if(${empty})
+      message(FATAL_ERROR
+        "No download info given for '${name}' and its source directory:\n"
+        " ${source_dir}\n"
+        "is not an existing non-empty directory.  Please specify one of:\n"
+        " * SOURCE_DIR with an existing non-empty directory\n"
+        " * DOWNLOAD_COMMAND\n"
+        " * URL\n"
+        " * GIT_REPOSITORY\n"
+        " * SVN_REPOSITORY\n"
+        " * HG_REPOSITORY\n"
+        " * CVS_REPOSITORY and CVS_MODULE"
+      )
+    endif()
+    if(arg_SCRIPT_FILE)
+      set(step_script_contents "message(VERBOSE [[Using SOURCE_DIR as is]])")
+    endif()
+  endif()
+
+  # We use configure_file() to write the repo_info_file so that the file's
+  # timestamp is not updated if we don't change the contents
+
+  set(repo_info_file ${stamp_dir}/${name}-${method}info.txt)
+  list(APPEND depends ${repo_info_file})
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/RepositoryInfo.txt.in"
+    "${repo_info_file}"
+    @ONLY
+  )
+
+  if(arg_SCRIPT_FILE)
+    set(step_name download)
+    configure_file(
+      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/stepscript.cmake.in"
+      "${arg_SCRIPT_FILE}"
+      @ONLY
+    )
+    set(${arg_DEPENDS_VARIABLE} "${depends}" PARENT_SCOPE)
+    return()
+  endif()
+
+  # Nothing below this point is applicable when we've been asked to put the
+  # download step in a script file (which is the FetchContent case).
+
+  if(_EP_LOG_DOWNLOAD)
+    set(log LOG 1)
+  else()
+    set(log "")
+  endif()
+
+  if(_EP_USES_TERMINAL_DOWNLOAD)
+    set(uses_terminal USES_TERMINAL 1)
+  else()
+    set(uses_terminal "")
+  endif()
+
+  set(__cmdQuoted)
+  foreach(__item IN LISTS cmd)
+    string(APPEND __cmdQuoted " [==[${__item}]==]")
+  endforeach()
+  cmake_language(EVAL CODE "
+    ExternalProject_Add_Step(\${name} download
+      INDEPENDENT TRUE
+      COMMENT \${comment}
+      COMMAND ${__cmdQuoted}
+      WORKING_DIRECTORY \${work_dir}
+      DEPENDS \${depends}
+      DEPENDEES mkdir
+      ${log}
+  ${uses_terminal}
+    )"
+  )
+endfunction()
+
+function(_ep_get_update_disconnected var name)
+  # Note that the arguments are assumed to have already been parsed and have
+  # been translated into variables with the prefix _EP_... by a call to
+  # ep_parse_arguments() or ep_parse_arguments_to_vars().
+  if(DEFINED _EP_UPDATE_DISCONNECTED)
+    set(update_disconnected "${_EP_UPDATE_DISCONNECTED}")
+  else()
+    get_property(update_disconnected
+      DIRECTORY
+      PROPERTY EP_UPDATE_DISCONNECTED
+    )
+  endif()
+  set(${var} "${update_disconnected}" PARENT_SCOPE)
+endfunction()
+
+function(_ep_add_update_command name)
+  set(noValueOptions )
+  set(singleValueOptions
+    SCRIPT_FILE       # These should only be used by FetchContent
+    DEPEND_VARIABLE   #
+  )
+  set(multiValueOptions )
+  cmake_parse_arguments(PARSE_ARGV 1 arg
+    "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}"
+  )
+
+  # The various _EP_... variables mentioned here and throughout this function
+  # are expected to already have been set by the caller via a call to
+  # _ep_parse_arguments() or ep_parse_arguments_to_vars(). Other variables
+  # with different names are assigned to for historical reasons only to keep
+  # the code more readable and minimize change.
+
+  set(source_dir     "${_EP_SOURCE_DIR}")
+  set(stamp_dir      "${_EP_STAMP_DIR}")
+  set(tmp_dir        "${_EP_TMP_DIR}")
+
+  set(cmd            "${_EP_UPDATE_COMMAND}")
+  set(cvs_repository "${_EP_CVS_REPOSITORY}")
+  set(svn_repository "${_EP_SVN_REPOSITORY}")
+  set(git_repository "${_EP_GIT_REPOSITORY}")
+  set(hg_repository  "${_EP_HG_REPOSITORY}")
+
+  _ep_get_update_disconnected(update_disconnected ${name})
+
+  set(work_dir)
+  set(comment)
+  set(always)
+  set(file_deps)
+
+  if(DEFINED _EP_UPDATE_COMMAND)
+    set(work_dir ${source_dir})
+    if(NOT "x${cmd}" STREQUAL "x")
+      set(always 1)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+
+  elseif(cvs_repository)
+    if(NOT CVS_EXECUTABLE)
+      message(FATAL_ERROR "error: could not find cvs for update of ${name}")
+    endif()
+    set(work_dir ${source_dir})
+    set(comment "Performing update step (CVS update) for '${name}'")
+    set(cvs_tag "${_EP_CVS_TAG}")
+    set(cmd ${CVS_EXECUTABLE} -d ${cvs_repository} -q up -dP ${cvs_tag})
+    set(always 1)
+
+    if(arg_SCRIPT_FILE)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+
+  elseif(svn_repository)
+    if(NOT Subversion_SVN_EXECUTABLE)
+      message(FATAL_ERROR "error: could not find svn for update of ${name}")
+    endif()
+    set(work_dir ${source_dir})
+    set(comment "Performing update step (SVN update) for '${name}'")
+    set(svn_revision   "${_EP_SVN_REVISION}")
+    set(svn_username   "${_EP_SVN_USERNAME}")
+    set(svn_password   "${_EP_SVN_PASSWORD}")
+    set(svn_trust_cert "${_EP_SVN_TRUST_CERT}")
+    set(uses_terminal  "${_EP_USES_TERMINAL_UPDATE}")
+    # The --trust-server-cert option requires --non-interactive
+    if(uses_terminal AND NOT svn_trust_cert)
+      set(svn_interactive_args "")
+    else()
+      set(svn_interactive_args "--non-interactive")
+    endif()
+    set(svn_user_pw_args "")
+    if(DEFINED svn_username)
+      set(svn_user_pw_args ${svn_user_pw_args} "--username=${svn_username}")
+    endif()
+    if(DEFINED svn_password)
+      set(svn_user_pw_args ${svn_user_pw_args} "--password=${svn_password}")
+    endif()
+    if(svn_trust_cert)
+      set(svn_trust_cert_args --trust-server-cert)
+    endif()
+    set(cmd
+      ${Subversion_SVN_EXECUTABLE}
+      up
+      ${svn_revision}
+      ${svn_interactive_args}
+      ${svn_trust_cert_args}
+      ${svn_user_pw_args}
+    )
+    set(always 1)
+
+    if(arg_SCRIPT_FILE)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+
+  elseif(git_repository)
+    # FetchContent gives us these directly, so don't try to recompute them
+    if(NOT GIT_EXECUTABLE OR NOT GIT_VERSION_STRING)
+      unset(CMAKE_MODULE_PATH) # Use CMake builtin find module
+      find_package(Git QUIET)
+      if(NOT GIT_EXECUTABLE)
+        message(FATAL_ERROR "error: could not find git for fetch of ${name}")
+      endif()
+    endif()
+    set(work_dir ${source_dir})
+    set(comment "Performing update step for '${name}'")
+    set(comment_disconnected "Performing disconnected update step for '${name}'")
+
+    set(git_tag "${_EP_GIT_TAG}")
+    if(NOT git_tag)
+      set(git_tag "master")
+    endif()
+
+    set(git_remote_name "${_EP_GIT_REMOTE_NAME}")
+    if(NOT git_remote_name)
+      set(git_remote_name "origin")
+    endif()
+
+    set(git_init_submodules TRUE)
+    if(DEFINED _EP_GIT_SUBMODULES)
+      set(git_submodules "${_EP_GIT_SUBMODULES}")
+      if(git_submodules STREQUAL "" AND _EP_CMP0097 STREQUAL "NEW")
+        set(git_init_submodules FALSE)
+      endif()
+    endif()
+
+    set(git_update_strategy "${_EP_GIT_REMOTE_UPDATE_STRATEGY}")
+    if(NOT git_update_strategy)
+      set(git_update_strategy "${CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY}")
+    endif()
+    if(NOT git_update_strategy)
+      set(git_update_strategy REBASE)
+    endif()
+    set(strategies CHECKOUT REBASE REBASE_CHECKOUT)
+    if(NOT git_update_strategy IN_LIST strategies)
+      message(FATAL_ERROR
+        "'${git_update_strategy}' is not one of the supported strategies: "
+        "${strategies}"
+      )
+    endif()
+
+    _ep_get_git_submodules_recurse(git_submodules_recurse)
+
+    _ep_get_tls_version(${name} tls_version)
+    _ep_get_tls_verify(${name} tls_verify)
+
+    set(update_script "${tmp_dir}/${name}-gitupdate.cmake")
+    list(APPEND file_deps ${update_script})
+    _ep_write_gitupdate_script(
+      "${update_script}"
+      "${GIT_EXECUTABLE}"
+      "${git_tag}"
+      "${git_remote_name}"
+      "${git_init_submodules}"
+      "${git_submodules_recurse}"
+      "${git_submodules}"
+      "${git_repository}"
+      "${work_dir}"
+      "${git_update_strategy}"
+      "${tls_version}"
+      "${tls_verify}"
+    )
+    set(cmd ${CMAKE_COMMAND}
+      -Dcan_fetch=YES
+      -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+      -P ${update_script}
+    )
+    set(cmd_disconnected ${CMAKE_COMMAND}
+      -Dcan_fetch=NO
+      -DCMAKE_MESSAGE_LOG_LEVEL=VERBOSE
+      -P ${update_script}
+    )
+    set(always 1)
+
+    if(arg_SCRIPT_FILE)
+      if(update_disconnected)
+        set(can_fetch_default NO)
+      else()
+        set(can_fetch_default YES)
+      endif()
+      set(step_script_contents "include(\"${update_script}\")")
+    endif()
+
+  elseif(hg_repository)
+    if(NOT HG_EXECUTABLE)
+      message(FATAL_ERROR "error: could not find hg for pull of ${name}")
+    endif()
+    set(work_dir ${source_dir})
+    set(comment "Performing update step (hg pull) for '${name}'")
+    set(comment_disconnected "Performing disconnected update step for '${name}'")
+
+    set(hg_tag "${_EP_HG_TAG}")
+    if(NOT hg_tag)
+      set(hg_tag "tip")
+    endif()
+
+    if("${HG_VERSION_STRING}" STREQUAL "2.1")
+      set(notesAnchor
+        "#A2.1.1:_revert_pull_return_code_change.2C_compile_issue_on_OS_X"
+      )
+      message(WARNING
+        "Mercurial 2.1 does not distinguish an empty pull from a failed pull:
+ http://mercurial.selenic.com/wiki/UpgradeNotes${notesAnchor}
+ http://thread.gmane.org/gmane.comp.version-control.mercurial.devel/47656
+Update to Mercurial >= 2.1.1.
+")
+    endif()
+
+    set(cmd
+      ${HG_EXECUTABLE} pull
+      COMMAND ${HG_EXECUTABLE} update ${hg_tag}
+    )
+    set(cmd_disconnected ${HG_EXECUTABLE} update ${hg_tag})
+    set(always 1)
+
+    if(arg_SCRIPT_FILE)
+      # These commands are simple, and we know whether updates need to be
+      # disconnected or not for this case, so write them directly instead of
+      # forming them from "cmd" and "cmd_disconnected".
+      if(NOT update_disconnected)
+        string(APPEND step_script_contents
+          "execute_process(\n"
+          "  WORKING_DIRECTORY \"${work_dir}\"\n"
+          "  COMMAND_ERROR_IS_FATAL LAST\n"
+          "  COMMAND \"${HG_EXECUTABLE}\" pull\n"
+          ")"
+        )
+      endif()
+      string(APPEND step_script_contents
+        "execute_process(\n"
+        "  WORKING_DIRECTORY \"${work_dir}\"\n"
+        "  COMMAND_ERROR_IS_FATAL LAST\n"
+        "  COMMAND \"${HG_EXECUTABLE}\" update \"${hg_tag}\"\n"
+        ")"
+      )
+    endif()
+  endif()
+
+  # We use configure_file() to write the update_info_file so that the file's
+  # timestamp is not updated if we don't change the contents
+  if(NOT DEFINED cmd_disconnected)
+    set(cmd_disconnected "${cmd}")
+  endif()
+  set(update_info_file ${stamp_dir}/${name}-update-info.txt)
+  list(APPEND file_deps ${update_info_file})
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/UpdateInfo.txt.in"
+    "${update_info_file}"
+    @ONLY
+  )
+
+  if(arg_SCRIPT_FILE)
+    set(step_name update)
+    configure_file(
+      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/stepscript.cmake.in"
+      "${arg_SCRIPT_FILE}"
+      @ONLY
+    )
+    set(${arg_DEPENDS_VARIABLE} "${file_deps}" PARENT_SCOPE)
+    return()
+  endif()
+
+  # Nothing below this point is applicable when we've been asked to put the
+  # update step in a script file (which is the FetchContent case).
+
+  if(_EP_LOG_UPDATE)
+    set(log LOG 1)
+  else()
+    set(log "")
+  endif()
+
+  if(_EP_USES_TERMINAL_UPDATE)
+    set(uses_terminal USES_TERMINAL 1)
+  else()
+    set(uses_terminal "")
+  endif()
+
+  set(__cmdQuoted)
+  foreach(__item IN LISTS cmd)
+    string(APPEND __cmdQuoted " [==[${__item}]==]")
+  endforeach()
+  cmake_language(EVAL CODE "
+    ExternalProject_Add_Step(${name} update
+      INDEPENDENT TRUE
+      COMMENT \${comment}
+      COMMAND ${__cmdQuoted}
+      ALWAYS \${always}
+      EXCLUDE_FROM_MAIN \${update_disconnected}
+      WORKING_DIRECTORY \${work_dir}
+      DEPENDEES download
+      DEPENDS \${file_deps}
+      ${log}
+  ${uses_terminal}
+    )"
+  )
+  if(update_disconnected)
+    if(NOT DEFINED comment_disconnected)
+      set(comment_disconnected "${comment}")
+    endif()
+    set(__cmdQuoted)
+    foreach(__item IN LISTS cmd_disconnected)
+      string(APPEND __cmdQuoted " [==[${__item}]==]")
+    endforeach()
+
+    cmake_language(EVAL CODE "
+      ExternalProject_Add_Step(${name} update_disconnected
+        INDEPENDENT TRUE
+        COMMENT \${comment_disconnected}
+        COMMAND ${__cmdQuoted}
+        WORKING_DIRECTORY \${work_dir}
+        DEPENDEES download
+        DEPENDS \${file_deps}
+        ${log}
+    ${uses_terminal}
+      )"
+    )
+  endif()
+
+endfunction()
+
+
+function(_ep_add_patch_command name)
+  set(noValueOptions )
+  set(singleValueOptions
+    SCRIPT_FILE        # These should only be used by FetchContent
+  )
+  set(multiValueOptions )
+  cmake_parse_arguments(PARSE_ARGV 1 arg
+    "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}"
+  )
+
+  # The various _EP_... variables mentioned here and throughout this function
+  # are expected to already have been set by the caller via a call to
+  # _ep_parse_arguments() or ep_parse_arguments_to_vars(). Other variables
+  # with different names are assigned to for historical reasons only to keep
+  # the code more readable and minimize change.
+
+  set(source_dir "${_EP_SOURCE_DIR}")
+  set(stamp_dir  "${_EP_STAMP_DIR}")
+
+  set(cmd "${_EP_PATCH_COMMAND}")
+  set(step_script_contents "")
+
+  set(work_dir)
+  if(DEFINED _EP_PATCH_COMMAND)
+    set(work_dir ${source_dir})
+    if(arg_SCRIPT_FILE)
+      _ep_add_script_commands(
+        step_script_contents
+        "${work_dir}"
+        "${cmd}"   # Must be a single quoted argument
+      )
+    endif()
+  endif()
+
+  # We use configure_file() to write the patch_info_file so that the file's
+  # timestamp is not updated if we don't change the contents
+  set(patch_info_file ${stamp_dir}/${name}-patch-info.txt)
+  configure_file(
+    "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/PatchInfo.txt.in"
+    "${patch_info_file}"
+    @ONLY
+  )
+
+  if(arg_SCRIPT_FILE)
+    set(step_name patch)
+    configure_file(
+      "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/stepscript.cmake.in"
+      "${arg_SCRIPT_FILE}"
+      @ONLY
+    )
+    return()
+  endif()
+
+  # Nothing below this point is applicable when we've been asked to put the
+  # patch step in a script file (which is the FetchContent case).
+
+  if(_EP_LOG_PATCH)
+    set(log LOG 1)
+  else()
+    set(log "")
+  endif()
+
+  if(_EP_USES_TERMINAL_PATCH)
+    set(uses_terminal USES_TERMINAL 1)
+  else()
+    set(uses_terminal "")
+  endif()
+
+  _ep_get_update_disconnected(update_disconnected ${name})
+
+  set(__cmdQuoted)
+  foreach(__item IN LISTS cmd)
+    string(APPEND __cmdQuoted " [==[${__item}]==]")
+  endforeach()
+  cmake_language(EVAL CODE "
+    ExternalProject_Add_Step(${name} patch
+      INDEPENDENT TRUE
+      COMMAND ${__cmdQuoted}
+      WORKING_DIRECTORY \${work_dir}
+      EXCLUDE_FROM_MAIN \${update_disconnected}
+      DEPENDEES update
+      DEPENDS \${patch_info_file}
+      ${log}
+  ${uses_terminal}
+    )"
+  )
+
+  if(update_disconnected)
+    cmake_language(EVAL CODE "
+      ExternalProject_Add_Step(${name} patch_disconnected
+        INDEPENDENT TRUE
+        COMMAND ${__cmdQuoted}
+        WORKING_DIRECTORY \${work_dir}
+        DEPENDEES update_disconnected
+        DEPENDS \${patch_info_file}
+        ${log}
+    ${uses_terminal}
+      )"
+    )
+  endif()
+
+endfunction()
+
+
+macro(_ep_get_add_keywords out_var)
+  set(${out_var}
+    #
+    # Directory options
+    #
+    PREFIX
+    TMP_DIR
+    STAMP_DIR
+    LOG_DIR
+    DOWNLOAD_DIR
+    SOURCE_DIR
+    BINARY_DIR
+    INSTALL_DIR
+    #
+    # Download step options
+    #
+    DOWNLOAD_COMMAND
+    #
+    URL
+    URL_HASH
+    URL_MD5
+    DOWNLOAD_NAME
+    DOWNLOAD_EXTRACT_TIMESTAMP
+    DOWNLOAD_NO_EXTRACT
+    DOWNLOAD_NO_PROGRESS
+    TIMEOUT
+    INACTIVITY_TIMEOUT
+    HTTP_USERNAME
+    HTTP_PASSWORD
+    HTTP_HEADER
+    TLS_VERSION    # Also used for git clone operations
+    TLS_VERIFY     # Also used for git clone operations
+    TLS_CAINFO
+    NETRC
+    NETRC_FILE
+    #
+    GIT_REPOSITORY
+    GIT_TAG
+    GIT_REMOTE_NAME
+    GIT_SUBMODULES
+    GIT_SUBMODULES_RECURSE
+    GIT_SHALLOW
+    GIT_PROGRESS
+    GIT_CONFIG
+    GIT_REMOTE_UPDATE_STRATEGY
+    #
+    SVN_REPOSITORY
+    SVN_REVISION
+    SVN_USERNAME
+    SVN_PASSWORD
+    SVN_TRUST_CERT
+    #
+    HG_REPOSITORY
+    HG_TAG
+    #
+    CVS_REPOSITORY
+    CVS_MODULE
+    CVS_TAG
+    #
+    # Update step options
+    #
+    UPDATE_COMMAND
+    UPDATE_DISCONNECTED
+    #
+    # Patch step options
+    #
+    PATCH_COMMAND
+    #
+    # Configure step options
+    #
+    CONFIGURE_COMMAND
+    CMAKE_COMMAND
+    CMAKE_GENERATOR
+    CMAKE_GENERATOR_PLATFORM
+    CMAKE_GENERATOR_TOOLSET
+    CMAKE_GENERATOR_INSTANCE
+    CMAKE_ARGS
+    CMAKE_CACHE_ARGS
+    CMAKE_CACHE_DEFAULT_ARGS
+    SOURCE_SUBDIR
+    CONFIGURE_HANDLED_BY_BUILD
+    #
+    # Build step options
+    #
+    BUILD_COMMAND
+    BUILD_IN_SOURCE
+    BUILD_ALWAYS
+    BUILD_BYPRODUCTS
+    BUILD_JOB_SERVER_AWARE
+    #
+    # Install step options
+    #
+    INSTALL_COMMAND
+    INSTALL_BYPRODUCTS
+    #
+    # Test step options
+    #
+    TEST_COMMAND
+    TEST_BEFORE_INSTALL
+    TEST_AFTER_INSTALL
+    TEST_EXCLUDE_FROM_MAIN
+    #
+    # Logging options
+    #
+    LOG_DOWNLOAD
+    LOG_UPDATE
+    LOG_PATCH
+    LOG_CONFIGURE
+    LOG_BUILD
+    LOG_INSTALL
+    LOG_TEST
+    LOG_MERGED_STDOUTERR
+    LOG_OUTPUT_ON_FAILURE
+    #
+    # Terminal access options
+    #
+    USES_TERMINAL_DOWNLOAD
+    USES_TERMINAL_UPDATE
+    USES_TERMINAL_PATCH
+    USES_TERMINAL_CONFIGURE
+    USES_TERMINAL_BUILD
+    USES_TERMINAL_INSTALL
+    USES_TERMINAL_TEST
+    #
+    # Target options
+    #
+    DEPENDS
+    EXCLUDE_FROM_ALL
+    STEP_TARGETS
+    INDEPENDENT_STEP_TARGETS
+    #
+    # Miscellaneous options
+    #
+    LIST_SEPARATOR
+    #
+    # Internal options (undocumented)
+    #
+    EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR
+  )
+endmacro()
diff --git a/Modules/ExternalProject/stepscript.cmake.in b/Modules/ExternalProject/stepscript.cmake.in
new file mode 100644
index 0000000..12e157e
--- /dev/null
+++ b/Modules/ExternalProject/stepscript.cmake.in
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.29)
+
+message(VERBOSE "Executing @step_name@ step for @name@")
+
+block(SCOPE_FOR VARIABLES)
+
+@step_script_contents@
+
+endblock()
diff --git a/Modules/ExternalProject/verify.cmake.in b/Modules/ExternalProject/verify.cmake.in
index c06da4e..30d0487 100644
--- a/Modules/ExternalProject/verify.cmake.in
+++ b/Modules/ExternalProject/verify.cmake.in
@@ -12,7 +12,7 @@
 endif()
 
 if("@ALGO@" STREQUAL "")
-  message(WARNING "File will not be verified since no URL_HASH specified")
+  message(WARNING "File cannot be verified since no URL_HASH specified")
   return()
 endif()
 
@@ -20,7 +20,7 @@
   message(FATAL_ERROR "EXPECT_VALUE can't be empty")
 endif()
 
-message(STATUS "verifying file...
+message(VERBOSE "verifying file...
      file='@LOCAL@'")
 
 file("@ALGO@" "@LOCAL@" actual_value)
@@ -34,4 +34,4 @@
 ")
 endif()
 
-message(STATUS "verifying file... done")
+message(VERBOSE "verifying file... done")
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index 3c01c2a..5006069 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -141,6 +141,11 @@
   exception, see :command:`FetchContent_MakeAvailable` for details on how that
   affects behavior.
 
+  .. versionchanged:: 3.30
+    When policy :policy:`CMP0168` is set to ``NEW``, some output-related and
+    directory-related options are ignored.  See the policy documentation for
+    details.
+
   In most cases, ``<contentOptions>`` will just be a couple of options defining
   the download method and method-specific details like a commit tag or archive
   hash.  For example:
@@ -437,12 +442,13 @@
   like variable or directory scope.  Therefore, it doesn't matter where in the
   project the details were previously declared, as long as they have been
   declared before the call to ``FetchContent_Populate()``.  Those saved details
-  are then used to construct a call to :command:`ExternalProject_Add` in a
-  private sub-build to perform the content population immediately.  The
-  implementation of ``ExternalProject_Add()`` ensures that if the content has
-  already been populated in a previous CMake run, that content will be reused
-  rather than repopulating them again.  For the common case where population
-  involves downloading content, the cost of the download is only paid once.
+  are then used to populate the content using a method based on
+  :command:`ExternalProject_Add` (see policy :policy:`CMP0168` for important
+  behavioral aspects of how that is done).  The implementation ensures that if
+  the content has already been populated in a previous CMake run, that content
+  will be reused rather than repopulating them again.  For the common case
+  where population involves downloading content, the cost of the download is
+  only paid once.
 
   An internal global property records when a particular content population
   request has been processed.  If ``FetchContent_Populate()`` is called more
@@ -529,6 +535,13 @@
     cache variable has no effect on ``FetchContent_Populate()`` calls where the
     content details are provided directly.
 
+    .. versionchanged:: 3.30
+      The ``QUIET`` option and global ``FETCHCONTENT_QUIET`` variable have no
+      effect when policy :policy:`CMP0168` is set to ``NEW``. The output is
+      still quiet by default in that case, but verbosity is controlled by the
+      message logging level (see :variable:`CMAKE_MESSAGE_LOG_LEVEL` and
+      :option:`--log-level <cmake --log-level>`).
+
   ``SUBBUILD_DIR``
     The ``SUBBUILD_DIR`` argument can be provided to change the location of the
     sub-build created to perform the population.  The default value is
@@ -538,6 +551,10 @@
     This option should not be confused with the ``SOURCE_SUBDIR`` option which
     only affects the :command:`FetchContent_MakeAvailable` command.
 
+    .. versionchanged:: 3.30
+      ``SUBBUILD_DIR`` is ignored when policy :policy:`CMP0168` is set to
+      ``NEW``, since there is no sub-build in that case.
+
   ``SOURCE_DIR``, ``BINARY_DIR``
     The ``SOURCE_DIR`` and ``BINARY_DIR`` arguments are supported by
     :command:`ExternalProject_Add`, but different default values are used by
@@ -548,7 +565,7 @@
     :variable:`CMAKE_CURRENT_BINARY_DIR`.
 
   In addition to the above explicit options, any other unrecognized options are
-  passed through unmodified to :command:`ExternalProject_Add` to perform the
+  passed through unmodified to :command:`ExternalProject_Add` to set up the
   download, patch and update steps.  The following options are explicitly
   prohibited (they are disabled by the ``FetchContent_Populate()`` command):
 
@@ -564,6 +581,11 @@
   :variable:`CMAKE_MAKE_PROGRAM` variables will need to be set appropriately
   on the command line invoking the script.
 
+  .. versionchanged:: 3.30
+    If policy :policy:`CMP0168` is set to ``NEW``, no sub-build is used.
+    Within CMake's script mode, that allows ``FetchContent_Populate()`` to be
+    called without any build tool or CMake generator.
+
   .. versionadded:: 3.18
     Added support for the ``DOWNLOAD_NO_EXTRACT`` option.
 
@@ -675,6 +697,13 @@
   problems with hung downloads, temporarily switching this option off may
   help diagnose which content population is causing the issue.
 
+  .. versionchanged:: 3.30
+    ``FETCHCONTENT_QUIET`` is ignored if policy :policy:`CMP0168` is set to
+    ``NEW``.  The output is still quiet by default in that case, but verbosity
+    is controlled by the message logging level (see
+    :variable:`CMAKE_MESSAGE_LOG_LEVEL` and
+    :option:`--log-level <cmake --log-level>`).
+
 .. variable:: FETCHCONTENT_FULLY_DISCONNECTED
 
   When this option is enabled, no attempt is made to download or update
@@ -682,7 +711,7 @@
   a previous run or the source directories have been pointed at existing
   contents the developer has provided manually (using options described
   further below).  When the developer knows that no changes have been made to
-  any content details, turning this option ``ON`` can significantly speed up
+  any content details, turning this option ``ON`` can speed up
   the configure stage.  It is ``OFF`` by default.
 
   .. note::
@@ -1167,7 +1196,13 @@
   set(__findPackageArgs)
   set(__sawQuietKeyword NO)
   set(__sawGlobalKeyword NO)
+  set(__direct_population NO)
   foreach(__item IN LISTS ARGN)
+    if(__item STREQUAL "__DIRECT_POPULATION")
+      set(__direct_population YES)
+      continue()
+    endif()
+
     if(DEFINED __findPackageArgs)
       # All remaining args are for find_package()
       string(APPEND __findPackageArgs " [==[${__item}]==]")
@@ -1206,6 +1241,10 @@
     string(APPEND __cmdArgs " [==[${__item}]==]")
   endforeach()
 
+  set_property(GLOBAL PROPERTY
+    "_FetchContent_${contentNameLower}_direct_population" ${__direct_population}
+  )
+
   define_property(GLOBAL PROPERTY ${savedDetailsPropertyName})
   cmake_language(EVAL CODE
     "set_property(GLOBAL PROPERTY ${savedDetailsPropertyName} ${__cmdArgs})"
@@ -1372,16 +1411,24 @@
   endif()
 
   # Add back in the keyword args we pulled out and potentially tweaked/added
+  set(forward_args "${ARG_UNPARSED_ARGUMENTS}")
   set(sep EXTERNALPROJECT_INTERNAL_ARGUMENT_SEPARATOR)
   foreach(key IN LISTS oneValueArgs)
     if(DEFINED ARG_${key})
-      list(PREPEND ARG_UNPARSED_ARGUMENTS ${key} "${ARG_${key}}" ${sep})
+      list(PREPEND forward_args ${key} "${ARG_${key}}" ${sep})
       set(sep "")
     endif()
   endforeach()
 
+  cmake_policy(GET CMP0168 cmp0168
+    PARENT_SCOPE # undocumented, do not use outside of CMake
+  )
+  if(cmp0168 STREQUAL "NEW")
+    list(PREPEND forward_args __DIRECT_POPULATION ${sep})
+  endif()
+
   set(__argsQuoted)
-  foreach(__item IN LISTS ARG_UNPARSED_ARGUMENTS)
+  foreach(__item IN LISTS forward_args)
     string(APPEND __argsQuoted " [==[${__item}]==]")
   endforeach()
   cmake_language(EVAL CODE
@@ -1498,7 +1545,7 @@
 # The value of contentName will always have been lowercased by the caller.
 # All other arguments are assumed to be options that are understood by
 # ExternalProject_Add(), except for QUIET and SUBBUILD_DIR.
-function(__FetchContent_directPopulate contentName)
+function(__FetchContent_doPopulation contentName)
 
   set(options
       QUIET
@@ -1533,8 +1580,14 @@
   cmake_parse_arguments(PARSE_ARGV 1 ARG
     "${options}" "${oneValueArgs}" "${multiValueArgs}")
 
+  get_property(direct_population GLOBAL PROPERTY
+    "_FetchContent_${contentNameLower}_direct_population"
+  )
+
   if(NOT ARG_SUBBUILD_DIR)
-    message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set")
+    if(NOT direct_population)
+      message(FATAL_ERROR "Internal error: SUBBUILD_DIR not set")
+    endif()
   elseif(NOT IS_ABSOLUTE "${ARG_SUBBUILD_DIR}")
     set(ARG_SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBBUILD_DIR}")
   endif()
@@ -1558,6 +1611,148 @@
   set(${contentName}_SOURCE_DIR "${ARG_SOURCE_DIR}" PARENT_SCOPE)
   set(${contentName}_BINARY_DIR "${ARG_BINARY_DIR}" PARENT_SCOPE)
 
+  if(direct_population)
+    __FetchContent_populateDirect()
+  else()
+    __FetchContent_populateSubbuild()
+  endif()
+endfunction()
+
+
+function(__FetchContent_populateDirect)
+  # Policies CMP0097, CMP0135 and CMP0150 are handled in FetchContent_Declare()
+  # and the stored arguments already account for them.
+  # For CMP0097, the arguments will always assume NEW behavior by the time
+  # we get to here, so ensure ExternalProject sees that.
+  set(_EP_CMP0097 NEW)
+
+  set(args_to_parse
+    "${ARG_UNPARSED_ARGUMENTS}"
+    SOURCE_DIR "${ARG_SOURCE_DIR}"
+    BINARY_DIR "${ARG_BINARY_DIR}"
+  )
+  if(ARG_DOWNLOAD_NO_EXTRACT)
+    list(APPEND args_to_parse DOWNLOAD_NO_EXTRACT YES)
+  endif()
+
+  get_property(cmake_role GLOBAL PROPERTY CMAKE_ROLE)
+  if(cmake_role STREQUAL "PROJECT")
+    # We don't support direct population where a project makes a direct call
+    # to FetchContent_Populate(). That always goes through ExternalProject and
+    # will soon be deprecated anyway.
+    set(function_for_args FetchContent_Declare)
+  elseif(cmake_role STREQUAL "SCRIPT")
+    set(function_for_args FetchContent_Populate)
+  else()
+    message(FATAL_ERROR "Unsupported context for direct population")
+  endif()
+
+  _ep_get_add_keywords(keywords)
+  _ep_parse_arguments_to_vars(
+    ${function_for_args}
+    "${keywords}"
+    ${contentName}
+    _EP_
+    "${args_to_parse}"
+  )
+
+  # We use a simplified set of directories here. We do not need the full set
+  # of directories that ExternalProject supports, and we don't need the
+  # extensive customization options it supports either. Note that
+  # _EP_SOURCE_DIR and _EP_BINARY_DIR are always included in the saved args,
+  # so we must not set them here.
+  set(_EP_STAMP_DIR "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-stamp")
+  set(_EP_TMP_DIR   "${FETCHCONTENT_BASE_DIR}/${contentNameLower}-tmp")
+  set(_EP_DOWNLOAD_DIR "${_EP_TMP_DIR}")
+
+  file(MAKE_DIRECTORY
+    "${_EP_SOURCE_DIR}"
+    "${_EP_BINARY_DIR}"
+    "${_EP_STAMP_DIR}"
+    "${_EP_TMP_DIR}"
+  )
+
+  # We take over the stamp files and use our own for detecting whether each
+  # step is up-to-date. The method used by ExternalProject is specific to
+  # using a sub-build and is not appropriate for us here.
+
+  set(download_script ${_EP_TMP_DIR}/download.cmake)
+  set(update_script   ${_EP_TMP_DIR}/upload.cmake)
+  set(patch_script    ${_EP_TMP_DIR}/patch.cmake)
+  _ep_add_download_command(${contentName}
+    SCRIPT_FILE ${download_script}
+    DEPENDS_VARIABLE download_depends
+  )
+  _ep_add_update_command(${contentName}
+    SCRIPT_FILE ${update_script}
+    DEPENDS_VARIABLE update_depends
+  )
+  _ep_add_patch_command(${contentName}
+    SCRIPT_FILE ${patch_script}
+    # No additional dependencies for the patch step
+  )
+
+  set(download_stamp ${_EP_STAMP_DIR}/download.stamp)
+  set(update_stamp   ${_EP_STAMP_DIR}/upload.stamp)
+  set(patch_stamp    ${_EP_STAMP_DIR}/patch.stamp)
+  __FetchContent_doStepDirect(
+    SCRIPT_FILE ${download_script}
+    STAMP_FILE  ${download_stamp}
+    DEPENDS     ${download_depends}
+  )
+  __FetchContent_doStepDirect(
+    SCRIPT_FILE ${update_script}
+    STAMP_FILE  ${update_stamp}
+    DEPENDS     ${update_depends} ${download_stamp}
+  )
+  __FetchContent_doStepDirect(
+    SCRIPT_FILE ${patch_script}
+    STAMP_FILE  ${patch_stamp}
+    DEPENDS     ${update_stamp}
+  )
+
+endfunction()
+
+
+function(__FetchContent_doStepDirect)
+  set(noValueOptions )
+  set(singleValueOptions
+    SCRIPT_FILE
+    STAMP_FILE
+  )
+  set(multiValueOptions
+    DEPENDS
+  )
+  cmake_parse_arguments(PARSE_ARGV 0 arg
+    "${noValueOptions}" "${singleValueOptions}" "${multiValueOptions}"
+  )
+
+  if(NOT EXISTS ${arg_STAMP_FILE})
+    set(do_step YES)
+  else()
+    set(do_step NO)
+    foreach(dep_file IN LISTS arg_DEPENDS arg_SCRIPT_FILE)
+      if(NOT EXISTS "${arg_STAMP_FILE}" OR
+        NOT EXISTS "${dep_file}" OR
+        NOT "${arg_STAMP_FILE}" IS_NEWER_THAN "${dep_file}")
+        set(do_step YES)
+        break()
+      endif()
+    endforeach()
+  endif()
+
+  if(do_step)
+    include(${arg_SCRIPT_FILE})
+    file(TOUCH "${arg_STAMP_FILE}")
+  endif()
+endfunction()
+
+
+function(__FetchContent_populateSubbuild)
+  # All argument parsing is done in __FetchContent_doPopulate(), since it is
+  # common to both the subbuild and direct population strategies.
+  # Parsed arguments are in ARG_... variables.
+
   # The unparsed arguments may contain spaces, so build up ARG_EXTRA
   # in such a way that it correctly substitutes into the generated
   # CMakeLists.txt file with each argument quoted.
@@ -1736,7 +1931,7 @@
   if(ARGN)
     # This is the direct population form with details fully specified
     # as part of the call, so we already have everything we need
-    __FetchContent_directPopulate(
+    __FetchContent_doPopulation(
       ${contentNameLower}
       SUBBUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-subbuild"
       SOURCE_DIR   "${CMAKE_CURRENT_BINARY_DIR}/${contentNameLower}-src"
@@ -1853,7 +2048,7 @@
       endif()
     endforeach()
     cmake_language(EVAL CODE "
-      __FetchContent_directPopulate(
+      __FetchContent_doPopulation(
         ${contentNameLower}
         ${quietFlag}
         UPDATE_DISCONNECTED ${disconnectUpdates}
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake
index 19b6c2a..b963ef5 100644
--- a/Modules/FindPython.cmake
+++ b/Modules/FindPython.cmake
@@ -77,9 +77,25 @@
   :prop_gbl:`CMAKE_ROLE` is ``PROJECT``.
 
 ``Python::Interpreter``
-  Python interpreter. Target defined if component ``Interpreter`` is found.
+  Python interpreter. This target is defined only if the ``Interpreter``
+  component is found.
+``Python::InterpreterDebug``
+  .. versionadded:: 3.30
+
+  Python debug interpreter. This target is defined only if the ``Interpreter``
+  component is found and the ``Python_EXECUTABLE_DEBUG`` variable is defined.
+  The target is only defined on the ``Windows`` platform.
+
+``Python::InterpreterMultiConfig``
+  .. versionadded:: 3.30
+
+  Python interpreter. The release or debug version of the interpreter will be
+  used, based on the context (platform, configuration).
+  This target is defined only if the ``Interpreter`` component is found
+
 ``Python::Compiler``
-  Python compiler. Target defined if component ``Compiler`` is found.
+  Python compiler. This target is defined only if the ``Compiler`` component is
+  found.
 
 ``Python::Module``
   .. versionadded:: 3.15
@@ -114,6 +130,20 @@
   System has the Python interpreter.
 ``Python_EXECUTABLE``
   Path to the Python interpreter.
+``Python_EXECUTABLE_DEBUG``
+  .. versionadded:: 3.30
+
+  Path to the debug Python interpreter. It is only defined on the ``Windows``
+  platform.
+
+``Python_INTERPRETER``
+  .. versionadded:: 3.30
+
+  Path to the Python interpreter, defined as a
+  :manual:`generator expression <cmake-generator-expressions(7)>` selecting
+  the ``Python_EXECUTABLE`` or ``Python_EXECUTABLE_DEBUG`` variable based on
+  the context (platform, configuration).
+
 ``Python_INTERPRETER_ID``
   A short string unique to the interpreter. Possible values include:
     * Python
@@ -194,6 +224,12 @@
 
   The Python include directories.
 
+``Python_DEBUG_POSTFIX``
+  .. versionadded.. 3.30
+
+  Postfix of debug python module. This variable can be used to define the
+  :prop_tgt:`DEBUG_POSTFIX` target property.
+
 ``Python_LINK_OPTIONS``
   .. versionadded:: 3.19
 
@@ -537,6 +573,10 @@
 
   When option ``WITH_SOABI`` is also specified,  the module suffix will include
   the ``Python_SOSABI`` value, if any.
+
+.. versionadded:: 3.30
+  For ``MODULE`` type, the :prop_tgt:`DEBUG_POSTFIX` target property is
+  initialized with the value of ``Python_DEBUG_POSTFIX`` variable if defined.
 #]=======================================================================]
 
 
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 410b736..d05d27b 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -360,7 +360,11 @@
   foreach (implementation IN LISTS _PGN_IMPLEMENTATIONS)
     if (implementation STREQUAL "CPython")
       if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "FIRST")
-        list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python)
+        if (_PGN_DEBUG)
+          list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}_d python_d)
+        else()
+          list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python)
+        endif()
       endif()
       foreach (version IN LISTS _PGN_VERSION)
         if (_PGN_WIN32)
@@ -412,7 +416,11 @@
         endif()
       endforeach()
       if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "LAST")
-        list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python)
+        if (_PGN_DEBUG)
+          list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}_d python_d)
+        else()
+          list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python)
+        endif()
       endif()
     elseif (implementation STREQUAL "IronPython")
       if (_PGN_INTERPRETER)
@@ -458,7 +466,7 @@
 function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
   unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE)
 
-  if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI)$")
+  if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI|POSTFIX)$")
     return()
   endif()
 
@@ -494,7 +502,7 @@
         if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$")
           set(_values "")
         else()
-          string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}")
+          string (REGEX REPLACE "^([.-]|_d\\.)(.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\2" _values "${_values}")
         endif()
       endif()
     endif()
@@ -545,7 +553,7 @@
           if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$")
             set(_values "")
           else()
-            string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}")
+            string (REGEX REPLACE "^([.-]|_d\\.)(.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\2" _values "${_values}")
           endif()
         endif()
       endif()
@@ -572,7 +580,7 @@
             if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$")
               set(_values "")
             else()
-              string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}")
+              string (REGEX REPLACE "^([.-]|_d\\.)(.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}")
             endif()
           endif()
         endif()
@@ -588,6 +596,10 @@
       else()
         string (REGEX REPLACE "^\\.(.+)\\.[^.]+$" "\\1" _values "${_values}")
       endif()
+    elseif (NAME STREQUAL "POSTFIX")
+      if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "_d${CMAKE_IMPORT_LIBRARY_SUFFIX}$")
+        set (_values "_d")
+      endif()
     else()
       set (config_flag "${NAME}")
       if (NAME STREQUAL "CONFIGDIR")
@@ -605,7 +617,7 @@
     endif()
   endif()
 
-  if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI")
+  if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI" OR NAME STREQUAL "POSTFIX")
     set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE)
     return()
   endif()
@@ -817,6 +829,7 @@
 
 function (_PYTHON_VALIDATE_INTERPRETER)
   if (NOT _${_PYTHON_PREFIX}_EXECUTABLE)
+    unset (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG CACHE)
     return()
   endif()
 
@@ -826,6 +839,9 @@
     # interpreter does not exist anymore
     set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
     set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
+    if (WIN32)
+      set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND")
+    endif()
     return()
   endif()
 
@@ -863,6 +879,9 @@
       # interpreter is not usable
       set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
       set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND")
+      if (WIN32)
+        set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE_DEBUG-NOTFOUND")
+      endif()
       return()
     endif()
 
@@ -1106,7 +1125,7 @@
 
 function (_PYTHON_VALIDATE_LIBRARY)
   if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE)
-    unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG)
+    unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE)
     return()
   endif()
 
@@ -1173,7 +1192,7 @@
 
 function (_PYTHON_VALIDATE_SABI_LIBRARY)
   if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
-    unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG)
+    unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE)
     return()
   endif()
 
@@ -1452,6 +1471,9 @@
 unset (${_PYTHON_PREFIX}_SOABI)
 unset (${_PYTHON_PREFIX}_SOSABI)
 
+# Windows CPython implementation may be requiring a postfix in debug mode
+unset (${_PYTHON_PREFIX}_DEBUG_POSTFIX)
+
 # Define lookup strategy
 cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY)
 if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW")
@@ -1788,6 +1810,7 @@
 # first step, search for the interpreter
 if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
   list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_EXECUTABLE
+                                              _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG
                                               _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES)
   if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter)
     list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE)
@@ -1800,6 +1823,7 @@
       unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE)
     endif()
     set (_${_PYTHON_PREFIX}_EXECUTABLE "${${_PYTHON_PREFIX}_EXECUTABLE}" CACHE INTERNAL "")
+    unset (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG CACHE)
   elseif (DEFINED _${_PYTHON_PREFIX}_EXECUTABLE)
     # compute interpreter signature and check validity of definition
     string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}")
@@ -1816,6 +1840,7 @@
       endif()
     else()
       unset (_${_PYTHON_PREFIX}_EXECUTABLE CACHE)
+      unset (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG CACHE)
     endif()
     if (NOT _${_PYTHON_PREFIX}_EXECUTABLE)
       unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE)
@@ -2263,7 +2288,30 @@
     set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}" CACHE FILEPATH "${_PYTHON_PREFIX} Interpreter")
   endif()
 
+  if (WIN32 AND _${_PYTHON_PREFIX}_EXECUTABLE AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS)
+    # search for debug interpreter
+    # use release interpreter location as a hint
+    _python_get_names (_${_PYTHON_PREFIX}_INTERPRETER_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} IMPLEMENTATIONS CPython INTERPRETER WIN32 DEBUG)
+    get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY)
+    set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR)
+
+    find_program (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG
+                  NAMES ${_${_PYTHON_PREFIX}_INTERPRETER_NAMES_DEBUG}
+                  NAMES_PER_DIR
+                  HINTS "${_${_PYTHON_PREFIX}_PATH}" ${${_PYTHON_PREFIX}_HINTS}
+                  NO_DEFAULT_PATH)
+    # second try including CMAKE variables to catch-up non conventional layouts
+    find_program (_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG
+                  NAMES ${_${_PYTHON_PREFIX}_INTERPRETER_NAMES_DEBUG}
+                  NAMES_PER_DIR
+                  NO_SYSTEM_ENVIRONMENT_PATH
+                  NO_CMAKE_SYSTEM_PATH)
+  endif()
+  set (${_PYTHON_PREFIX}_EXECUTABLE_DEBUG "${_${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}")
+  set (${_PYTHON_PREFIX}_INTERPRETER "$<IF:$<AND:$<CONFIG:Debug>,$<BOOL:${WIN32}>,$<BOOL:${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}>>,${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG},${${_PYTHON_PREFIX}_EXECUTABLE}>")
+
   _python_mark_as_internal (_${_PYTHON_PREFIX}_EXECUTABLE
+                            _${_PYTHON_PREFIX}_EXECUTABLE_DEBUG
                             _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES
                             _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE)
 endif()
@@ -3730,6 +3778,10 @@
     _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI)
   endif()
 
+  if (WIN32 AND NOT DEFINED ${_PYTHON_PREFIX}_DEBUG_POSTFIX)
+    _python_get_config_var (${_PYTHON_PREFIX}_DEBUG_POSTFIX POSTFIX)
+  endif()
+
   _python_compute_development_signature (Module)
   _python_compute_development_signature (SABIModule)
   _python_compute_development_signature (Embed)
@@ -3892,11 +3944,27 @@
 # Create imported targets and helper functions
 if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT")
   if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
-      AND ${_PYTHON_PREFIX}_Interpreter_FOUND
-      AND NOT TARGET ${_PYTHON_PREFIX}::Interpreter)
-    add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED)
-    set_property (TARGET ${_PYTHON_PREFIX}::Interpreter
-                  PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}")
+      AND ${_PYTHON_PREFIX}_Interpreter_FOUND)
+    if(NOT TARGET ${_PYTHON_PREFIX}::Interpreter)
+      add_executable (${_PYTHON_PREFIX}::Interpreter IMPORTED)
+      set_property (TARGET ${_PYTHON_PREFIX}::Interpreter
+                    PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}")
+    endif()
+    if(${_PYTHON_PREFIX}_EXECUTABLE_DEBUG AND NOT TARGET ${_PYTHON_PREFIX}::InterpreterDebug)
+      add_executable (${_PYTHON_PREFIX}::InterpreterDebug IMPORTED)
+      set_property (TARGET ${_PYTHON_PREFIX}::InterpreterDebug
+                    PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}")
+    endif()
+    if(NOT TARGET ${_PYTHON_PREFIX}::InterpreterMultiConfig)
+      add_executable (${_PYTHON_PREFIX}::InterpreterMultiConfig IMPORTED)
+      set_property (TARGET ${_PYTHON_PREFIX}::InterpreterMultiConfig
+                    PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_EXECUTABLE}")
+      if(${_PYTHON_PREFIX}_EXECUTABLE_DEBUG)
+        set_target_properties (${_PYTHON_PREFIX}::InterpreterMultiConfig
+                               PROPERTIES IMPORTED_CONFIGURATIONS DEBUG
+                                          IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_EXECUTABLE_DEBUG}")
+      endif()
+    endif()
   endif()
 
   if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
@@ -4108,6 +4176,9 @@
         set_property (TARGET ${name} PROPERTY PREFIX "")
         if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
           set_property (TARGET ${name} PROPERTY SUFFIX ".pyd")
+          if (${prefix}_DEBUG_POSTFIX)
+            set_property (TARGET ${name} PROPERTY DEBUG_POSTFIX "${${prefix}_DEBUG_POSTFIX}")
+          endif()
         endif()
 
         if (PYTHON_ADD_LIBRARY_WITH_SOABI)
diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake
index 4c7ab5c..eb2b5a8 100644
--- a/Modules/FindPython2.cmake
+++ b/Modules/FindPython2.cmake
@@ -71,9 +71,25 @@
   :prop_gbl:`CMAKE_ROLE` is ``PROJECT``.
 
 ``Python2::Interpreter``
-  Python 2 interpreter. Target defined if component ``Interpreter`` is found.
+  Python 2 interpreter. This target is defined only if the ``Interpreter``
+  component is found.
+``Python2::InterpreterDebug``
+  .. versionadded:: 3.30
+
+  Python 2 debug interpreter. This target is defined only if the
+  ``Interpreter`` component is found and the ``Python2_EXECUTABLE_DEBUG``
+  variable is defined. The target is only defined on the ``Windows`` platform.
+
+``Python2::InterpreterMultiConfig``
+  .. versionadded:: 3.30
+
+  Python 2 interpreter. The release or debug version of the interpreter will be
+  used, based on the context (platform, configuration).
+  This target is defined only if the ``Interpreter`` component is found
+
 ``Python2::Compiler``
-  Python 2 compiler. Target defined if component ``Compiler`` is found.
+  Python 2 compiler. This target is defined only if the ``Compiler`` component
+  is found.
 ``Python2::Module``
   .. versionadded:: 3.15
 
@@ -101,6 +117,20 @@
   System has the Python 2 interpreter.
 ``Python2_EXECUTABLE``
   Path to the Python 2 interpreter.
+``Python2_EXECUTABLE_DEBUG``
+  .. versionadded:: 3.30
+
+  Path to the debug Python 2 interpreter. It is only defined on the ``Windows``
+  platform.
+
+``Python2_INTERPRETER``
+  .. versionadded:: 3.30
+
+  Path to the Python 2 interpreter, defined as a
+  :manual:`generator expression <cmake-generator-expressions(7)>` selecting
+  the ``Python2_EXECUTABLE`` or ``Python2_EXECUTABLE_DEBUG`` variable based on
+  the context (platform, configuration).
+
 ``Python2_INTERPRETER_ID``
   A short string unique to the interpreter. Possible values include:
     * Python
@@ -158,6 +188,12 @@
 ``Python2_INCLUDE_DIRS``
   The Python 2 include directories.
 
+``Python2_DEBUG_POSTFIX``
+  .. versionadded.. 3.30
+
+  Postfix of debug python module. This variable can be used to define the
+  :prop_tgt:`DEBUG_POSTFIX` target property.
+
 ``Python2_LINK_OPTIONS``
   .. versionadded:: 3.19
 
@@ -417,6 +453,10 @@
                        <source1> [<source2> ...])
 
 If library type is not specified, ``MODULE`` is assumed.
+
+.. versionadded:: 3.30
+  For ``MODULE`` type, the :prop_tgt:`DEBUG_POSTFIX` target property is
+  initialized with the value of ``Python2_DEBUG_POSTFIX`` variable if defined.
 #]=======================================================================]
 
 
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake
index 901565b..b2a5b09 100644
--- a/Modules/FindPython3.cmake
+++ b/Modules/FindPython3.cmake
@@ -78,9 +78,25 @@
   :prop_gbl:`CMAKE_ROLE` is ``PROJECT``.
 
 ``Python3::Interpreter``
-  Python 3 interpreter. Target defined if component ``Interpreter`` is found.
+  Python 3 interpreter. This target is defined only if the ``Interpreter``
+  component is found.
+``Python3::InterpreterDebug``
+  .. versionadded:: 3.30
+
+  Python 3 debug interpreter. This target is defined only if the
+  ``Interpreter`` component is found and the ``Python3_EXECUTABLE_DEBUG``
+  variable is defined. The target is only defined on the ``Windows`` platform.
+
+``Python3::InterpreterMultiConfig``
+  .. versionadded:: 3.30
+
+  Python 3 interpreter. The release or debug version of the interpreter will be
+  used, based on the context (platform, configuration).
+  This target is defined only if the ``Interpreter`` component is found
+
 ``Python3::Compiler``
-  Python 3 compiler. Target defined if component ``Compiler`` is found.
+  Python 3 compiler. This target is defined only if the ``Compiler`` component
+  is found.
 
 ``Python3::Module``
   .. versionadded:: 3.15
@@ -115,6 +131,20 @@
   System has the Python 3 interpreter.
 ``Python3_EXECUTABLE``
   Path to the Python 3 interpreter.
+``Python3_EXECUTABLE_DEBUG``
+  .. versionadded:: 3.30
+
+  Path to the debug Python 3 interpreter. It is only defined on ``Windows``
+  platform.
+
+``Python3_INTERPRETER``
+  .. versionadded:: 3.30
+
+  Path to the Python 3 interpreter, defined as a
+  :manual:`generator expression <cmake-generator-expressions(7)>` selecting
+  the ``Python3_EXECUTABLE`` or ``Python3_EXECUTABLE_DEBUG`` variable based on
+  the context (platform, configuration).
+
 ``Python3_INTERPRETER_ID``
   A short string unique to the interpreter. Possible values include:
     * Python
@@ -196,6 +226,12 @@
 
   The Python 3 include directories.
 
+``Python3_DEBUG_POSTFIX``
+  .. versionadded.. 3.30
+
+  Postfix of debug python module. This variable can be used to define the
+  :prop_tgt:`DEBUG_POSTFIX` target property.
+
 ``Python3_LINK_OPTIONS``
   .. versionadded:: 3.19
 
@@ -535,6 +571,10 @@
 
   When option ``WITH_SOABI`` is also specified,  the module suffix will include
   the ``Python3_SOSABI`` value, if any.
+
+.. versionadded:: 3.30
+  For ``MODULE`` type, the :prop_tgt:`DEBUG_POSTFIX` target property is
+  initialized with the value of ``Python3_DEBUG_POSTFIX`` variable if defined.
 #]=======================================================================]
 
 
diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake
index bd5ba9a..31f4293 100644
--- a/Modules/Platform/Apple-Clang.cmake
+++ b/Modules/Platform/Apple-Clang.cmake
@@ -19,6 +19,7 @@
 
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+  set(CMAKE_${lang}_LINK_LIBRARY_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
   # linker selection
   set(CMAKE_${lang}_USING_LINKER_SYSTEM "-fuse-ld=ld")
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake
index 15f6a71..20b18ad 100644
--- a/Modules/Platform/Apple-GNU.cmake
+++ b/Modules/Platform/Apple-GNU.cmake
@@ -17,6 +17,7 @@
 
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
   set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+  set(CMAKE_LINK_LIBRARY_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
   set(CMAKE_${lang}_USING_LINKER_SYSTEM "")
   set(CMAKE_${lang}_USING_LINKER_APPLE_CLASSIC "LINKER:-ld_classic")
diff --git a/Modules/Platform/CYGWIN-GNU.cmake b/Modules/Platform/CYGWIN-GNU.cmake
index 070b24d..5e2ba41 100644
--- a/Modules/Platform/CYGWIN-GNU.cmake
+++ b/Modules/Platform/CYGWIN-GNU.cmake
@@ -40,6 +40,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 macro(__cygwin_compiler_gnu lang)
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index 533b9ce..1272baf 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -114,29 +114,37 @@
 # Defines LINK_LIBRARY features for frameworks
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK "LINKER:-framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_FRAMEWORK "LINKER:-needed_framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_NEEDED_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_FRAMEWORK "LINKER:-reexport_framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_REEXPORT_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK "LINKER:-weak_framework,<LIBRARY>")
 set(CMAKE_LINK_LIBRARY_USING_WEAK_FRAMEWORK_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WEAK_FRAMEWORK_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 # Defines LINK_LIBRARY features for libraries
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY "PATH{LINKER:-needed_library,<LIBRARY>}NAME{LINKER:-needed-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_NEEDED_LIBRARY_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_NEEDED_LIBRARY_PROPERTIES LIBRARY_TYPE=SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY "PATH{LINKER:-reexport_library,<LIBRARY>}NAME{LINKER:-reexport-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_REEXPORT_LIBRARY_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_REEXPORT_LIBRARY_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY "PATH{LINKER:-weak_library,<LIBRARY>}NAME{LINKER:-weak-l<LIBRARY>}")
 set(CMAKE_LINK_LIBRARY_USING_WEAK_LIBRARY_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WEAK_LIBRARY_PROPERTIES LIBRARY_TYPE=STATIC,SHARED UNICITY=DEFAULT OVERRIDE=DEFAULT)
 
 # Defines LINK_LIBRARY feature to Force loading of all members of an archive
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:-force_load,<LIB_ITEM>")
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 # default to searching for frameworks first
 if(NOT DEFINED CMAKE_FIND_FRAMEWORK)
diff --git a/Modules/Platform/FreeBSD.cmake b/Modules/Platform/FreeBSD.cmake
index bd5a786..7d5f951 100644
--- a/Modules/Platform/FreeBSD.cmake
+++ b/Modules/Platform/FreeBSD.cmake
@@ -51,6 +51,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/Linux.cmake b/Modules/Platform/Linux.cmake
index 97a116f..fba6ab8 100644
--- a/Modules/Platform/Linux.cmake
+++ b/Modules/Platform/Linux.cmake
@@ -44,6 +44,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 # Features for LINK_GROUP generator expression
 ## RESCAN: request the linker to rescan static libraries until there is
diff --git a/Modules/Platform/NetBSD.cmake b/Modules/Platform/NetBSD.cmake
index ab85923..af368cd 100644
--- a/Modules/Platform/NetBSD.cmake
+++ b/Modules/Platform/NetBSD.cmake
@@ -37,6 +37,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/SunOS.cmake b/Modules/Platform/SunOS.cmake
index b8a302c..73205c1 100644
--- a/Modules/Platform/SunOS.cmake
+++ b/Modules/Platform/SunOS.cmake
@@ -20,6 +20,7 @@
                                              "LINKER:-z,defaultextract")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 
 # Features for LINK_GROUP generator expression
diff --git a/Modules/Platform/Windows-Clang.cmake b/Modules/Platform/Windows-Clang.cmake
index b9e6394..35055bc 100644
--- a/Modules/Platform/Windows-Clang.cmake
+++ b/Modules/Platform/Windows-Clang.cmake
@@ -140,6 +140,7 @@
     ## WHOLE_ARCHIVE: Force loading all members of an archive
     set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>")
     set(CMAKE_${lang}_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+    set(CMAKE_${lang}_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
   endif()
 
   enable_language(RC)
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index 9f81882..d2b25c6 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -88,6 +88,7 @@
                                              "LINKER:--no-whole-archive")
 endif()
 set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 
 # Features for LINK_GROUP generator expression
 ## RESCAN: request the linker to rescan static libraries until there is
diff --git a/Modules/Platform/Windows-IntelLLVM.cmake b/Modules/Platform/Windows-IntelLLVM.cmake
index b1a336b..691c7ce 100644
--- a/Modules/Platform/Windows-IntelLLVM.cmake
+++ b/Modules/Platform/Windows-IntelLLVM.cmake
@@ -37,6 +37,7 @@
     ## WHOLE_ARCHIVE: Force loading all members of an archive
     set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "LINKER:/WHOLEARCHIVE:<LIBRARY>")
     set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+    set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
   endif()
 
   set(CMAKE_${lang}_LINK_EXECUTABLE
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index ef57031..c737b88 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -358,6 +358,7 @@
   ## WHOLE_ARCHIVE: Force loading all members of an archive
   set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE "/WHOLEARCHIVE:<LIBRARY>")
   set(CMAKE_LINK_LIBRARY_USING_WHOLE_ARCHIVE_SUPPORTED TRUE)
+  set(CMAKE_LINK_LIBRARY_WHOLE_ARCHIVE_PROPERTIES LIBRARY_TYPE=STATIC UNICITY=YES OVERRIDE=DEFAULT)
 endif()
 
 
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 8c57762..9953caf 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -886,8 +886,6 @@
         cmGlobalVisualStudio7Generator.h
         cmGlobalVisualStudio8Generator.cxx
         cmGlobalVisualStudio8Generator.h
-        cmGlobalVisualStudio9Generator.cxx
-        cmGlobalVisualStudio9Generator.h
         cmVisualStudioGeneratorOptions.h
         cmVisualStudioGeneratorOptions.cxx
         cmVsProjectType.h
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index dc9887d..b4fc494 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 29)
-set(CMake_VERSION_PATCH 20240507)
+set(CMake_VERSION_PATCH 20240521)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index ea97287..d943011 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -357,13 +357,18 @@
     cc->SetDepends(depends);
     cc->SetImplicitDepends(implicit_depends);
     mf.AddCustomCommandToOutput(std::move(cc));
-  } else if (!byproducts.empty()) {
-    status.SetError("BYPRODUCTS may not be specified with SOURCE signatures");
-    return false;
-  } else if (uses_terminal) {
-    status.SetError("USES_TERMINAL may not be used with SOURCE signatures");
-    return false;
   } else {
+    if (!byproducts.empty()) {
+      status.SetError(
+        "BYPRODUCTS may not be specified with SOURCE signatures");
+      return false;
+    }
+
+    if (uses_terminal) {
+      status.SetError("USES_TERMINAL may not be used with SOURCE signatures");
+      return false;
+    }
+
     bool issueMessage = true;
     std::ostringstream e;
     MessageType messageType = MessageType::AUTHOR_WARNING;
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 8651436..db0d71a 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -30,6 +30,7 @@
 #include "cmMessageType.h"
 #include "cmPolicies.h"
 #include "cmRange.h"
+#include "cmState.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
@@ -201,6 +202,113 @@
   return makefile->GetDefinition(featureSupported).IsOn();
 }
 
+// LINK_LIBRARY feature properties management
+struct LinkLibraryFeaturePropertySet
+{
+  std::set<cmStateEnums::TargetType> LibraryTypes = {
+    cmStateEnums::EXECUTABLE, cmStateEnums::STATIC_LIBRARY,
+    cmStateEnums::SHARED_LIBRARY, cmStateEnums::MODULE_LIBRARY,
+    cmStateEnums::UNKNOWN_LIBRARY
+  };
+  std::set<std::string> Override;
+
+  enum UnicityKind
+  {
+    Default,
+    Yes,
+    No
+  };
+  UnicityKind Unicity = Default;
+};
+std::map<std::string, LinkLibraryFeaturePropertySet>
+  LinkLibraryFeatureProperties;
+const LinkLibraryFeaturePropertySet& GetLinkLibraryFeatureProperties(
+  cmMakefile* makefile, std::string const& linkLanguage,
+  const std::string& feature)
+{
+  auto it = LinkLibraryFeatureProperties.find(feature);
+  if (it != LinkLibraryFeatureProperties.end()) {
+    return it->second;
+  }
+
+  auto featurePropertiesVariable =
+    cmStrCat("CMAKE_", linkLanguage, "_LINK_LIBRARY_", feature, "_PROPERTIES");
+  auto featurePropertiesValues =
+    makefile->GetDefinition(featurePropertiesVariable);
+  if (featurePropertiesValues.IsEmpty()) {
+    // try language agnostic definition
+    featurePropertiesVariable =
+      cmStrCat("CMAKE_LINK_LIBRARY_", feature, "_PROPERTIES");
+    featurePropertiesValues =
+      makefile->GetDefinition(featurePropertiesVariable);
+  }
+  if (!featurePropertiesValues.IsEmpty()) {
+    LinkLibraryFeaturePropertySet featureProperties;
+    cmsys::RegularExpression processingOption{
+      "^(LIBRARY_TYPE|UNICITY|OVERRIDE)=((STATIC|SHARED|MODULE|EXECUTABLE)(,("
+      "STATIC|"
+      "SHARED|MODULE|EXECUTABLE)"
+      ")*|YES|NO|DEFAULT|[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*)$"
+    };
+    std::string errorMessage;
+    for (auto const& option : cmList{ featurePropertiesValues }) {
+      if (processingOption.find(option)) {
+        if (processingOption.match(1) == "LIBRARY_TYPE") {
+          featureProperties.LibraryTypes.clear();
+          for (auto const& value :
+               cmTokenize(processingOption.match(2), ","_s)) {
+            if (value == "STATIC") {
+              featureProperties.LibraryTypes.emplace(
+                cmStateEnums::STATIC_LIBRARY);
+            } else if (value == "SHARED") {
+              featureProperties.LibraryTypes.emplace(
+                cmStateEnums::SHARED_LIBRARY);
+            } else if (value == "MODULE") {
+              featureProperties.LibraryTypes.emplace(
+                cmStateEnums::MODULE_LIBRARY);
+            } else if (value == "EXECUTABLE") {
+              featureProperties.LibraryTypes.emplace(cmStateEnums::EXECUTABLE);
+            } else {
+              errorMessage += cmStrCat("  ", option, '\n');
+              break;
+            }
+          }
+          // Always add UNKNOWN type
+          featureProperties.LibraryTypes.emplace(
+            cmStateEnums::UNKNOWN_LIBRARY);
+        } else if (processingOption.match(1) == "UNICITY") {
+          if (processingOption.match(2) == "YES") {
+            featureProperties.Unicity = LinkLibraryFeaturePropertySet::Yes;
+          } else if (processingOption.match(2) == "NO") {
+            featureProperties.Unicity = LinkLibraryFeaturePropertySet::No;
+          } else if (processingOption.match(2) == "DEFAULT") {
+            featureProperties.Unicity = LinkLibraryFeaturePropertySet::Default;
+          } else {
+            errorMessage += cmStrCat("  ", option, '\n');
+          }
+        } else if (processingOption.match(1) == "OVERRIDE") {
+          featureProperties.Override.clear();
+          auto values = cmTokenize(processingOption.match(2), ","_s);
+          featureProperties.Override.insert(values.begin(), values.end());
+        }
+      } else {
+        errorMessage += cmStrCat("  ", option, '\n');
+      }
+    }
+    if (!errorMessage.empty()) {
+      makefile->GetCMakeInstance()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Erroneous option(s) for '", featurePropertiesVariable,
+                 "':\n", errorMessage));
+    }
+    return LinkLibraryFeatureProperties.emplace(feature, featureProperties)
+      .first->second;
+  }
+  return LinkLibraryFeatureProperties
+    .emplace(feature, LinkLibraryFeaturePropertySet{})
+    .first->second;
+}
+
 // LINK_GROUP helpers
 const auto LG_BEGIN = "<LINK_GROUP:"_s;
 const auto LG_END = "</LINK_GROUP:"_s;
@@ -235,7 +343,9 @@
   EntriesProcessing(const cmGeneratorTarget* target,
                     const std::string& linkLanguage, EntryVector& entries,
                     EntryVector& finalEntries)
-    : Entries(entries)
+    : Target(target)
+    , LinkLanguage(linkLanguage)
+    , Entries(entries)
     , FinalEntries(finalEntries)
   {
     const auto* makefile = target->Makefile;
@@ -398,6 +508,18 @@
 
   bool IncludeEntry(LinkEntry const& entry) const
   {
+    if (entry.Feature != cmComputeLinkDepends::LinkEntry::DEFAULT) {
+      auto const& featureProperties = GetLinkLibraryFeatureProperties(
+        this->Target->Makefile, this->LinkLanguage, entry.Feature);
+      if ((entry.Target == nullptr ||
+           featureProperties.LibraryTypes.find(entry.Target->GetType()) !=
+             featureProperties.LibraryTypes.end()) &&
+          featureProperties.Unicity !=
+            LinkLibraryFeaturePropertySet::Default) {
+        return featureProperties.Unicity == LinkLibraryFeaturePropertySet::No;
+      }
+    }
+
     return this->Unicity == None ||
       (this->Unicity == Shared &&
        (entry.Target == nullptr ||
@@ -418,6 +540,8 @@
 
   OrderKind Order = Reverse;
   UnicityKind Unicity = Shared;
+  const cmGeneratorTarget* Target;
+  const std::string& LinkLanguage;
   EntryVector& Entries;
   EntryVector& FinalEntries;
   std::set<size_t> Emitted;
@@ -973,12 +1097,14 @@
     auto ale = this->AddLinkEntry(item, groupIndex.first);
     dependee_index = ale.first;
     LinkEntry& entry = this->EntryList[dependee_index];
+    bool supportedItem = true;
     auto const& itemFeature =
       this->GetCurrentFeature(entry.Item.Value, item.Feature);
     if (inGroup && ale.second && entry.Target != nullptr &&
         (entry.Target->GetType() == cmStateEnums::TargetType::OBJECT_LIBRARY ||
          entry.Target->GetType() ==
            cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
+      supportedItem = false;
       const auto& groupFeature = this->EntryList[groupIndex.first].Feature;
       this->CMakeInstance->IssueMessage(
         MessageType::AUTHOR_WARNING,
@@ -995,30 +1121,27 @@
     }
     if (ale.second) {
       // current item not yet defined
-      if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr &&
-          (entry.Target->GetType() ==
-             cmStateEnums::TargetType::OBJECT_LIBRARY ||
-           entry.Target->GetType() ==
-             cmStateEnums::TargetType::INTERFACE_LIBRARY)) {
-        this->CMakeInstance->IssueMessage(
-          MessageType::AUTHOR_WARNING,
-          cmStrCat("The feature '", itemFeature,
-                   "', specified as part of a generator-expression "
-                   "'$<LINK_LIBRARY:",
-                   itemFeature, ">', will not be applied to the ",
-                   (entry.Target->GetType() ==
-                        cmStateEnums::TargetType::OBJECT_LIBRARY
-                      ? "OBJECT"
-                      : "INTERFACE"),
-                   " library '", entry.Item.Value, "'."),
-          this->Target->GetBacktrace());
-      }
       entry.Feature = itemFeature;
-    }
 
-    bool supportedItem = entry.Target == nullptr ||
-      (entry.Target->GetType() != cmStateEnums::TargetType::OBJECT_LIBRARY &&
-       entry.Target->GetType() != cmStateEnums::TargetType::INTERFACE_LIBRARY);
+      if (itemFeature != LinkEntry::DEFAULT && entry.Target != nullptr) {
+        auto const& featureProperties = GetLinkLibraryFeatureProperties(
+          this->Makefile, this->LinkLanguage, itemFeature);
+        if (featureProperties.LibraryTypes.find(entry.Target->GetType()) ==
+            featureProperties.LibraryTypes.end()) {
+          supportedItem = false;
+          entry.Feature = LinkEntry::DEFAULT;
+          this->CMakeInstance->IssueMessage(
+            MessageType::AUTHOR_WARNING,
+            cmStrCat("The feature '", itemFeature,
+                     "', specified as part of a generator-expression "
+                     "'$<LINK_LIBRARY:",
+                     itemFeature, ">', will not be applied to the ",
+                     cmState::GetTargetTypeName(entry.Target->GetType()), " '",
+                     entry.Item.Value, "'."),
+            this->Target->GetBacktrace());
+        }
+      }
+    }
 
     if (supportedItem) {
       if (inGroup) {
@@ -1043,21 +1166,41 @@
         }
       }
       if (entry.Feature != itemFeature) {
-        // incompatibles features occurred
-        this->CMakeInstance->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat("Impossible to link target '", this->Target->GetName(),
-                   "' because the link item '", entry.Item.Value,
-                   "', specified ",
-                   (itemFeature == LinkEntry::DEFAULT
-                      ? "without any feature or 'DEFAULT' feature"
-                      : cmStrCat("with the feature '", itemFeature, '\'')),
-                   ", has already occurred ",
-                   (entry.Feature == LinkEntry::DEFAULT
-                      ? "without any feature or 'DEFAULT' feature"
-                      : cmStrCat("with the feature '", entry.Feature, '\'')),
-                   ", which is not allowed."),
-          this->Target->GetBacktrace());
+        bool incompatibleFeatures = true;
+        // check if an override is possible
+        auto const& entryFeatureProperties = GetLinkLibraryFeatureProperties(
+          this->Makefile, this->LinkLanguage, entry.Feature);
+        auto const& itemFeatureProperties = GetLinkLibraryFeatureProperties(
+          this->Makefile, this->LinkLanguage, itemFeature);
+        if (entryFeatureProperties.Override.empty() &&
+            !itemFeatureProperties.Override.empty() &&
+            itemFeatureProperties.Override.find(entry.Feature) !=
+              itemFeatureProperties.Override.end()) {
+          entry.Feature = itemFeature;
+          incompatibleFeatures = false;
+        } else if (!entryFeatureProperties.Override.empty() &&
+                   itemFeatureProperties.Override.empty() &&
+                   entryFeatureProperties.Override.find(itemFeature) !=
+                     entryFeatureProperties.Override.end()) {
+          incompatibleFeatures = false;
+        }
+        if (incompatibleFeatures) {
+          // incompatibles features occurred
+          this->CMakeInstance->IssueMessage(
+            MessageType::FATAL_ERROR,
+            cmStrCat("Impossible to link target '", this->Target->GetName(),
+                     "' because the link item '", entry.Item.Value,
+                     "', specified ",
+                     (itemFeature == LinkEntry::DEFAULT
+                        ? "without any feature or 'DEFAULT' feature"
+                        : cmStrCat("with the feature '", itemFeature, '\'')),
+                     ", has already occurred ",
+                     (entry.Feature == LinkEntry::DEFAULT
+                        ? "without any feature or 'DEFAULT' feature"
+                        : cmStrCat("with the feature '", entry.Feature, '\'')),
+                     ", which is not allowed."),
+            this->Target->GetBacktrace());
+        }
       }
     }
 
diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx
index b6b9540..4b2aec7 100644
--- a/Source/cmCxxModuleMapper.cxx
+++ b/Source/cmCxxModuleMapper.cxx
@@ -434,3 +434,17 @@
   assert(false);
   return {};
 }
+
+CxxModuleMapMode CxxModuleMapOpenMode(CxxModuleMapFormat format)
+{
+  switch (format) {
+    case CxxModuleMapFormat::Gcc:
+      return CxxModuleMapMode::Binary;
+    case CxxModuleMapFormat::Clang:
+    case CxxModuleMapFormat::Msvc:
+      return CxxModuleMapMode::Default;
+  }
+
+  assert(false);
+  return CxxModuleMapMode::Default;
+}
diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h
index c785099..898396c 100644
--- a/Source/cmCxxModuleMapper.h
+++ b/Source/cmCxxModuleMapper.h
@@ -83,6 +83,14 @@
                     LookupMethod method);
 };
 
+enum class CxxModuleMapMode
+{
+  Text,
+  Binary,
+
+  Default = Text,
+};
+
 // Return the extension to use for a given modulemap format.
 cm::static_string_view CxxModuleMapExtension(
   cm::optional<CxxModuleMapFormat> format);
@@ -101,3 +109,6 @@
                                 CxxModuleLocations const& loc,
                                 cmScanDepInfo const& obj,
                                 CxxModuleUsage const& usages);
+
+// Return the open mode required for the modmap file format.
+CxxModuleMapMode CxxModuleMapOpenMode(CxxModuleMapFormat format);
diff --git a/Source/cmGlobalBorlandMakefileGenerator.cxx b/Source/cmGlobalBorlandMakefileGenerator.cxx
index 2fd7f8a..6eabe9c 100644
--- a/Source/cmGlobalBorlandMakefileGenerator.cxx
+++ b/Source/cmGlobalBorlandMakefileGenerator.cxx
@@ -24,6 +24,7 @@
   this->ToolSupportsColor = true;
   this->UseLinkScript = false;
   cm->GetState()->SetWindowsShell(true);
+  cm->GetState()->SetBorlandMake(true);
   this->IncludeDirective = "!include";
   this->DefineWindowsNULL = true;
   this->PassMakeflags = true;
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index e397fa2..0ee2dac 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -4,13 +4,11 @@
 
 #include <algorithm>
 #include <cassert>
-#include <chrono>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <functional>
 #include <initializer_list>
-#include <iomanip>
 #include <iterator>
 #include <sstream>
 #include <type_traits>
@@ -1351,8 +1349,6 @@
 
 void cmGlobalGenerator::Configure()
 {
-  auto startTime = std::chrono::steady_clock::now();
-
   this->FirstTimeProgress = 0.0f;
   this->ClearGeneratorMembers();
   this->NextDeferId = 0;
@@ -1407,21 +1403,6 @@
   this->GetCMakeInstance()->AddCacheEntry(
     "CMAKE_NUMBER_OF_MAKEFILES", std::to_string(this->Makefiles.size()),
     "number of local generators", cmStateEnums::INTERNAL);
-
-  auto endTime = std::chrono::steady_clock::now();
-
-  if (this->CMakeInstance->GetWorkingMode() == cmake::NORMAL_MODE) {
-    std::ostringstream msg;
-    if (cmSystemTools::GetErrorOccurredFlag()) {
-      msg << "Configuring incomplete, errors occurred!";
-    } else {
-      auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
-        endTime - startTime);
-      msg << "Configuring done (" << std::fixed << std::setprecision(1)
-          << ms.count() / 1000.0L << "s)";
-    }
-    this->CMakeInstance->UpdateProgress(msg.str(), -1);
-  }
 }
 
 void cmGlobalGenerator::CreateGenerationObjects(TargetTypes targetTypes)
@@ -1683,8 +1664,6 @@
 
 void cmGlobalGenerator::Generate()
 {
-  auto startTime = std::chrono::steady_clock::now();
-
   // Create a map from local generator to the complete set of targets
   // it builds by default.
   this->InitializeProgressMarks();
@@ -1775,14 +1754,6 @@
     this->GetCMakeInstance()->IssueMessage(MessageType::AUTHOR_WARNING,
                                            w.str());
   }
-
-  auto endTime = std::chrono::steady_clock::now();
-  auto ms =
-    std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime);
-  std::ostringstream msg;
-  msg << "Generating done (" << std::fixed << std::setprecision(1)
-      << ms.count() / 1000.0L << "s)";
-  this->CMakeInstance->UpdateProgress(msg.str(), -1);
 }
 
 bool cmGlobalGenerator::ComputeTargetDepends()
@@ -2759,6 +2730,7 @@
 }
 
 static bool RaiseCMP0037Message(cmake* cm, cmTarget* tgt,
+                                std::string const& targetNameAsWritten,
                                 std::string const& reason)
 {
   MessageType messageType = MessageType::AUTHOR_WARNING;
@@ -2779,8 +2751,8 @@
       break;
   }
   if (issueMessage) {
-    e << "The target name \"" << tgt->GetName() << "\" is reserved " << reason
-      << ".";
+    e << "The target name \"" << targetNameAsWritten << "\" is reserved "
+      << reason << ".";
     if (messageType == MessageType::AUTHOR_WARNING) {
       e << "  It may result in undefined behavior.";
     }
@@ -2799,7 +2771,8 @@
   if (!tgt) {
     return true;
   }
-  return RaiseCMP0037Message(this->GetCMakeInstance(), tgt, reason);
+  return RaiseCMP0037Message(this->GetCMakeInstance(), tgt, targetName,
+                             reason);
 }
 
 void cmGlobalGenerator::CreateDefaultGlobalTargets(
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 612af4f..250546f 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -2806,7 +2806,10 @@
         // `cmNinjaTargetGenerator::WriteObjectBuildStatements` and
         // `cmNinjaTargetGenerator::ExportObjectCompileCommand` to generate the
         // corresponding file path.
-        cmGeneratedFileStream mmf(cmStrCat(object.PrimaryOutput, ".modmap"));
+        cmGeneratedFileStream mmf;
+        mmf.Open(cmStrCat(object.PrimaryOutput, ".modmap"), false,
+                 CxxModuleMapOpenMode(*modmap_fmt) ==
+                   CxxModuleMapMode::Binary);
         mmf.SetCopyIfDifferent(true);
         mmf << mm;
       }
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 91fbccc..7abe75a 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -518,7 +518,7 @@
 
   this->DefaultPlatformToolset = this->SelectWindowsCEToolset();
 
-  if (this->GetVersion() == cmGlobalVisualStudioGenerator::VSVersion::VS12) {
+  if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS12) {
     // VS 12 .NET CF defaults to .NET framework 3.9 for Windows CE.
     this->DefaultTargetFrameworkVersion = "v3.9";
     this->DefaultTargetFrameworkIdentifier = "WindowsEmbeddedCompact";
@@ -1275,12 +1275,6 @@
 const char* cmGlobalVisualStudio10Generator::GetToolsVersion() const
 {
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-      return "4.0";
-
-      // in Visual Studio 2013 they detached the MSBuild tools version
-      // from the .Net Framework version and instead made it have it's own
-      // version number
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "12.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index 866ace2..7e04b9f 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -310,26 +310,6 @@
                                 GetSLNFile(this->LocalGenerators[0].get()));
   }
 
-  if (this->Version == VSVersion::VS9 &&
-      !this->CMakeInstance->GetIsInTryCompile()) {
-    std::string cmakeWarnVS9;
-    if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
-          "CMAKE_WARN_VS9")) {
-      this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS9");
-      cmakeWarnVS9 = *cached;
-    } else {
-      cmSystemTools::GetEnv("CMAKE_WARN_VS9", cmakeWarnVS9);
-    }
-    if (cmakeWarnVS9.empty() || !cmIsOff(cmakeWarnVS9)) {
-      this->CMakeInstance->IssueMessage(
-        MessageType::WARNING,
-        "The \"Visual Studio 9 2008\" generator is deprecated "
-        "and will be removed in a future version of CMake."
-        "\n"
-        "Add CMAKE_WARN_VS9=OFF to the cache to disable this warning.");
-    }
-  }
-
   if (this->Version == VSVersion::VS12 &&
       !this->CMakeInstance->GetIsInTryCompile()) {
     std::string cmakeWarnVS12;
@@ -403,8 +383,7 @@
         std::string mapping;
 
         // On VS 19 and above, always map .NET SDK projects to "Any CPU".
-        if (target->IsDotNetSdkTarget() &&
-            this->GetVersion() >= VSVersion::VS16 &&
+        if (target->IsDotNetSdkTarget() && this->Version >= VSVersion::VS16 &&
             !cmGlobalVisualStudio7Generator::IsReservedTarget(
               target->GetName())) {
           mapping = "Any CPU";
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 0d357ad..b1fba8f 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -225,11 +225,6 @@
   return "generate.stamp.list";
 }
 
-void cmGlobalVisualStudio8Generator::Configure()
-{
-  this->cmGlobalVisualStudio7Generator::Configure();
-}
-
 bool cmGlobalVisualStudio8Generator::UseFolderProperty() const
 {
   // NOLINTNEXTLINE(bugprone-parent-virtual-call)
diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h
index cb0ea76..931de40 100644
--- a/Source/cmGlobalVisualStudio8Generator.h
+++ b/Source/cmGlobalVisualStudio8Generator.h
@@ -40,12 +40,6 @@
   cm::optional<std::string> const& GetTargetFrameworkIdentifier() const;
   cm::optional<std::string> const& GetTargetFrameworkTargetsVersion() const;
 
-  /**
-   * Override Configure and Generate to add the build-system check
-   * target.
-   */
-  void Configure() override;
-
   /** Return true if the target project file should have the option
       LinkLibraryDependencies and link to .sln dependencies. */
   bool NeedLinkLibraryDependencies(cmGeneratorTarget* target) override;
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx
deleted file mode 100644
index de2153d..0000000
--- a/Source/cmGlobalVisualStudio9Generator.cxx
+++ /dev/null
@@ -1,163 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmGlobalVisualStudio9Generator.h"
-
-#include <cstring>
-#include <utility>
-#include <vector>
-
-#include "cmGlobalGenerator.h"
-#include "cmGlobalGeneratorFactory.h"
-#include "cmGlobalVisualStudioGenerator.h"
-#include "cmStringAlgorithms.h"
-#include "cmSystemTools.h"
-#include "cmVisualStudioWCEPlatformParser.h"
-
-class cmake;
-
-static const char vs9generatorName[] = "Visual Studio 9 2008";
-
-class cmGlobalVisualStudio9Generator::Factory : public cmGlobalGeneratorFactory
-{
-public:
-  std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
-    const std::string& name, bool allowArch, cmake* cm) const override
-  {
-    if (strncmp(name.c_str(), vs9generatorName,
-                sizeof(vs9generatorName) - 1) != 0) {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-
-    const char* p = name.c_str() + sizeof(vs9generatorName) - 1;
-    if (p[0] == '\0') {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio9Generator(cm, name, ""));
-    }
-
-    if (!allowArch || p[0] != ' ') {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-
-    ++p;
-
-    if (!strcmp(p, "IA64")) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio9Generator(cm, name, "Itanium"));
-    }
-
-    if (!strcmp(p, "Win64")) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio9Generator(cm, name, "x64"));
-    }
-
-    cmVisualStudioWCEPlatformParser parser(p);
-    parser.ParseVersion("9.0");
-    if (!parser.Found()) {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-
-    auto ret = std::unique_ptr<cmGlobalVisualStudio9Generator>(
-      new cmGlobalVisualStudio9Generator(cm, name, p));
-    ret->WindowsCEVersion = parser.GetOSVersion();
-    return std::unique_ptr<cmGlobalGenerator>(std::move(ret));
-  }
-
-  cmDocumentationEntry GetDocumentation() const override
-  {
-    return { cmStrCat(vs9generatorName, " [arch]"),
-             "Deprecated.  Generates Visual Studio 2008 project files.  "
-             "Optional [arch] can be \"Win64\" or \"IA64\"." };
-  }
-
-  std::vector<std::string> GetGeneratorNames() const override
-  {
-    std::vector<std::string> names;
-    names.emplace_back(vs9generatorName);
-    return names;
-  }
-
-  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
-  {
-    std::vector<std::string> names;
-    names.emplace_back(cmStrCat(vs9generatorName, " Win64"));
-    names.emplace_back(cmStrCat(vs9generatorName, " IA64"));
-    cmVisualStudioWCEPlatformParser parser;
-    parser.ParseVersion("9.0");
-    const std::vector<std::string>& availablePlatforms =
-      parser.GetAvailablePlatforms();
-    for (std::string const& i : availablePlatforms) {
-      names.emplace_back(cmStrCat("Visual Studio 9 2008 ", i));
-    }
-    return names;
-  }
-
-  bool SupportsToolset() const override { return false; }
-  bool SupportsPlatform() const override { return true; }
-
-  std::vector<std::string> GetKnownPlatforms() const override
-  {
-    std::vector<std::string> platforms;
-    platforms.emplace_back("x64");
-    platforms.emplace_back("Win32");
-    platforms.emplace_back("Itanium");
-    cmVisualStudioWCEPlatformParser parser;
-    parser.ParseVersion("9.0");
-    const std::vector<std::string>& availablePlatforms =
-      parser.GetAvailablePlatforms();
-    for (std::string const& i : availablePlatforms) {
-      platforms.emplace_back(i);
-    }
-    return platforms;
-  }
-
-  std::string GetDefaultPlatformName() const override { return "Win32"; }
-};
-
-std::unique_ptr<cmGlobalGeneratorFactory>
-cmGlobalVisualStudio9Generator::NewFactory()
-{
-  return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory);
-}
-
-cmGlobalVisualStudio9Generator::cmGlobalVisualStudio9Generator(
-  cmake* cm, const std::string& name,
-  std::string const& platformInGeneratorName)
-  : cmGlobalVisualStudio8Generator(cm, name, platformInGeneratorName)
-{
-  this->Version = VSVersion::VS9;
-  std::string vc9Express;
-  this->ExpressEdition = cmSystemTools::ReadRegistryValue(
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\9.0\\Setup\\VC;"
-    "ProductDir",
-    vc9Express, cmSystemTools::KeyWOW64_32);
-}
-
-std::string cmGlobalVisualStudio9Generator::GetUserMacrosDirectory()
-{
-  std::string base;
-  std::string path;
-
-  // base begins with the VisualStudioProjectsLocation reg value...
-  if (cmSystemTools::ReadRegistryValue(
-        "HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\9.0;"
-        "VisualStudioProjectsLocation",
-        base)) {
-    cmSystemTools::ConvertToUnixSlashes(base);
-
-    // 9.0 macros folder:
-    path = cmStrCat(base, "/VSMacros80");
-    // *NOT* a typo; right now in Visual Studio 2008 beta the macros
-    // folder is VSMacros80... They may change it to 90 before final
-    // release of 2008 or they may not... we'll have to keep our eyes
-    // on it
-  }
-
-  // path is (correctly) still empty if we did not read the base value from
-  // the Registry value
-  return path;
-}
-
-std::string cmGlobalVisualStudio9Generator::GetUserMacrosRegKeyBase()
-{
-  return R"(Software\Microsoft\VisualStudio\9.0\vsmacros)";
-}
diff --git a/Source/cmGlobalVisualStudio9Generator.h b/Source/cmGlobalVisualStudio9Generator.h
deleted file mode 100644
index 1c93d49..0000000
--- a/Source/cmGlobalVisualStudio9Generator.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#pragma once
-
-#include <memory>
-#include <string>
-
-#include "cmGlobalVisualStudio8Generator.h"
-
-class cmGlobalGeneratorFactory;
-class cmake;
-
-/** \class cmGlobalVisualStudio9Generator
- * \brief Write a Unix makefiles.
- *
- * cmGlobalVisualStudio9Generator manages UNIX build process for a tree
- */
-class cmGlobalVisualStudio9Generator : public cmGlobalVisualStudio8Generator
-{
-public:
-  static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory();
-
-  /**
-   * Where does this version of Visual Studio look for macros for the
-   * current user? Returns the empty string if this version of Visual
-   * Studio does not implement support for VB macros.
-   */
-  std::string GetUserMacrosDirectory() override;
-
-  /**
-   * What is the reg key path to "vsmacros" for this version of Visual
-   * Studio?
-   */
-  std::string GetUserMacrosRegKeyBase() override;
-
-protected:
-  cmGlobalVisualStudio9Generator(cmake* cm, const std::string& name,
-                                 std::string const& platformInGeneratorName);
-
-private:
-  class Factory;
-  friend class Factory;
-};
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index 6fdc32c..f96a84c 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -104,8 +104,6 @@
 const char* cmGlobalVisualStudioGenerator::GetIDEVersion() const
 {
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-      return "9.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "12.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -127,10 +125,6 @@
   fout << '\n';
 
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-      fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n";
-      fout << "# Visual Studio 2008\n";
-      break;
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
       if (this->ExpressEdition) {
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 76713fa..4746f13 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -34,7 +34,6 @@
   /** Known versions of Visual Studio.  */
   enum class VSVersion : uint16_t
   {
-    VS9 = 90,
     VS12 = 120,
     /* VS13 = 130 was skipped */
     VS14 = 140,
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index d816d7b..b4683aa 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -125,8 +125,6 @@
   cmGlobalVisualStudioGenerator::VSVersion v)
 {
   switch (v) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-      return 9;
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return 12;
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -145,8 +143,6 @@
   cmGlobalVisualStudioGenerator::VSVersion v)
 {
   switch (v) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-      return "v90";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "v120";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -165,8 +161,6 @@
   cmGlobalVisualStudioGenerator::VSVersion v)
 {
   switch (v) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-      return "9";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "12";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -185,7 +179,6 @@
   cmGlobalVisualStudioGenerator::VSVersion v)
 {
   switch (v) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -485,7 +478,6 @@
 {
   std::string genName;
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
       break;
@@ -752,7 +744,6 @@
   const
 {
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS9:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index bf66255..aa948a5 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -264,6 +264,27 @@
 #endif
 }
 
+namespace {
+std::string ConvertToMakefilePath(std::string const& path)
+{
+  std::string result;
+  result.reserve(path.size());
+  for (char c : path) {
+    switch (c) {
+      case '\\':
+      case ' ':
+      case '#':
+        result.push_back('\\');
+        CM_FALLTHROUGH;
+      default:
+        result.push_back(c);
+        break;
+    }
+  }
+  return result;
+}
+}
+
 bool cmGlobalXCodeGenerator::FindMakeProgram(cmMakefile* mf)
 {
   // The Xcode generator knows how to lookup its build tool
@@ -653,7 +674,7 @@
   if (regenerate && isGenerateProject) {
     this->CreateReRunCMakeFile(root, gens);
     std::string file =
-      this->ConvertToRelativeForMake(this->CurrentReRunCMakeMakefile);
+      cmSystemTools::ConvertToOutputPath(this->CurrentReRunCMakeMakefile);
     cmSystemTools::ReplaceString(file, "\\ ", " ");
     cc = cm::make_unique<cmCustomCommand>();
     cc->SetCommandLines(cmMakeSingleCommandLine({ "make", "-f", file }));
@@ -732,7 +753,7 @@
 
   for (const auto& lfile : lfiles) {
     makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard "
-                   << this->ConvertToRelativeForMake(lfile) << "))\n";
+                   << ConvertToMakefilePath(lfile) << "))\n";
   }
   makefileStream << '\n';
 
@@ -743,25 +764,25 @@
     makefileStream << ".NOTPARALLEL:\n\n"
                       ".PHONY: all VERIFY_GLOBS\n\n"
                       "all: VERIFY_GLOBS "
-                   << this->ConvertToRelativeForMake(checkCache)
+                   << ConvertToMakefilePath(checkCache)
                    << "\n\n"
                       "VERIFY_GLOBS:\n"
                       "\t"
-                   << this->ConvertToRelativeForMake(
-                        cmSystemTools::GetCMakeCommand())
+                   << ConvertToMakefilePath(cmSystemTools::GetCMakeCommand())
                    << " -P "
-                   << this->ConvertToRelativeForMake(cm->GetGlobVerifyScript())
+                   << ConvertToMakefilePath(cm->GetGlobVerifyScript())
                    << "\n\n";
   }
 
-  makefileStream << this->ConvertToRelativeForMake(checkCache)
-                 << ": $(TARGETS)\n";
-  makefileStream
-    << '\t' << this->ConvertToRelativeForMake(cmSystemTools::GetCMakeCommand())
-    << " -S" << this->ConvertToRelativeForMake(root->GetSourceDirectory())
-    << " -B" << this->ConvertToRelativeForMake(root->GetBinaryDirectory())
-    << (cm->GetIgnoreWarningAsError() ? " --compile-no-warning-as-error" : "")
-    << '\n';
+  makefileStream << ConvertToMakefilePath(checkCache) << ": $(TARGETS)\n";
+  makefileStream << '\t'
+                 << ConvertToMakefilePath(cmSystemTools::GetCMakeCommand())
+                 << " -S" << ConvertToMakefilePath(root->GetSourceDirectory())
+                 << " -B" << ConvertToMakefilePath(root->GetBinaryDirectory())
+                 << (cm->GetIgnoreWarningAsError()
+                       ? " --compile-no-warning-as-error"
+                       : "")
+                 << '\n';
 }
 
 static bool objectIdLessThan(const std::unique_ptr<cmXCodeObject>& l,
@@ -2199,11 +2220,10 @@
   }
 
   std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
-  cdir = this->ConvertToRelativeForMake(cdir);
+  cdir = cmSystemTools::ConvertToOutputPath(cdir);
   std::string makecmd = cmStrCat(
-    "make -C ", cdir, " -f ",
-    this->ConvertToRelativeForMake(cmStrCat(makefile, "$CONFIGURATION")),
-    " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
+    "make -C ", cdir, " -f ", cmSystemTools::ConvertToOutputPath(makefile),
+    "$CONFIGURATION", " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
   buildphase->AddAttribute("shellScript", this->CreateString(makecmd));
   buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
 }
@@ -2237,7 +2257,7 @@
       const std::vector<std::string>& outputs = ccg.GetOutputs();
       if (!outputs.empty()) {
         for (auto const& output : outputs) {
-          makefileStream << "\\\n\t" << this->ConvertToRelativeForMake(output);
+          makefileStream << "\\\n\t" << ConvertToMakefilePath(output);
         }
       } else {
         std::ostringstream str;
@@ -2291,7 +2311,7 @@
         // There is at least one output, start the rule for it
         const char* sep = "";
         for (auto const& output : outputs) {
-          makefileStream << sep << this->ConvertToRelativeForMake(output);
+          makefileStream << sep << ConvertToMakefilePath(output);
           sep = " ";
         }
         makefileStream << ": ";
@@ -2300,7 +2320,7 @@
         makefileStream << tname[&ccg.GetCC()] << ": ";
       }
       for (auto const& dep : realDepends) {
-        makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep);
+        makefileStream << "\\\n" << ConvertToMakefilePath(dep);
       }
       makefileStream << '\n';
 
@@ -2317,12 +2337,12 @@
         // Build the command line in a single string.
         std::string cmd2 = ccg.GetCommand(c);
         cmSystemTools::ReplaceString(cmd2, "/./", "/");
-        cmd2 = this->ConvertToRelativeForMake(cmd2);
+        cmd2 = ConvertToMakefilePath(cmd2);
         std::string cmd;
         std::string wd = ccg.GetWorkingDirectory();
         if (!wd.empty()) {
           cmd += "cd ";
-          cmd += this->ConvertToRelativeForMake(wd);
+          cmd += ConvertToMakefilePath(wd);
           cmd += " && ";
         }
         cmd += cmd2;
@@ -2336,7 +2356,7 @@
               target->GetLocalGenerator()->GetMakefile()->GetSource(
                 dep, cmSourceFileLocationKind::Known)) {
           if (dsf->GetPropertyAsBool("SYMBOLIC")) {
-            makefileStream << this->ConvertToRelativeForMake(dep) << ":\n";
+            makefileStream << ConvertToMakefilePath(dep) << ":\n";
           }
         }
       }
@@ -4865,7 +4885,7 @@
           gt->GetType() == cmStateEnums::SHARED_LIBRARY ||
           gt->GetType() == cmStateEnums::MODULE_LIBRARY) {
         std::string tfull = gt->GetFullPath(configName);
-        std::string trel = this->ConvertToRelativeForMake(tfull);
+        std::string trel = ConvertToMakefilePath(tfull);
 
         // Add this target to the post-build phases of its dependencies.
         auto const y = target->GetDependTargets().find(configName);
@@ -4891,7 +4911,7 @@
         auto const x = target->GetDependLibraries().find(configName);
         if (x != target->GetDependLibraries().end()) {
           for (auto const& deplib : x->second) {
-            std::string file = this->ConvertToRelativeForMake(deplib);
+            std::string file = ConvertToMakefilePath(deplib);
             makefileStream << "\\\n\t" << file;
             dummyRules.insert(file);
           }
@@ -4903,7 +4923,7 @@
           std::string d = cmStrCat(this->GetTargetTempDir(gt, configName),
                                    "/lib", objLibName, ".a");
 
-          std::string dependency = this->ConvertToRelativeForMake(d);
+          std::string dependency = ConvertToMakefilePath(d);
           makefileStream << "\\\n\t" << dependency;
           dummyRules.insert(dependency);
         }
@@ -4911,7 +4931,7 @@
         // Write the action to remove the target if it is out of date.
         makefileStream << "\n"
                           "\t/bin/rm -f "
-                       << this->ConvertToRelativeForMake(tfull) << '\n';
+                       << ConvertToMakefilePath(tfull) << '\n';
         // if building for more than one architecture
         // then remove those executables as well
         if (this->Architectures.size() > 1) {
@@ -4921,8 +4941,7 @@
             std::string universalFile = cmStrCat(universal, architecture, '/',
                                                  gt->GetFullName(configName));
             makefileStream << "\t/bin/rm -f "
-                           << this->ConvertToRelativeForMake(universalFile)
-                           << '\n';
+                           << ConvertToMakefilePath(universalFile) << '\n';
           }
         }
         makefileStream << "\n\n";
@@ -5123,12 +5142,6 @@
            "Generate Xcode project files." };
 }
 
-std::string cmGlobalXCodeGenerator::ConvertToRelativeForMake(
-  std::string const& p)
-{
-  return cmSystemTools::ConvertToOutputPath(p);
-}
-
 std::string cmGlobalXCodeGenerator::RelativeToSource(const std::string& p)
 {
   std::string const& rootSrc =
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 2375e95..14e9308 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -163,7 +163,6 @@
   std::string RelativeToSource(const std::string& p);
   std::string RelativeToRootBinary(const std::string& p);
   std::string RelativeToBinary(const std::string& p);
-  std::string ConvertToRelativeForMake(std::string const& p);
   void CreateCustomCommands(
     cmXCodeObject* buildPhases, cmXCodeObject* sourceBuildPhase,
     cmXCodeObject* headerBuildPhase, cmXCodeObject* resourceBuildPhase,
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 22daa0e..1440771 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -4403,7 +4403,12 @@
   }
 
   // Make sure the output file name has no invalid characters.
-  std::string::size_type pos = output.find_first_of("#<>");
+  const bool hashNotAllowed = lg.GetState()->UseBorlandMake();
+  std::string::size_type pos = output.find_first_of("<>");
+  if (pos == std::string::npos && hashNotAllowed) {
+    pos = output.find_first_of('#');
+  }
+
   if (pos != std::string::npos) {
     lg.GetCMakeInstance()->IssueMessage(
       MessageType::FATAL_ERROR,
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index 12ca528..8577cc4 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -196,13 +196,6 @@
   this->FortranProject = gg->TargetIsFortranOnly(target);
   this->WindowsCEProject = gg->TargetsWindowsCE();
 
-  // Intel Fortran always uses VS9 format ".vfproj" files.
-  cmGlobalVisualStudioGenerator::VSVersion realVersion = gg->GetVersion();
-  if (this->FortranProject &&
-      gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS12) {
-    gg->SetVersion(cmGlobalVisualStudioGenerator::VSVersion::VS9);
-  }
-
   // add to the list of projects
   target->Target->SetProperty("GENERATOR_FILE_NAME", lname);
   // create the dsp.cmake file
@@ -224,7 +217,8 @@
     this->GlobalGenerator->FileReplacedDuringGenerate(fname);
   }
 
-  gg->SetVersion(realVersion);
+  this->WindowsCEProject = false;
+  this->FortranProject = false;
 }
 
 cmSourceFile* cmLocalVisualStudio7Generator::CreateVCProjBuildRule()
@@ -1139,12 +1133,7 @@
         fout << "\t\t\t\tGenerateDebugInformation=\"true\"\n";
       }
       if (this->WindowsCEProject) {
-        if (this->GetVersion() <
-            cmGlobalVisualStudioGenerator::VSVersion::VS9) {
-          fout << "\t\t\t\tSubSystem=\"9\"\n";
-        } else {
-          fout << "\t\t\t\tSubSystem=\"8\"\n";
-        }
+        fout << "\t\t\t\tSubSystem=\"8\"\n";
       }
       std::string stackVar = cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE");
       cmValue stackVal = this->Makefile->GetDefinition(stackVar);
@@ -1227,12 +1216,7 @@
         fout << "\t\t\t\tGenerateDebugInformation=\"true\"\n";
       }
       if (this->WindowsCEProject) {
-        if (this->GetVersion() <
-            cmGlobalVisualStudioGenerator::VSVersion::VS9) {
-          fout << "\t\t\t\tSubSystem=\"9\"\n";
-        } else {
-          fout << "\t\t\t\tSubSystem=\"8\"\n";
-        }
+        fout << "\t\t\t\tSubSystem=\"8\"\n";
 
         if (!linkOptions.GetFlag("EntryPointSymbol")) {
           const char* entryPointSymbol = targetOptions.UsingUnicode()
diff --git a/Source/cmLocalVisualStudio7Generator.h b/Source/cmLocalVisualStudio7Generator.h
index ce1156f..bb05226 100644
--- a/Source/cmLocalVisualStudio7Generator.h
+++ b/Source/cmLocalVisualStudio7Generator.h
@@ -91,6 +91,8 @@
     return this->SourcesVisited[target];
   };
 
+  bool IsVFProj() const override { return this->FortranProject; }
+
 protected:
   virtual void GenerateTarget(cmGeneratorTarget* target);
 
@@ -153,8 +155,8 @@
 
   friend class EventWriter;
 
-  bool FortranProject;
-  bool WindowsCEProject;
+  bool FortranProject = false;
+  bool WindowsCEProject = false;
   std::unique_ptr<cmLocalVisualStudio7GeneratorInternals> Internal;
 
   std::map<cmGeneratorTarget const*, std::set<cmSourceFile const*>>
diff --git a/Source/cmLocalVisualStudioGenerator.h b/Source/cmLocalVisualStudioGenerator.h
index 8fed1bd..3e1eb5f 100644
--- a/Source/cmLocalVisualStudioGenerator.h
+++ b/Source/cmLocalVisualStudioGenerator.h
@@ -31,6 +31,8 @@
   cmLocalVisualStudioGenerator(cmGlobalGenerator* gg, cmMakefile* mf);
   ~cmLocalVisualStudioGenerator() override;
 
+  virtual bool IsVFProj() const = 0;
+
   std::string ConstructScript(cmCustomCommandGenerator const& ccg,
                               const std::string& newline = "\n");
   std::string FinishConstructScript(VsProjectType projectType,
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index ed159fe..9dd3ce5 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -514,7 +514,11 @@
          "private dependencies of static libraries.",                         \
          3, 30, 0, cmPolicies::WARN)                                          \
   SELECT(POLICY, CMP0167, "The FindBoost module is removed.", 3, 30, 0,       \
-         cmPolicies::WARN)
+         cmPolicies::WARN)                                                    \
+  SELECT(POLICY, CMP0168,                                                     \
+         "FetchContent implements steps directly instead of through a "       \
+         "sub-build.",                                                        \
+         3, 30, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index 72eeb9d..e05eb10 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -716,6 +716,16 @@
   return this->GhsMultiIDE;
 }
 
+void cmState::SetBorlandMake(bool borlandMake)
+{
+  this->BorlandMake = borlandMake;
+}
+
+bool cmState::UseBorlandMake() const
+{
+  return this->BorlandMake;
+}
+
 void cmState::SetWatcomWMake(bool watcomWMake)
 {
   this->WatcomWMake = watcomWMake;
diff --git a/Source/cmState.h b/Source/cmState.h
index 5aceb6a..4dc982f 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -213,6 +213,8 @@
   bool UseWindowsVSIDE() const;
   void SetGhsMultiIDE(bool ghsMultiIDE);
   bool UseGhsMultiIDE() const;
+  void SetBorlandMake(bool borlandMake);
+  bool UseBorlandMake() const;
   void SetWatcomWMake(bool watcomWMake);
   bool UseWatcomWMake() const;
   void SetMinGWMake(bool minGWMake);
@@ -294,6 +296,7 @@
   bool WindowsShell = false;
   bool WindowsVSIDE = false;
   bool GhsMultiIDE = false;
+  bool BorlandMake = false;
   bool WatcomWMake = false;
   bool MinGWMake = false;
   bool NMake = false;
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index 659510c..a5f2cdf 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -26,7 +26,6 @@
   cmVS7FlagTable const* extraTable)
   : cmIDEOptions()
   , LocalGenerator(lg)
-  , Version(lg->GetVersion())
   , CurrentTool(tool)
 {
   // Store the given flag tables.
@@ -75,21 +74,14 @@
   // initialization to off, but the user has the option of removing
   // the flag to disable exception handling.  When the user does
   // remove the flag we need to override the IDE default of on.
-  switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS12:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS14:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS15:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS16:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS17:
-      // by default VS puts <ExceptionHandling></ExceptionHandling> empty
-      // for a project, to make our projects look the same put a new line
-      // and space over for the closing </ExceptionHandling> as the default
-      // value
-      this->FlagMap["ExceptionHandling"] = "\n      ";
-      break;
-    default:
-      this->FlagMap["ExceptionHandling"] = "0";
-      break;
+  if (!this->LocalGenerator->IsVFProj()) {
+    // by default VS puts <ExceptionHandling></ExceptionHandling> empty
+    // for a project, to make our projects look the same put a new line
+    // and space over for the closing </ExceptionHandling> as the default
+    // value
+    this->FlagMap["ExceptionHandling"] = "\n      ";
+  } else {
+    this->FlagMap["ExceptionHandling"] = "0";
   }
 }
 
@@ -100,13 +92,12 @@
   // to the generated project to disable logo suppression.  Otherwise
   // the GUI default is to enable suppression.
   //
-  // On Visual Studio 9, the value of this attribute should be
+  // In '.vfproj' files, the value of this attribute should be
   // "FALSE", instead of an empty string.
   if (verbose &&
       this->FlagMap.find("SuppressStartupBanner") == this->FlagMap.end()) {
     this->FlagMap["SuppressStartupBanner"] =
-      this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9 ? "FALSE"
-                                                                     : "";
+      !this->LocalGenerator->IsVFProj() ? "" : "FALSE";
   }
 }
 
@@ -373,24 +364,22 @@
   }
 
   std::ostringstream oss;
-  if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
+  if (!this->LocalGenerator->IsVFProj()) {
     oss << "%(" << tag << ')';
   }
   auto de = cmRemoveDuplicates(this->Defines);
   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
-    // Escape the definition for the compiler.
     std::string define;
-    if (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS9) {
-      define = this->LocalGenerator->EscapeForShell(di, true);
-    } else {
+    if (!this->LocalGenerator->IsVFProj()) {
+      // Escape the definition for MSBuild.
       define = di;
-    }
-    // Escape this flag for the MSBuild.
-    if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
       cmVS10EscapeForMSBuild(define);
       if (lang == "RC"_s) {
         cmSystemTools::ReplaceString(define, "\"", "\\\"");
       }
+    } else {
+      // Escape the definition for the compiler.
+      define = this->LocalGenerator->EscapeForShell(di, true);
     }
     // Store the flag in the project file.
     oss << ';' << define;
@@ -428,7 +417,7 @@
     }
 
     // Escape this include for the MSBuild.
-    if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
+    if (!this->LocalGenerator->IsVFProj()) {
       cmVS10EscapeForMSBuild(include);
     }
     oss << sep << include;
@@ -440,7 +429,7 @@
     }
   }
 
-  if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
+  if (!this->LocalGenerator->IsVFProj()) {
     oss << sep << "%(" << tag << ')';
   }
 
@@ -454,7 +443,7 @@
     std::ostringstream oss;
     const char* sep = "";
     for (std::string i : m.second) {
-      if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
+      if (!this->LocalGenerator->IsVFProj()) {
         cmVS10EscapeForMSBuild(i);
       }
       oss << sep << i;
diff --git a/Source/cmVisualStudioGeneratorOptions.h b/Source/cmVisualStudioGeneratorOptions.h
index 1ca569d..409fd62 100644
--- a/Source/cmVisualStudioGeneratorOptions.h
+++ b/Source/cmVisualStudioGeneratorOptions.h
@@ -87,7 +87,6 @@
 
 private:
   cmLocalVisualStudioGenerator* LocalGenerator;
-  cmGlobalVisualStudioGenerator::VSVersion Version;
 
   std::string Configuration;
   Tool CurrentTool;
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 319e597..775265f 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -4,10 +4,12 @@
 
 #include <algorithm>
 #include <array>
+#include <chrono>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <initializer_list>
+#include <iomanip>
 #include <iostream>
 #include <sstream>
 #include <stdexcept>
@@ -97,7 +99,6 @@
 #    include "cmGlobalNMakeMakefileGenerator.h"
 #    include "cmGlobalVisualStudio12Generator.h"
 #    include "cmGlobalVisualStudio14Generator.h"
-#    include "cmGlobalVisualStudio9Generator.h"
 #    include "cmGlobalVisualStudioVersionedGenerator.h"
 #    include "cmVSSetupHelper.h"
 
@@ -2558,7 +2559,22 @@
 #endif
 
   // actually do the configure
+  auto startTime = std::chrono::steady_clock::now();
   this->GlobalGenerator->Configure();
+  auto endTime = std::chrono::steady_clock::now();
+
+  if (this->GetWorkingMode() == cmake::NORMAL_MODE) {
+    std::ostringstream msg;
+    if (cmSystemTools::GetErrorOccurredFlag()) {
+      msg << "Configuring incomplete, errors occurred!";
+    } else {
+      auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+        endTime - startTime);
+      msg << "Configuring done (" << std::fixed << std::setprecision(1)
+          << ms.count() / 1000.0L << "s)";
+    }
+    this->UpdateProgress(msg.str(), -1);
+  }
 
 #if !defined(CMAKE_BOOTSTRAP)
   this->ConfigureLog.reset();
@@ -2636,7 +2652,6 @@
   static VSVersionedGenerator const vsGenerators[] = {
     { "14.0", "Visual Studio 14 2015" }, //
     { "12.0", "Visual Studio 12 2013" }, //
-    { "9.0", "Visual Studio 9 2008" }
   };
   static const char* const vsEntries[] = {
     "\\Setup\\VC;ProductDir", //
@@ -2891,10 +2906,20 @@
   auto profilingRAII = this->CreateProfilingEntry("project", "generate");
 #endif
 
+  auto startTime = std::chrono::steady_clock::now();
   if (!this->GlobalGenerator->Compute()) {
     return -1;
   }
   this->GlobalGenerator->Generate();
+  auto endTime = std::chrono::steady_clock::now();
+  {
+    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(endTime -
+                                                                    startTime);
+    std::ostringstream msg;
+    msg << "Generating done (" << std::fixed << std::setprecision(1)
+        << ms.count() / 1000.0L << "s)";
+    this->UpdateProgress(msg.str(), -1);
+  }
   if (!this->GraphVizFile.empty()) {
     std::cout << "Generate graphviz: " << this->GraphVizFile << '\n';
     this->GenerateGraphViz(this->GraphVizFile);
@@ -3021,7 +3046,6 @@
     cmGlobalVisualStudioVersionedGenerator::NewFactory15());
   this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory());
   this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory());
-  this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory());
   this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory());
   this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory());
   this->Generators.push_back(cmGlobalJOMMakefileGenerator::NewFactory());
@@ -3781,22 +3805,12 @@
   // itself, there is the risk of building an out-of-date solution file due
   // to limitations of the underlying build system.
   std::string const stampList = cachePath + "/" + "CMakeFiles/" +
-    cmGlobalVisualStudio9Generator::GetGenerateStampList();
+    cmGlobalVisualStudio12Generator::GetGenerateStampList();
 
   // Note that the stampList file only exists for VS generators.
   if (cmSystemTools::FileExists(stampList)) {
 
-    // Check if running for Visual Studio 9 - we need to explicitly run
-    // the glob verification script before starting the build
     this->AddScriptingCommands();
-    if (this->GlobalGenerator->MatchesGeneratorName("Visual Studio 9 2008")) {
-      std::string const globVerifyScript =
-        cachePath + "/" + "CMakeFiles/" + "VerifyGlobs.cmake";
-      if (cmSystemTools::FileExists(globVerifyScript)) {
-        std::vector<std::string> args;
-        this->ReadListFile(args, globVerifyScript);
-      }
-    }
 
     if (!cmakeCheckStampList(stampList)) {
       // Correctly initialize the home (=source) and home output (=binary)
diff --git a/Tests/BuildDepends/CMakeLists.txt b/Tests/BuildDepends/CMakeLists.txt
index dbafc85..99418df 100644
--- a/Tests/BuildDepends/CMakeLists.txt
+++ b/Tests/BuildDepends/CMakeLists.txt
@@ -42,7 +42,7 @@
 
 list(APPEND _cmake_options "-DCMAKE_FORCE_DEPFILES=1")
 
-if(NOT CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])")
+if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
   set(TEST_MULTI3 1)
   list(APPEND _cmake_options "-DTEST_MULTI3=1")
 endif()
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index a75f6c2..9ff38bf 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -40,9 +40,7 @@
 endif()
 
 # Suppress generator deprecation warnings in test suite.
-if(CMAKE_GENERATOR MATCHES "^Visual Studio 9 2008")
-  set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS9} OFF)")
-elseif(CMAKE_GENERATOR MATCHES "^Visual Studio 12 2013")
+if(CMAKE_GENERATOR MATCHES "^Visual Studio 12 2013")
   set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS12} OFF)")
 else()
   set(TEST_WARN_VS_CODE "")
@@ -74,9 +72,6 @@
   set(CMake_TEST_DEVENV "")
   if(CMAKE_VS_DEVENV_COMMAND)
     set(CMake_TEST_DEVENV "${CMAKE_VS_DEVENV_COMMAND}")
-  elseif(CMAKE_GENERATOR MATCHES "Visual Studio 9 " AND
-      NOT CMAKE_MAKE_PROGRAM MATCHES "[mM][sS][bB][uU][iI][lL][dD]\\.[eE][xX][eE]")
-    set(CMake_TEST_DEVENV "${CMAKE_MAKE_PROGRAM}")
   endif()
 
   if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
@@ -172,10 +167,7 @@
     set(CPACK_BINARY_DEB OFF)
   endif()
 
-  # Look for NuGet to use for tests.
-  find_program(NUGET_EXECUTABLE NAMES NuGet nuget)
-
-  if(NUGET_EXECUTABLE)
+  if(CMake_TEST_CPACK_NUGET)
     set(CPACK_BINARY_NUGET ON)
   else()
     set(CPACK_BINARY_NUGET OFF)
@@ -472,7 +464,7 @@
     add_subdirectory(ObjCXX)
   endif()
 
-  if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
+  if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
     ADD_TEST_MACRO(CSharpOnly CSharpOnly)
     if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
       ADD_TEST_MACRO(CSharpLinkToCxx CSharpLinkToCxx)
@@ -1120,9 +1112,7 @@
     if(CPACK_BINARY_DEB)
       list(APPEND ACTIVE_CPACK_GENERATORS DEB)
     endif()
-    # Check whether if NuGet command is found
-    # before adding NuGet tests
-    if(CPACK_BINARY_NUGET)
+    if(CMake_TEST_CPACK_NUGET)
       list(APPEND ACTIVE_CPACK_GENERATORS NUGET)
       set(CPACK_GENERATOR_STRING_NUGET NuGet)
     endif()
@@ -2209,8 +2199,6 @@
   endif()
 
   if(MSVC AND NOT MSVC_VERSION LESS 1310
-     AND (NOT CMAKE_GENERATOR MATCHES "Visual Studio 9 "
-          OR CMAKE_SIZEOF_VOID_P EQUAL 4)
      AND (NOT CMAKE_C_COMPILER_ARCHITECTURE_ID STREQUAL "ARM64")
       )
     ADD_TEST_MACRO(VSMASM VSMASM)
@@ -2221,8 +2209,7 @@
       ADD_TEST_MACRO(SBCS SBCS)
     endif()
 
-    if(NOT "${CMAKE_GENERATOR}" MATCHES "Visual Studio 9 "
-        AND NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90"
+    if(NOT CMAKE_GENERATOR_TOOLSET STREQUAL "v90"
         AND NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
       ADD_TEST_MACRO(VSWindowsFormsResx VSWindowsFormsResx)
       ADD_TEST_MACRO(VSManagedCustomCommand)
@@ -2373,7 +2360,7 @@
     endif()
   endif()
 
-  if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])" AND nasm)
+  if(CMAKE_GENERATOR MATCHES "Visual Studio" AND nasm)
     ADD_TEST_MACRO(VSNASM VSNASM)
   endif()
 
diff --git a/Tests/CMakeOnly/CMakeLists.txt b/Tests/CMakeOnly/CMakeLists.txt
index ea06464..727c7fc 100644
--- a/Tests/CMakeOnly/CMakeLists.txt
+++ b/Tests/CMakeOnly/CMakeLists.txt
@@ -54,7 +54,7 @@
   add_CMakeOnly_test(CompilerIdFortran)
   set_property(TEST CMakeOnly.CompilerIdFortran APPEND PROPERTY LABELS "Fortran")
 endif()
-if(CMAKE_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])")
+if(CMAKE_GENERATOR MATCHES "Visual Studio")
   add_CMakeOnly_test(CompilerIdCSharp)
 endif()
 
diff --git a/Tests/CompileOptions/CMakeLists.txt b/Tests/CompileOptions/CMakeLists.txt
index 0fbfb83..ad2075e 100644
--- a/Tests/CompileOptions/CMakeLists.txt
+++ b/Tests/CompileOptions/CMakeLists.txt
@@ -74,10 +74,8 @@
   endif()
   set_property(TARGET CompileOptions APPEND PROPERTY COMPILE_OPTIONS "-DFLAG_B=2" "-DFLAG_C=2" "-DFLAG_D=1")
   set_property(TARGET testlib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "-DFLAG_D=2")
-  if(NOT CMAKE_GENERATOR MATCHES "^Visual Studio 9")
-    set_property(TARGET testlib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "-DFLAG_E=1")
-    set_property(SOURCE main.cpp PROPERTY COMPILE_OPTIONS "-DFLAG_E=2")
-  endif()
+  set_property(TARGET testlib APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "-DFLAG_E=1")
+  set_property(SOURCE main.cpp PROPERTY COMPILE_OPTIONS "-DFLAG_E=2")
 endif()
 
 target_link_libraries(CompileOptions testlib)
diff --git a/Tests/CustomCommand/CMakeLists.txt b/Tests/CustomCommand/CMakeLists.txt
index d46ee08..c145907 100644
--- a/Tests/CustomCommand/CMakeLists.txt
+++ b/Tests/CustomCommand/CMakeLists.txt
@@ -588,3 +588,12 @@
 # Test empty COMMANDs are omitted
 add_executable(empty_command empty_command.cxx)
 add_custom_command(TARGET empty_command POST_BUILD COMMAND $<0:date>)
+
+# Test OUTPUT allows filenames containing "#" on generators that support this
+if(NOT CMAKE_GENERATOR MATCHES "Borland Makefiles")
+  add_custom_target(file_with_hash ALL DEPENDS "${PROJECT_BINARY_DIR}/hash#in#name.txt")
+  add_custom_command(
+    OUTPUT "${PROJECT_BINARY_DIR}/hash#in#name.txt"
+    COMMAND ${CMAKE_COMMAND} -E touch "${PROJECT_BINARY_DIR}/hash#in#name.txt"
+  )
+endif()
diff --git a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
index 18f8fda..f92db5f 100644
--- a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
+++ b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestArtifactsInteractive LANGUAGES C)
 
diff --git a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
index e0148f3..e9d14f5 100644
--- a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
+++ b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestCustomFailureMessage LANGUAGES NONE)
 
diff --git a/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
index 0fb3036..d72d258 100644
--- a/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
+++ b/Tests/FindPython/CustomFailureMessage/Check/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestCustomFailureMessage.Check LANGUAGES NONE)
 
diff --git a/Tests/FindPython/DifferentComponents/CMakeLists.txt b/Tests/FindPython/DifferentComponents/CMakeLists.txt
index e3e7b36..e72b4bf 100644
--- a/Tests/FindPython/DifferentComponents/CMakeLists.txt
+++ b/Tests/FindPython/DifferentComponents/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestDifferentComponents LANGUAGES C)
 
diff --git a/Tests/FindPython/ExactVersion/CMakeLists.txt b/Tests/FindPython/ExactVersion/CMakeLists.txt
index 1bd94c4..e3bb43b 100644
--- a/Tests/FindPython/ExactVersion/CMakeLists.txt
+++ b/Tests/FindPython/ExactVersion/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestExactVersion LANGUAGES C)
 
diff --git a/Tests/FindPython/FindPythonScript.cmake b/Tests/FindPython/FindPythonScript.cmake
index 808496e..fa51f59 100644
--- a/Tests/FindPython/FindPythonScript.cmake
+++ b/Tests/FindPython/FindPythonScript.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.12)
+cmake_minimum_required(VERSION 3.15)
 if (PYTHON_MUST_NOT_BE_FOUND)
   find_package(${PYTHON_PACKAGE_NAME} QUIET)
   if (${PYTHON_PACKAGE_NAME}_FOUND)
diff --git a/Tests/FindPython/Implementation/CMakeLists.txt b/Tests/FindPython/Implementation/CMakeLists.txt
index 8086c16..da33034 100644
--- a/Tests/FindPython/Implementation/CMakeLists.txt
+++ b/Tests/FindPython/Implementation/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestImplementation${Python_REQUESTED_IMPLEMENTATION} LANGUAGES NONE)
 
diff --git a/Tests/FindPython/IronPython/CMakeLists.txt b/Tests/FindPython/IronPython/CMakeLists.txt
index fd3182e..3c28527 100644
--- a/Tests/FindPython/IronPython/CMakeLists.txt
+++ b/Tests/FindPython/IronPython/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestIronPython LANGUAGES NONE)
 
diff --git a/Tests/FindPython/IronPython2/CMakeLists.txt b/Tests/FindPython/IronPython2/CMakeLists.txt
index b623972..0a4dcb4 100644
--- a/Tests/FindPython/IronPython2/CMakeLists.txt
+++ b/Tests/FindPython/IronPython2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestIronPython2 LANGUAGES NONE)
 
diff --git a/Tests/FindPython/IronPython3/CMakeLists.txt b/Tests/FindPython/IronPython3/CMakeLists.txt
index b09097a..f39b84d 100644
--- a/Tests/FindPython/IronPython3/CMakeLists.txt
+++ b/Tests/FindPython/IronPython3/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestIronPython3 LANGUAGES NONE)
 
diff --git a/Tests/FindPython/MultiplePackages/CMakeLists.txt b/Tests/FindPython/MultiplePackages/CMakeLists.txt
index 352a2f6f..6536e46 100644
--- a/Tests/FindPython/MultiplePackages/CMakeLists.txt
+++ b/Tests/FindPython/MultiplePackages/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestMultiplePackages C)
 
@@ -20,7 +20,7 @@
 
   add_test (NAME python2_spam2
             COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-            "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")")
+            "${Python2_INTERPRETER}" -c "import spam2; spam2.system(\"cd\")")
 
 endif()
 
@@ -40,6 +40,6 @@
 
   add_test (NAME python3_spam3
             COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-            "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
+            "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")")
 
 endif()
diff --git a/Tests/FindPython/NumPy/CMakeLists.txt b/Tests/FindPython/NumPy/CMakeLists.txt
index 336bb83..c5552d8 100644
--- a/Tests/FindPython/NumPy/CMakeLists.txt
+++ b/Tests/FindPython/NumPy/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestNumPy LANGUAGES C)
 
@@ -12,7 +12,7 @@
 
   add_test (NAME python2_arraytest
     COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest2>"
-    "${Python2_EXECUTABLE}" -c "import numpy; import arraytest2; arraytest2.vecsq(numpy.array([1, 2, 3]));")
+    "${Python2_INTERPRETER}" -c "import numpy; import arraytest2; arraytest2.vecsq(numpy.array([1, 2, 3]));")
 
 endif()
 
@@ -26,6 +26,6 @@
 
   add_test (NAME python3_arraytest
     COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest3>"
-    "${Python3_EXECUTABLE}" -c "import numpy; import arraytest3; arraytest3.vecsq(numpy.array([1, 2, 3]));")
+    "${Python3_INTERPRETER}" -c "import numpy; import arraytest3; arraytest3.vecsq(numpy.array([1, 2, 3]));")
 
 endif()
diff --git a/Tests/FindPython/NumPyOnly/CMakeLists.txt b/Tests/FindPython/NumPyOnly/CMakeLists.txt
index 115cf2b..db7e68e 100644
--- a/Tests/FindPython/NumPyOnly/CMakeLists.txt
+++ b/Tests/FindPython/NumPyOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestNumPyOnly LANGUAGES C)
 
diff --git a/Tests/FindPython/PyPy/CMakeLists.txt b/Tests/FindPython/PyPy/CMakeLists.txt
index dfc22d8..2e8bc58 100644
--- a/Tests/FindPython/PyPy/CMakeLists.txt
+++ b/Tests/FindPython/PyPy/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPyPy LANGUAGES C)
 
diff --git a/Tests/FindPython/PyPy2/CMakeLists.txt b/Tests/FindPython/PyPy2/CMakeLists.txt
index 5b7ce30..2d89531 100644
--- a/Tests/FindPython/PyPy2/CMakeLists.txt
+++ b/Tests/FindPython/PyPy2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPyPy2 LANGUAGES C)
 
diff --git a/Tests/FindPython/PyPy3/CMakeLists.txt b/Tests/FindPython/PyPy3/CMakeLists.txt
index b702c99..93388a0 100644
--- a/Tests/FindPython/PyPy3/CMakeLists.txt
+++ b/Tests/FindPython/PyPy3/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPyPy3 LANGUAGES C)
 
diff --git a/Tests/FindPython/Python/CMakeLists.txt b/Tests/FindPython/Python/CMakeLists.txt
index 85b1711..7074649 100644
--- a/Tests/FindPython/Python/CMakeLists.txt
+++ b/Tests/FindPython/Python/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython LANGUAGES C)
 
@@ -32,7 +32,7 @@
 
   add_test (NAME python_spam${Python_REQUESTED_VERSION}
             COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam${Python_REQUESTED_VERSION}>"
-            "${Python_EXECUTABLE}" -c "import spam${Python_REQUESTED_VERSION}; spam${Python_REQUESTED_VERSION}.system(\"cd\")")
+            "${Python_INTERPRETER}" -c "import spam${Python_REQUESTED_VERSION}; spam${Python_REQUESTED_VERSION}.system(\"cd\")")
 else()
   add_test(NAME findpython_script
            COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python
diff --git a/Tests/FindPython/Python2/CMakeLists.txt b/Tests/FindPython/Python2/CMakeLists.txt
index 95ed495..f858574 100644
--- a/Tests/FindPython/Python2/CMakeLists.txt
+++ b/Tests/FindPython/Python2/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython2 LANGUAGES C)
 
@@ -39,7 +39,7 @@
 
 add_test (NAME python2_spam2
           COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam2>"
-          "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")")
+          "${Python2_INTERPRETER}" -c "import spam2; spam2.system(\"cd\")")
 
 add_test(NAME findpython2_script
          COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python2
diff --git a/Tests/FindPython/Python2Embedded/CMakeLists.txt b/Tests/FindPython/Python2Embedded/CMakeLists.txt
index d9b2d22..0a04f70 100644
--- a/Tests/FindPython/Python2Embedded/CMakeLists.txt
+++ b/Tests/FindPython/Python2Embedded/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython2Embedded LANGUAGES C)
 
diff --git a/Tests/FindPython/Python2Fail/CMakeLists.txt b/Tests/FindPython/Python2Fail/CMakeLists.txt
index 7a6520d..7f7b906 100644
--- a/Tests/FindPython/Python2Fail/CMakeLists.txt
+++ b/Tests/FindPython/Python2Fail/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython2Fail C)
 
diff --git a/Tests/FindPython/Python2Module/CMakeLists.txt b/Tests/FindPython/Python2Module/CMakeLists.txt
index 7334d7a..6fffad4 100644
--- a/Tests/FindPython/Python2Module/CMakeLists.txt
+++ b/Tests/FindPython/Python2Module/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython2Module LANGUAGES C)
 
@@ -34,4 +34,4 @@
 
 add_test (NAME python2_spam2
           COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam2>"
-          "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")")
+          "${Python2_INTERPRETER}" -c "import spam2; spam2.system(\"cd\")")
diff --git a/Tests/FindPython/Python2SABIModule/CMakeLists.txt b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
index ffbaa33..c43dedc 100644
--- a/Tests/FindPython/Python2SABIModule/CMakeLists.txt
+++ b/Tests/FindPython/Python2SABIModule/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython2SABIModule LANGUAGES C)
 
diff --git a/Tests/FindPython/Python3/CMakeLists.txt b/Tests/FindPython/Python3/CMakeLists.txt
index 42d31f2..53d8b12 100644
--- a/Tests/FindPython/Python3/CMakeLists.txt
+++ b/Tests/FindPython/Python3/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython3 LANGUAGES C)
 
@@ -39,7 +39,7 @@
 
 add_test (NAME python3_spam3
           COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-          "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
+          "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")")
 
 add_test(NAME findpython3_script
          COMMAND "${CMAKE_COMMAND}" -DPYTHON_PACKAGE_NAME=Python3
diff --git a/Tests/FindPython/Python3Embedded/CMakeLists.txt b/Tests/FindPython/Python3Embedded/CMakeLists.txt
index 1d362be..46f7042 100644
--- a/Tests/FindPython/Python3Embedded/CMakeLists.txt
+++ b/Tests/FindPython/Python3Embedded/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython3Embedded LANGUAGES C)
 
diff --git a/Tests/FindPython/Python3Fail/CMakeLists.txt b/Tests/FindPython/Python3Fail/CMakeLists.txt
index 5eca0cb..810375b 100644
--- a/Tests/FindPython/Python3Fail/CMakeLists.txt
+++ b/Tests/FindPython/Python3Fail/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython3Fail C)
 
diff --git a/Tests/FindPython/Python3Module/CMakeLists.txt b/Tests/FindPython/Python3Module/CMakeLists.txt
index 57c0ddf..e47a76a 100644
--- a/Tests/FindPython/Python3Module/CMakeLists.txt
+++ b/Tests/FindPython/Python3Module/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython3Module LANGUAGES C)
 
@@ -43,4 +43,4 @@
 
 add_test (NAME python3_spam3
           COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-          "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
+          "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")")
diff --git a/Tests/FindPython/Python3SABIModule/CMakeLists.txt b/Tests/FindPython/Python3SABIModule/CMakeLists.txt
index e045b69..77d3abc 100644
--- a/Tests/FindPython/Python3SABIModule/CMakeLists.txt
+++ b/Tests/FindPython/Python3SABIModule/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.7)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestPython3SABIModule LANGUAGES C)
 
@@ -52,4 +52,4 @@
 
 add_test (NAME python3_spam3
           COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-          "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
+          "${Python3_INTERPRETER}" -c "import spam3; spam3.system(\"cd\")")
diff --git a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
index eec28a5..5df01c6 100644
--- a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestRequiredArtifacts LANGUAGES C)
 
diff --git a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
index 4d9c7c8..25bdcf3 100644
--- a/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/Check/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestRequiredArtifacts.Check LANGUAGES C)
 
diff --git a/Tests/FindPython/SOABI/CMakeLists.txt b/Tests/FindPython/SOABI/CMakeLists.txt
index 6c0e9a9..362df7f 100644
--- a/Tests/FindPython/SOABI/CMakeLists.txt
+++ b/Tests/FindPython/SOABI/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestSOABI LANGUAGES C)
 
diff --git a/Tests/FindPython/VirtualEnv/CMakeLists.txt b/Tests/FindPython/VirtualEnv/CMakeLists.txt
index ea742ea..7837916 100644
--- a/Tests/FindPython/VirtualEnv/CMakeLists.txt
+++ b/Tests/FindPython/VirtualEnv/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestVirtualEnv LANGUAGES NONE)
 
diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake
index 020ecac..8f56d00 100644
--- a/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake
+++ b/Tests/FindPython/VirtualEnv/VirtualEnvDefault.cmake
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.15)
 
 find_package (Python3 REQUIRED)
 
diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake
index 29a4924..9ae4975 100644
--- a/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake
+++ b/Tests/FindPython/VirtualEnv/VirtualEnvOnly.cmake
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.15)
 
 #
 # Virtual environment is defined for python3
diff --git a/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake b/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake
index 89f27d8..353a91b 100644
--- a/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake
+++ b/Tests/FindPython/VirtualEnv/VirtualEnvStandard.cmake
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.15)
 
 set (Python3_FIND_VIRTUALENV STANDARD)
 find_package (Python3 REQUIRED)
diff --git a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
index 3a64c31..6482793 100644
--- a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
+++ b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.15)
 
 project(TestVirtualEnvConda LANGUAGES NONE)
 
diff --git a/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake b/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake
index 020ecac..8f56d00 100644
--- a/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake
+++ b/Tests/FindPython/VirtualEnvConda/VirtualEnvDefault.cmake
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.15)
 
 find_package (Python3 REQUIRED)
 
diff --git a/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake b/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake
index 29a4924..9ae4975 100644
--- a/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake
+++ b/Tests/FindPython/VirtualEnvConda/VirtualEnvOnly.cmake
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.15)
 
 #
 # Virtual environment is defined for python3
diff --git a/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake b/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake
index 89f27d8..353a91b 100644
--- a/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake
+++ b/Tests/FindPython/VirtualEnvConda/VirtualEnvStandard.cmake
@@ -1,3 +1,4 @@
+cmake_minimum_required(VERSION 3.15)
 
 set (Python3_FIND_VIRTUALENV STANDARD)
 find_package (Python3 REQUIRED)
diff --git a/Tests/IncludeDirectories/CMakeLists.txt b/Tests/IncludeDirectories/CMakeLists.txt
index e82cea2..99cad41 100644
--- a/Tests/IncludeDirectories/CMakeLists.txt
+++ b/Tests/IncludeDirectories/CMakeLists.txt
@@ -98,8 +98,8 @@
   # NMake and Borland seem to have no way to encode a single '^'.
   string(APPEND special_chars "^")
 endif()
-if(NOT CMAKE_GENERATOR MATCHES "Visual Studio 9 2008|Watcom WMake")
-  # The vcproj format separates values with ','.
+if(NOT CMAKE_GENERATOR MATCHES "Watcom WMake")
+  # The wmake format does not support ','.
   string(APPEND special_chars ",")
 endif()
 if(NOT WIN32 AND NOT CYGWIN)
diff --git a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
index fada37a..a5f9622 100644
--- a/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
+++ b/Tests/RunCMake/BuildDepends/RunCMakeTest.cmake
@@ -180,8 +180,7 @@
   unset(RunCMake_TEST_NO_CLEAN)
 endif()
 
-if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode" AND
-    NOT RunCMake_GENERATOR MATCHES "Visual Studio (9|10)( |$)")
+if(RunCMake_GENERATOR MATCHES "Make|Ninja|Visual Studio|Xcode")
   unset(run_BuildDepends_skip_step_3)
   run_BuildDepends(CustomCommandDepfile)
   set(run_BuildDepends_skip_step_3 1)
@@ -191,8 +190,7 @@
   run_BuildDepends(MakeDependencies)
 endif()
 
-if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 " OR
-   (RunCMake_GENERATOR MATCHES "Ninja" AND ninja_version VERSION_LESS 1.7))
+if(RunCMake_GENERATOR MATCHES "Ninja" AND ninja_version VERSION_LESS 1.7)
   # This build tool misses the dependency.
   set(run_BuildDepends_skip_step_2 1)
 endif()
diff --git a/Tests/RunCMake/CMP0037/RunCMakeTest.cmake b/Tests/RunCMake/CMP0037/RunCMakeTest.cmake
index 558fba3..89beb59 100644
--- a/Tests/RunCMake/CMP0037/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0037/RunCMakeTest.cmake
@@ -45,6 +45,10 @@
 run_cmake(WARN-cond-test)
 run_cmake(WARN-cond-package)
 
+run_cmake(alias-test-NEW)
+run_cmake(alias-test-OLD)
+run_cmake(alias-test-WARN)
+
 if(RunCMake_GENERATOR MATCHES "Make|Ninja")
   run_cmake(NEW-cond-package_source)
   run_cmake(OLD-cond-package_source)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/CMP0037/alias-test-NEW-result.txt
similarity index 100%
rename from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
rename to Tests/RunCMake/CMP0037/alias-test-NEW-result.txt
diff --git a/Tests/RunCMake/CMP0037/alias-test-NEW-stderr.txt b/Tests/RunCMake/CMP0037/alias-test-NEW-stderr.txt
new file mode 100644
index 0000000..68a36cc
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-NEW-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Error at alias-test-common\.cmake:[0-9]+ \(add_library\):
+  The target name "test" is reserved when CTest testing is enabled\.
+Call Stack \(most recent call first\):
+  alias-test-NEW\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0037/alias-test-NEW.cmake b/Tests/RunCMake/CMP0037/alias-test-NEW.cmake
new file mode 100644
index 0000000..32569b5
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0037 NEW)
+include(alias-test-common.cmake)
diff --git a/Tests/RunCMake/CMP0037/alias-test-OLD-stderr.txt b/Tests/RunCMake/CMP0037/alias-test-OLD-stderr.txt
new file mode 100644
index 0000000..bf177e2
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at alias-test-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0037 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0037/alias-test-OLD.cmake b/Tests/RunCMake/CMP0037/alias-test-OLD.cmake
new file mode 100644
index 0000000..1f3e770
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0037 OLD)
+include(alias-test-common.cmake)
diff --git a/Tests/RunCMake/CMP0037/alias-test-WARN-stderr.txt b/Tests/RunCMake/CMP0037/alias-test-WARN-stderr.txt
new file mode 100644
index 0000000..43bf98b
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-WARN-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Warning \(dev\) at alias-test-common\.cmake:[0-9]+ \(add_library\):
+  Policy CMP0037 is not set: Target names should not be reserved and should
+  match a validity pattern\.  Run "cmake --help-policy CMP0037" for policy
+  details\.  Use the cmake_policy command to set the policy and suppress this
+  warning\.
+
+  The target name "test" is reserved when CTest testing is enabled\.  It may
+  result in undefined behavior\.
+Call Stack \(most recent call first\):
+  alias-test-WARN\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CMP0037/alias-test-WARN.cmake b/Tests/RunCMake/CMP0037/alias-test-WARN.cmake
new file mode 100644
index 0000000..688cb95
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-WARN.cmake
@@ -0,0 +1,2 @@
+# leave CMP0037 unset
+include(alias-test-common.cmake)
diff --git a/Tests/RunCMake/CMP0037/alias-test-common.cmake b/Tests/RunCMake/CMP0037/alias-test-common.cmake
new file mode 100644
index 0000000..f6e5000
--- /dev/null
+++ b/Tests/RunCMake/CMP0037/alias-test-common.cmake
@@ -0,0 +1,3 @@
+enable_testing()
+add_library(iface INTERFACE)
+add_library(test ALIAS iface)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-stderr.txt b/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-stderr.txt
deleted file mode 100644
index 1159ec0..0000000
--- a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Error at CMP0069-NEW-generator\.cmake:[0-9]+ \(add_executable\):
-  CMake doesn't support IPO for current generator
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)
-+
-CMake Generate step failed\.  Build files cannot be regenerated correctly\.$
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator.cmake b/Tests/RunCMake/CMP0069/CMP0069-NEW-generator.cmake
deleted file mode 100644
index 80d4e15..0000000
--- a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-cmake_policy(SET CMP0069 NEW)
-
-set(_CMAKE_CXX_IPO_SUPPORTED_BY_CMAKE YES)
-set(_CMAKE_CXX_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
-
-add_executable(foo main.cpp)
-set_target_properties(foo PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
diff --git a/Tests/RunCMake/CMP0069/RunCMakeTest.cmake b/Tests/RunCMake/CMP0069/RunCMakeTest.cmake
index 456e6a6..6128325 100644
--- a/Tests/RunCMake/CMP0069/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0069/RunCMakeTest.cmake
@@ -4,7 +4,3 @@
 run_cmake(CMP0069-NEW-cmake)
 run_cmake(CMP0069-NEW-compiler)
 run_cmake(CMP0069-WARN)
-
-if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ")
-  run_cmake(CMP0069-NEW-generator)
-endif()
diff --git a/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt
index 6bf944e..1632d63 100644
--- a/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt
@@ -1,4 +1,4 @@
-CMake Warning \(dev\) at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\):
+CMake Warning \(dev\) at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is
   not set\.  The policy's OLD behavior will be used\.  When using a URL
   download, the timestamps of extracted files should preferably be that of
@@ -9,7 +9,7 @@
   DOWNLOAD_EXTRACT_TIMESTAMP option with a value of true to avoid this
   robustness issue\.
 .*
-CMake Warning \(dev\) at .*/Modules/FetchContent.cmake:[0-9]+ \(message\):
+CMake Warning \(dev\) at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\):
   The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is
   not set\.  The policy's OLD behavior will be used\.  When using a URL
   download, the timestamps of extracted files should preferably be that of
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 192675e..76a68b4 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -516,10 +516,15 @@
 add_RunCMake_test(add_test -DPSEUDO_EMULATOR=$<TARGET_FILE:pseudo_emulator>)
 add_RunCMake_test(build_command)
 add_executable(exit_code exit_code.c)
+if(NOT CMAKE_C_COMPILER_ID MATCHES "OrangeC|Watcom"
+    AND NOT CMAKE_C_FLAGS MATCHES "-fsanitize=")
+  add_executable(exit_crash exit_crash.c)
+  set(EXIT_CRASH_EXE $<TARGET_FILE:exit_crash>)
+endif()
 set(execute_process_ARGS
   -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code>
+  -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE}
   -DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin>
-  -DPython_EXECUTABLE=${Python_EXECUTABLE}
   -DCYGWIN=${CYGWIN}
   )
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
@@ -565,6 +570,7 @@
 foreach(var
     CMake_TEST_NO_NETWORK
     CMake_TEST_TLS_VERIFY_URL
+    CMake_TEST_TLS_VERIFY_URL_BAD
     CMake_TEST_TLS_VERSION
     )
   if(DEFINED ${var})
@@ -736,13 +742,7 @@
 if("${CMAKE_GENERATOR}" MATCHES "Visual Studio")
   add_RunCMake_test(CMAKE_MSVCIDE_RUN_PATH)
   add_RunCMake_test(include_external_msproject -DVS_PLATFORM_NAME=${CMAKE_VS_PLATFORM_NAME})
-  if("${CMAKE_GENERATOR}" MATCHES "Visual Studio (9|10)" AND NOT CMAKE_VS_DEVENV_COMMAND)
-    set(NO_USE_FOLDERS 1)
-  endif()
-  add_RunCMake_test(VSSolution -DNO_USE_FOLDERS=${NO_USE_FOLDERS})
-endif()
-
-if("${CMAKE_GENERATOR}" MATCHES "Visual Studio ([^9]|9[0-9])")
+  add_RunCMake_test(VSSolution)
   add_RunCMake_test(VS10Project
     -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
     -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}
@@ -753,7 +753,8 @@
   endif()
 endif()
 
-if(CMAKE_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])")
+if(CMAKE_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])"
+    AND NOT CMAKE_GENERATOR_TOOLSET MATCHES "^(v80|v90|v100|v110|v120)$")
   add_RunCMake_test(VsDotnetSdk)
   add_RunCMake_test(VsNugetPackageRestore)
 endif()
@@ -1163,7 +1164,7 @@
   set_property(TEST RunCMake.Android PROPERTY TIMEOUT ${CMake_TEST_ANDROID_TIMEOUT})
 endif()
 
-if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|9[0-9])")
+if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
   add_RunCMake_test(CSharpCustomCommand)
   if(NOT CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64")
     add_RunCMake_test(CSharpReferenceImport)
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index 42a4ed9..2ec0a43 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -262,15 +262,13 @@
   run_cmake_presets(VisualStudioWin32)
   run_cmake_presets(VisualStudioWin64)
   run_cmake_presets(VisualStudioWin32Override -A x64)
-  if(NOT RunCMake_GENERATOR STREQUAL "Visual Studio 9 2008")
-    run_cmake_presets(VisualStudioToolset)
-    run_cmake_presets(VisualStudioToolsetOverride -T "Test Toolset")
-    run_cmake_presets(VisualStudioInheritanceParent)
-    run_cmake_presets(VisualStudioInheritanceChild)
-    run_cmake_presets(VisualStudioInheritanceOverride)
-    run_cmake_presets(VisualStudioInheritanceMulti)
-    run_cmake_presets(VisualStudioInheritanceMultiSecond)
-  endif()
+  run_cmake_presets(VisualStudioToolset)
+  run_cmake_presets(VisualStudioToolsetOverride -T "Test Toolset")
+  run_cmake_presets(VisualStudioInheritanceParent)
+  run_cmake_presets(VisualStudioInheritanceChild)
+  run_cmake_presets(VisualStudioInheritanceOverride)
+  run_cmake_presets(VisualStudioInheritanceMulti)
+  run_cmake_presets(VisualStudioInheritanceMultiSecond)
 else()
   run_cmake_presets(ArchToolsetStrategyNone)
   run_cmake_presets(ArchToolsetStrategyDefault)
diff --git a/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt
index e3d31c5..866fa55 100644
--- a/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NoCXX23TargetRequired-stderr.txt
@@ -3,7 +3,7 @@
   the "__CMAKE::CXX23" target exist, but it was not provided by the
   toolchain.  Reason:
 
-    (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain|Unsupported generator: [^\n]*)
+    (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*)
 
 
 CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt
index 8addcb2..6e14f1e 100644
--- a/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NoCXX26TargetRequired-stderr.txt
@@ -3,7 +3,7 @@
   the "__CMAKE::CXX26" target exist, but it was not provided by the
   toolchain.  Reason:
 
-    (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain|Unsupported generator: [^\n]*)
+    (Toolchain does not support discovering `import std` support|Experimental `import std` support not enabled when detecting toolchain; it must be set before `CXX` is enabled \(usually a `project\(\)` call\)|Unsupported generator: [^\n]*)
 
 
 CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake b/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake
index 2fb159e..1dcf0ef 100644
--- a/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CheckIPOSupported/RunCMakeTest.cmake
@@ -12,13 +12,8 @@
 run_cmake(cmp0069-is-old)
 
 if(_CMAKE_C_IPO_SUPPORTED_BY_CMAKE
-    AND _CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER
-    AND NOT RunCMake_GENERATOR MATCHES "^Visual Studio 9 ")
+    AND _CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER)
   run_cmake(CMP0138-WARN)
   run_cmake(CMP0138-OLD)
   run_cmake(CMP0138-NEW)
 endif()
-
-if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ")
-  run_cmake(not-supported-by-generator)
-endif()
diff --git a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-result.txt b/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-stderr.txt b/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-stderr.txt
deleted file mode 100644
index a2aa58c..0000000
--- a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-^CMake Error at .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(message\):
-  IPO is not supported \(CMake doesn't support IPO for current generator\)\.
-Call Stack \(most recent call first\):
-  .*/Modules/CheckIPOSupported\.cmake:[0-9]+ \(_ipo_not_supported\)
-  not-supported-by-generator\.cmake:[0-9]+ \(check_ipo_supported\)
-  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator.cmake b/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator.cmake
deleted file mode 100644
index c32af6e..0000000
--- a/Tests/RunCMake/CheckIPOSupported/not-supported-by-generator.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-project(${RunCMake_TEST} LANGUAGES C)
-
-set(_CMAKE_C_IPO_SUPPORTED_BY_CMAKE YES)
-set(_CMAKE_C_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
-
-check_ipo_supported()
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake
deleted file mode 100644
index e69de29..0000000
--- a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-OFF.cmake
+++ /dev/null
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt
deleted file mode 100644
index c3329a0..0000000
--- a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON-stderr.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-^CMake Warning:
-  The "Visual Studio 9 2008" generator is deprecated and will be removed in a
-  future version of CMake.
-
-  Add CMAKE_WARN_VS9=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake
deleted file mode 100644
index e69de29..0000000
--- a/Tests/RunCMake/CommandLine/DeprecateVS9-WARN-ON.cmake
+++ /dev/null
diff --git a/Tests/RunCMake/CommandLine/Envgen-A-platform-stderr-vs9.txt b/Tests/RunCMake/CommandLine/Envgen-A-platform-stderr-vs9.txt
deleted file mode 100644
index 4eae6aa..0000000
--- a/Tests/RunCMake/CommandLine/Envgen-A-platform-stderr-vs9.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-^CMake Error at CMakeLists.+
-  No CMAKE_C_COMPILER could be found.
diff --git a/Tests/RunCMake/CommandLine/Envgen-platform-invalid-stderr-vs9.txt b/Tests/RunCMake/CommandLine/Envgen-platform-invalid-stderr-vs9.txt
deleted file mode 100644
index 4eae6aa..0000000
--- a/Tests/RunCMake/CommandLine/Envgen-platform-invalid-stderr-vs9.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-^CMake Error at CMakeLists.+
-  No CMAKE_C_COMPILER could be found.
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 7b34773..8a5a25a 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -393,16 +393,10 @@
       # Envvar shouldn't affect existing build tree
       run_cmake_command(Envgen-platform-existing ${CMAKE_COMMAND} -E chdir ..
         ${CMAKE_COMMAND} --build Envgen-build)
-      if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ")
-        set(RunCMake-stderr-file "Envgen-platform-invalid-stderr-vs9.txt")
-      endif()
       run_cmake_command(Envgen-platform-invalid ${CMAKE_COMMAND} ${source_dir})
       unset(RunCMake-stderr-file)
       # Command line -G implies -A""
       run_cmake_command(Envgen-G-implicit-platform ${CMAKE_COMMAND} -G "${RunCMake_GENERATOR}" ${source_dir})
-      if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 ")
-        set(RunCMake-stderr-file "Envgen-A-platform-stderr-vs9.txt")
-      endif()
       run_cmake_command(Envgen-A-platform ${CMAKE_COMMAND} -A "fromcli" ${source_dir})
       unset(RunCMake-stderr-file)
       unset(ENV{CMAKE_GENERATOR_PLATFORM})
@@ -1124,13 +1118,6 @@
 run_cmake(ProfilingTest)
 unset(RunCMake_TEST_OPTIONS)
 
-if(RunCMake_GENERATOR MATCHES "^Visual Studio 9 2008")
-  run_cmake_with_options(DeprecateVS9-WARN-ON -DCMAKE_WARN_VS9=ON)
-  unset(ENV{CMAKE_WARN_VS9})
-  run_cmake(DeprecateVS9-WARN-ON)
-  run_cmake_with_options(DeprecateVS9-WARN-OFF -DCMAKE_WARN_VS9=OFF)
-endif()
-
 if(RunCMake_GENERATOR MATCHES "^Visual Studio 12 2013")
   run_cmake_with_options(DeprecateVS12-WARN-ON -DCMAKE_WARN_VS12=ON)
   unset(ENV{CMAKE_WARN_VS12})
diff --git a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt
index 2fc7d29..72c4b81 100644
--- a/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/NoOptions-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Error at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\):
+^CMake Error at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   No download info given for 'MyProj' and its source directory:
 
    .*/Tests/RunCMake/ExternalProject/NoOptions-build/MyProj-prefix/src/MyProj
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
index f16e479..44c6f74 100644
--- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -213,7 +213,7 @@
   file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-configure.cmake" [[message(FATAL_ERROR "once: configure should not run again")]])
   file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-build.cmake" [[message(FATAL_ERROR "once: build should not run again")]])
   file(WRITE "${RunCMake_TEST_BINARY_DIR}/once-install.cmake" [[message(FATAL_ERROR "once: install should not run again")]])
-  if(NOT RunCMake_GENERATOR MATCHES "^(Xcode|Visual Studio 9 )")
+  if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
     # The Xcode and VS 9 build systems decide to run this every time.
     file(WRITE "${RunCMake_TEST_BINARY_DIR}/always-configure.cmake" [[message(FATAL_ERROR "always: configure should not run again")]])
   endif()
@@ -247,9 +247,7 @@
   run_cmake_command(CONFIGURE_HANDLED_BY_BUILD-rebuild ${CMAKE_COMMAND} --build . ${BUILD_CONFIG})
 endfunction()
 
-if(NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 ")
-  __ep_test_CONFIGURE_HANDLED_BY_BUILD()
-endif()
+__ep_test_CONFIGURE_HANDLED_BY_BUILD()
 
 find_package(Git QUIET)
 if(GIT_EXECUTABLE)
diff --git a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt
index 07c6e87..8789dde 100644
--- a/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/SourceEmpty-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Error at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\):
+^CMake Error at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   No download info given for 'MyProj' and its source directory:
 
    .*/Tests/RunCMake/ExternalProject/SourceEmpty-build/SourceEmpty
diff --git a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt
index 373f6e3..9ff2681 100644
--- a/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/SourceMissing-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Error at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\):
+^CMake Error at .*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   No download info given for 'MyProj' and its source directory:
 
    .*/Tests/RunCMake/ExternalProject/SourceMissing-build/SourceMissing
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt
index 1231797..f0973ba 100644
--- a/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/TLSVersionBadArg-stderr.txt
@@ -1,9 +1,9 @@
 ^CMake Error at [^
-]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\):
+]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   TLS_VERSION 'bad-arg' not known
 Call Stack \(most recent call first\):
   [^
-]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_get_tls_version\)
+]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(_ep_get_tls_version\)
   [^
 ]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_add_download_command\)
   TLSVersionBadArg\.cmake:[0-9]+ \(ExternalProject_Add\)
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt
index 38b0fb8..4069159 100644
--- a/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/TLSVersionBadEnv-stderr.txt
@@ -1,9 +1,9 @@
 ^CMake Error at [^
-]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\):
+]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   ENV{CMAKE_TLS_VERSION} 'bad-env' not known
 Call Stack \(most recent call first\):
   [^
-]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_get_tls_version\)
+]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(_ep_get_tls_version\)
   [^
 ]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_add_download_command\)
   TLSVersionBadEnv\.cmake:[0-9]+ \(ExternalProject_Add\)
diff --git a/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt b/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt
index aaec60b..a5f7d64 100644
--- a/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt
+++ b/Tests/RunCMake/ExternalProject/TLSVersionBadVar-stderr.txt
@@ -1,9 +1,9 @@
 ^CMake Error at [^
-]*/Modules/ExternalProject\.cmake:[0-9]+ \(message\):
+]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(message\):
   CMAKE_TLS_VERSION 'bad-var' not known
 Call Stack \(most recent call first\):
   [^
-]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_get_tls_version\)
+]*/Modules/ExternalProject/shared_internal_commands\.cmake:[0-9]+ \(_ep_get_tls_version\)
   [^
 ]*/Modules/ExternalProject\.cmake:[0-9]+ \(_ep_add_download_command\)
   TLSVersionBadVar\.cmake:[0-9]+ \(ExternalProject_Add\)
diff --git a/Tests/RunCMake/FetchContent/CMakeLists.txt b/Tests/RunCMake/FetchContent/CMakeLists.txt
index 3cc2e43..eb0b40c 100644
--- a/Tests/RunCMake/FetchContent/CMakeLists.txt
+++ b/Tests/RunCMake/FetchContent/CMakeLists.txt
@@ -4,4 +4,10 @@
 # Tests assume no previous downloads in the output directory
 file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/_deps)
 
+if(CMP0168 STREQUAL "NEW")
+  cmake_policy(SET CMP0168 NEW)
+  string(REGEX REPLACE "-direct$" "" RunCMake_TEST "${RunCMake_TEST}")
+else()
+  cmake_policy(SET CMP0168 OLD)
+endif()
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/FetchContent/DownloadFile.cmake b/Tests/RunCMake/FetchContent/DownloadFile.cmake
index 741b6d3..e21ae28 100644
--- a/Tests/RunCMake/FetchContent/DownloadFile.cmake
+++ b/Tests/RunCMake/FetchContent/DownloadFile.cmake
@@ -1,8 +1,12 @@
 include(FetchContent)
 
+# The file hash depends on the line endings used by git
+file(MD5 ${CMAKE_CURRENT_LIST_DIR}/dummyFile.txt md5_hash)
+
 FetchContent_Declare(
   t1
   URL ${CMAKE_CURRENT_LIST_DIR}/dummyFile.txt
+  URL_HASH MD5=${md5_hash}
   DOWNLOAD_NO_EXTRACT YES
 )
 
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FetchContent/DownloadTwice-direct-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/FetchContent/DownloadTwice-direct-result.txt
diff --git a/Tests/RunCMake/FetchContent/DownloadTwice-direct-stderr.txt b/Tests/RunCMake/FetchContent/DownloadTwice-direct-stderr.txt
new file mode 100644
index 0000000..e793902
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/DownloadTwice-direct-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\):
+  Content t1 already populated in
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-result.txt
diff --git a/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-stderr.txt b/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-stderr.txt
new file mode 100644
index 0000000..9c3fc27
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/MakeAvailableUndeclared-direct-stderr.txt
@@ -0,0 +1,2 @@
+CMake Error at .*/Modules/FetchContent\.cmake:[0-9]+ \(message\):
+  No content details recorded for NoDetails
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-result.txt
diff --git a/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-stderr.txt b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-stderr.txt
new file mode 100644
index 0000000..7ecb06b
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing-direct-stderr.txt
@@ -0,0 +1,2 @@
+ *Manually specified source directory is missing:
++ *FETCHCONTENT_SOURCE_DIR_WITHPROJECT --> .*/ADirThatDoesNotExist
diff --git a/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake
index 0e24c1a..82e5c46 100644
--- a/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake
+++ b/Tests/RunCMake/FetchContent/ManualSourceDirectoryMissing.cmake
@@ -1,3 +1,4 @@
+message(STATUS "FETCHCONTENT_SOURCE_DIR_WITHPROJECT = ${FETCHCONTENT_SOURCE_DIR_WITHPROJECT}")
 include(FetchContent)
 
 FetchContent_Declare(
diff --git a/Tests/RunCMake/FetchContent/ManualSourceDirectoryRelative-direct-stderr.txt b/Tests/RunCMake/FetchContent/ManualSourceDirectoryRelative-direct-stderr.txt
new file mode 100644
index 0000000..3defcb4
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/ManualSourceDirectoryRelative-direct-stderr.txt
@@ -0,0 +1,3 @@
+ *Relative source directory specified.  This is not safe, as it depends on
+ *the calling directory scope.
++ *FETCHCONTENT_SOURCE_DIR_WITHPROJECT --> WithProject
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
index 0f443a7..72a458c 100644
--- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -2,81 +2,107 @@
 
 unset(RunCMake_TEST_NO_CLEAN)
 
-run_cmake(MissingDetails)
-run_cmake(DirectIgnoresDetails)
-run_cmake(FirstDetailsWin)
-run_cmake(DownloadTwice)
-run_cmake(DownloadFile)
-run_cmake(IgnoreToolchainFile)
-run_cmake(SameGenerator)
-run_cmake(System)
-run_cmake(VarDefinitions)
-run_cmake(VarPassthroughs)
-run_cmake(GetProperties)
-run_cmake(UsesTerminalOverride)
-run_cmake(MakeAvailable)
-run_cmake(MakeAvailableTwice)
-run_cmake(MakeAvailableUndeclared)
-run_cmake(VerifyHeaderSet)
+function(run_cmake_with_cmp0168 name)
+  run_cmake_with_options("${name}" -D CMP0168=OLD ${ARGN})
+  run_cmake_with_options("${name}-direct" -D CMP0168=NEW ${ARGN})
+endfunction()
 
-run_cmake_with_options(FindDependencyExport
+# Won't get to the part where CMP0168 matters
+run_cmake_with_options(MissingDetails -D CMP0168=NEW)
+
+# These are testing specific aspects of the sub-build
+run_cmake_with_options(SameGenerator -D CMP0168=OLD)
+run_cmake_with_options(VarPassthroughs -D CMP0168=OLD)
+
+run_cmake_with_cmp0168(DirectIgnoresDetails)
+run_cmake_with_cmp0168(FirstDetailsWin)
+run_cmake_with_cmp0168(DownloadTwice)
+run_cmake_with_cmp0168(DownloadFile)
+run_cmake_with_cmp0168(IgnoreToolchainFile)
+run_cmake_with_cmp0168(System)
+run_cmake_with_cmp0168(VarDefinitions)
+run_cmake_with_cmp0168(GetProperties)
+run_cmake_with_cmp0168(UsesTerminalOverride)
+run_cmake_with_cmp0168(MakeAvailable)
+run_cmake_with_cmp0168(MakeAvailableTwice)
+run_cmake_with_cmp0168(MakeAvailableUndeclared)
+run_cmake_with_cmp0168(VerifyHeaderSet)
+
+run_cmake_with_cmp0168(FindDependencyExport
   -D "CMAKE_PROJECT_TOP_LEVEL_INCLUDES=${CMAKE_CURRENT_LIST_DIR}/FindDependencyExportDP.cmake"
 )
 
-run_cmake_with_options(ManualSourceDirectory
+run_cmake_with_cmp0168(ManualSourceDirectory
   -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT=${CMAKE_CURRENT_LIST_DIR}/WithProject"
 )
-run_cmake_with_options(ManualSourceDirectoryMissing
+run_cmake_with_cmp0168(ManualSourceDirectoryMissing
   -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT=${CMAKE_CURRENT_LIST_DIR}/ADirThatDoesNotExist"
 )
 # Need to use :STRING to prevent CMake from automatically converting it to an
 # absolute path
-run_cmake_with_options(ManualSourceDirectoryRelative
+run_cmake_with_cmp0168(ManualSourceDirectoryRelative
   -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT:STRING=WithProject"
 )
 
-function(run_FetchContent_DirOverrides)
-  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides-build)
+function(run_FetchContent_DirOverrides cmp0168)
+  if(cmp0168 STREQUAL "NEW")
+    set(suffix "-direct")
+  else()
+    set(suffix "")
+  endif()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides${suffix}-build)
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(DirOverrides)
+  run_cmake_with_options(DirOverrides${suffix} -D CMP0168=${cmp0168})
 
   set(RunCMake_TEST_NO_CLEAN 1)
-  run_cmake_with_options(DirOverridesDisconnected
+  run_cmake_with_options(DirOverridesDisconnected${suffix}
+    -D CMP0168=${cmp0168}
     -D FETCHCONTENT_FULLY_DISCONNECTED=YES
   )
 endfunction()
-run_FetchContent_DirOverrides()
+run_FetchContent_DirOverrides(OLD)
+run_FetchContent_DirOverrides(NEW)
 
 set(RunCMake_TEST_OUTPUT_MERGE 1)
-run_cmake(PreserveEmptyArgs)
+run_cmake_with_cmp0168(PreserveEmptyArgs)
 set(RunCMake_TEST_OUTPUT_MERGE 0)
 
-# We need to pass through CMAKE_GENERATOR and CMAKE_MAKE_PROGRAM
-# to ensure the test can run on machines where the build tool
-# isn't on the PATH. Some build slaves explicitly test with such
-# an arrangement (e.g. to test with spaces in the path). We also
-# pass through the platform and toolset for completeness, even
-# though we don't build anything, just in case this somehow affects
-# the way the build tool is invoked.
-run_cmake_command(ScriptMode
-    ${CMAKE_COMMAND}
-    -DCMAKE_GENERATOR=${RunCMake_GENERATOR}
-    -DCMAKE_GENERATOR_PLATFORM=${RunCMake_GENERATOR_PLATFORM}
-    -DCMAKE_GENERATOR_TOOLSET=${RunCMake_GENERATOR_TOOLSET}
-    -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}
-    -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake
-)
-
 function(run_FetchContent_ExcludeFromAll)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build)
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
 
-  run_cmake(ExcludeFromAll)
+  # We're testing FetchContent_MakeAvailable()'s add_subdirectory() behavior,
+  # so it doesn't matter if we use OLD or NEW for CMP0168, but NEW is faster.
+  run_cmake(ExcludeFromAll -D CMP0168=NEW)
 
   set(RunCMake_TEST_NO_CLEAN 1)
   run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build .)
 endfunction()
 run_FetchContent_ExcludeFromAll()
+
+# Script mode testing requires more care for CMP0168 set to OLD.
+# We need to pass through CMAKE_GENERATOR and CMAKE_MAKE_PROGRAM
+# to ensure the test can run on machines where the build tool
+# isn't on the PATH. Some build machines explicitly test with such
+# an arrangement (e.g. to test with spaces in the path). We also
+# pass through the platform and toolset for completeness, even
+# though we don't build anything, just in case this somehow affects
+# the way the build tool is invoked.
+run_cmake_command(ScriptMode
+  ${CMAKE_COMMAND}
+  -DCMP0168=OLD
+  -DCMAKE_GENERATOR=${RunCMake_GENERATOR}
+  -DCMAKE_GENERATOR_PLATFORM=${RunCMake_GENERATOR_PLATFORM}
+  -DCMAKE_GENERATOR_TOOLSET=${RunCMake_GENERATOR_TOOLSET}
+  -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}
+  -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake
+)
+# CMP0168 NEW doesn't need a build tool or generator, so don't set them.
+run_cmake_command(ScriptMode-direct
+  ${CMAKE_COMMAND}
+  -DCMP0168=NEW
+  -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake
+)
diff --git a/Tests/RunCMake/FetchContent/ScriptMode.cmake b/Tests/RunCMake/FetchContent/ScriptMode.cmake
index 0a93d62..bf66140 100644
--- a/Tests/RunCMake/FetchContent/ScriptMode.cmake
+++ b/Tests/RunCMake/FetchContent/ScriptMode.cmake
@@ -1,3 +1,5 @@
+cmake_policy(SET CMP0168 ${CMP0168})
+
 include(FetchContent)
 
 file(WRITE tmpFile.txt "Generated contents, not important")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
index 2ad45ba..a061127 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/RunCMakeTest.cmake
@@ -35,3 +35,10 @@
 run_cmake(override-features3)
 run_cmake(override-features4)
 run_cmake(override-features5)
+
+# testing feature properties specification
+run_cmake(bad-feature-properties1)
+run_cmake(bad-feature-properties2)
+run_cmake(bad-feature-properties3)
+run_cmake(bad-feature-properties4)
+run_cmake(bad-feature-properties5)
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-stderr.txt
new file mode 100644
index 0000000..ac07251
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    BAD_PROPERTY=XXX
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1.cmake
new file mode 100644
index 0000000..e5790a8
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties1.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES BAD_PROPERTY=XXX)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-stderr.txt
new file mode 100644
index 0000000..ac07251
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    BAD_PROPERTY=XXX
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2.cmake
new file mode 100644
index 0000000..dea98d2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties2.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES LIBRARY_TYPE=STATIC BAD_PROPERTY=XXX UNICITY=YES)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-stderr.txt
new file mode 100644
index 0000000..29f5f66
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    LIBRARY_TYPE=STATIC,BAD_TYPE
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3.cmake
new file mode 100644
index 0000000..0a535db
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties3.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES LIBRARY_TYPE=STATIC,BAD_TYPE)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-stderr.txt
new file mode 100644
index 0000000..29f5f66
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    LIBRARY_TYPE=STATIC,BAD_TYPE
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4.cmake
new file mode 100644
index 0000000..c106653
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties4.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES UNICITY=YES LIBRARY_TYPE=STATIC,BAD_TYPE)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-result.txt
similarity index 100%
copy from Tests/RunCMake/CMP0069/CMP0069-NEW-generator-result.txt
copy to Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-result.txt
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-stderr.txt
new file mode 100644
index 0000000..3e57782
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error:
+  Erroneous option\(s\) for 'CMAKE_LINK_LIBRARY_feature_PROPERTIES':
+
+    UNICITY=YES,NO
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5.cmake
new file mode 100644
index 0000000..06efe7e
--- /dev/null
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/bad-feature-properties5.cmake
@@ -0,0 +1,10 @@
+enable_language(C)
+
+set(CMAKE_LINK_LIBRARY_USING_feature "<LIBRARY>")
+set(CMAKE_LINK_LIBRARY_USING_feature_SUPPORTED TRUE)
+set(CMAKE_LINK_LIBRARY_feature_PROPERTIES UNICITY=YES,NO)
+
+add_library(dep SHARED empty.c)
+
+add_library(lib SHARED empty.c)
+target_link_libraries(lib PRIVATE "$<LINK_LIBRARY:feature,dep>")
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
index f9a99af..8f43a7f 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored-stderr.txt
@@ -1,6 +1,6 @@
 CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
   The feature 'feat', specified as part of a generator-expression
-  '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE library
+  '\$<LINK_LIBRARY:feat>', will not be applied to the INTERFACE_LIBRARY
   'front'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
@@ -8,7 +8,14 @@
 
 CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
   The feature 'feat', specified as part of a generator-expression
-  '\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT library 'dep'.
+  '\$<LINK_LIBRARY:feat>', will not be applied to the OBJECT_LIBRARY 'dep'.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers.  Use -Wno-dev to suppress it.
+
+CMake Warning \(dev\) at library-ignored.cmake:[0-9]+ \(add_library\):
+  The feature 'feat', specified as part of a generator-expression
+  '\$<LINK_LIBRARY:feat>', will not be applied to the SHARED_LIBRARY 'lib'.
 Call Stack \(most recent call first\):
   CMakeLists.txt:[0-9]+ \(include\)
 This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
index a888bb8..675b87d 100644
--- a/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
+++ b/Tests/RunCMake/GenEx-LINK_LIBRARY/library-ignored.cmake
@@ -2,13 +2,14 @@
 
 set(CMAKE_C_LINK_LIBRARY_USING_feat_SUPPORTED TRUE)
 set(CMAKE_C_LINK_LIBRARY_USING_feat "<LIBRARY>")
+set(CMAKE_C_LINK_LIBRARY_feat_PROPERTIES "LIBRARY_TYPE=STATIC")
 
 add_library(dep OBJECT empty.c)
 
 add_library(lib SHARED empty.c)
 
 add_library(front INTERFACE)
-target_link_libraries(front INTERFACE lib)
+target_link_libraries(front INTERFACE "$<LINK_LIBRARY:feat,lib>")
 
 
 add_library(lib2 SHARED empty.c)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
index 07052eb..c278831 100644
--- a/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/RunCMakeTest.cmake
@@ -16,6 +16,7 @@
 run_cmake(TransitiveBuild)
 run_cmake(TransitiveLink-CMP0166-OLD)
 run_cmake(TransitiveLink-CMP0166-NEW)
+run_cmake(Unset)
 
 block()
   run_cmake(Scope)
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset-check.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset-check.cmake
new file mode 100644
index 0000000..59910f1
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset-check.cmake
@@ -0,0 +1,4 @@
+file(READ ${RunCMake_TEST_BINARY_DIR}/out.txt out)
+if(NOT out STREQUAL "'' ''")
+  set(RunCMake_TEST_FAILED "PROPERTY_THAT_IS_NOT_SET did not evaluate as empty:\n ${out}")
+endif()
diff --git a/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset.cmake b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset.cmake
new file mode 100644
index 0000000..dfe24a2
--- /dev/null
+++ b/Tests/RunCMake/GenEx-TARGET_PROPERTY/Unset.cmake
@@ -0,0 +1,3 @@
+add_library(iface INTERFACE)
+file(GENERATE OUTPUT out.txt CONTENT
+  "'$<TARGET_PROPERTY:iface,PROPERTY_THAT_IS_NOT_SET>' '$<TARGET_PROPERTY:PROPERTY_THAT_IS_NOT_SET>'" TARGET iface)
diff --git a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
index d8965f7..b1c758a 100644
--- a/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorPlatform/RunCMakeTest.cmake
@@ -3,7 +3,7 @@
 set(RunCMake_GENERATOR_PLATFORM "")
 run_cmake(NoPlatform)
 
-if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio ([89]|1[0124567])( 20[0-9][0-9])?$")
+if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio [0-9]+( 20[0-9][0-9])?$")
   set(RunCMake_GENERATOR_PLATFORM "x64")
   run_cmake(x64Platform)
 else()
@@ -17,7 +17,7 @@
 run_cmake(TwoPlatforms)
 unset(RunCMake_TEST_OPTIONS)
 
-if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio ([89]|1[0124567])( 20[0-9][0-9])?$")
+if("${RunCMake_GENERATOR}" MATCHES "^Visual Studio [0-9]+( 20[0-9][0-9])?$")
   set(RunCMake_TEST_OPTIONS -DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/TestPlatform-toolchain.cmake)
   run_cmake(TestPlatformToolchain)
   unset(RunCMake_TEST_OPTIONS)
diff --git a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
index 17f4c86..08cc274 100644
--- a/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GoogleTest/RunCMakeTest.cmake
@@ -362,10 +362,7 @@
   run_GoogleTestXML(${DISCOVERY_MODE})
   message("Testing ${DISCOVERY_MODE} discovery mode via DISCOVERY_MODE option...")
   run_GoogleTest_discovery_timeout(${DISCOVERY_MODE})
-  if(# VS 9 does not rebuild if POST_BUILD command changes.
-      NOT "${DISCOVERY_MODE};${RunCMake_GENERATOR}" MATCHES "^POST_BUILD;Visual Studio 9")
-    run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE})
-  endif()
+  run_GoogleTest_discovery_arg_change(${DISCOVERY_MODE})
   run_GoogleTest_discovery_test_list(${DISCOVERY_MODE})
   run_GoogleTest_discovery_test_list_scoped(${DISCOVERY_MODE})
   run_GoogleTest_discovery_flush_script(${DISCOVERY_MODE})
diff --git a/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
index 0a3f01c..c57e727 100644
--- a/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
+++ b/Tests/RunCMake/LinkerSelection/RunCMakeTest.cmake
@@ -1,10 +1,5 @@
 include(RunCMake)
 
-if (RunCMake_GENERATOR MATCHES "Visual Studio 9 2008")
-  run_cmake(UnsupportedLinkerType)
-  return()
-endif()
-
 run_cmake(InvalidLinkerType1)
 run_cmake(InvalidLinkerType2)
 
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt
deleted file mode 100644
index 6473451..0000000
--- a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType-stderr.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-CMake Error at UnsupportedLinkerType.cmake:[0-9]+ \(add_executable\):
-  'LINKER_TYPE' property, specified on target 'main', is not supported by
-  this generator.
diff --git a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake b/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake
deleted file mode 100644
index 1b0703c..0000000
--- a/Tests/RunCMake/LinkerSelection/UnsupportedLinkerType.cmake
+++ /dev/null
@@ -1,5 +0,0 @@
-
-enable_language(C)
-
-set(CMAKE_LINKER_TYPE LDD)
-add_executable(main main.c)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
index 9514c97..d50d403 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/RunCMakeTest.cmake
@@ -26,8 +26,7 @@
 if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows|AIX|SunOS)$|BSD"
     AND NOT CMAKE_C_COMPILER_ID MATCHES "^(Borland|Embarcadero|OpenWatcom|OrangeC|Watcom)$"
     AND NOT (CMAKE_C_COMPILER_ID MATCHES "^(Intel|IntelLLVM)$" AND CMAKE_SYSTEM_NAME STREQUAL "Windows")
-    AND NOT CMAKE_C_COMPILER_LINKER MATCHES "Visual Studio 9\\.0"
-    AND NOT RunCMake_GENERATOR MATCHES "Visual Studio 9 "
+    AND NOT CMAKE_C_COMPILER_LINKER MATCHES "Microsoft Visual Studio 9\\.0/VC/bin"
     )
   if(NOT CMAKE_C_COMPILER_LINKER OR NOT CMAKE_C_COMPILER_LINKER_ID OR NOT CMAKE_C_COMPILER_LINKER_VERSION)
     message(SEND_ERROR "C compiler's linker not identified:\n"
diff --git a/Tests/RunCMake/Swift/EnableSwift.cmake b/Tests/RunCMake/Swift/EnableSwift.cmake
new file mode 100644
index 0000000..19f297a
--- /dev/null
+++ b/Tests/RunCMake/Swift/EnableSwift.cmake
@@ -0,0 +1 @@
+enable_language(Swift)
diff --git a/Tests/RunCMake/Swift/RunCMakeTest.cmake b/Tests/RunCMake/Swift/RunCMakeTest.cmake
index 3711efb..e8a1dab 100644
--- a/Tests/RunCMake/Swift/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Swift/RunCMakeTest.cmake
@@ -70,6 +70,20 @@
     endblock()
 
     block()
+      # Try enabling Swift with a static-library try-compile
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/StaticLibTryCompile-build)
+      set(RunCMake_TEST_OPTIONS -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY)
+      run_cmake(EnableSwift)
+    endblock()
+
+    block()
+      # Try enabling Swift with an executable try-compile
+      set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExecutableTryCompile-build)
+      set(RunCMake_TEST_OPTIONS -DCMAKE_TRY_COMPILE_TARGET_TYPE=EXECUTABLE)
+      run_cmake(EnableSwift)
+    endblock()
+
+    block()
       set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ForceResponseFile-build)
       run_cmake(ForceResponseFile)
       set(RunCMake_TEST_NO_CLEAN 1)
diff --git a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake
index 134821d..8737c9a 100644
--- a/Tests/RunCMake/VSSolution/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VSSolution/RunCMakeTest.cmake
@@ -13,9 +13,7 @@
 run_cmake(StartupProjectMissing)
 run_cmake(AddPackageToDefault)
 
-if(NOT NO_USE_FOLDERS)
-  run_cmake(StartupProjectUseFolders)
-  run_cmake(CMP0143-WARN)
-  run_cmake_with_options(CMP0143-OLD "-DCMAKE_POLICY_DEFAULT_CMP0143=OLD")
-  run_cmake_with_options(CMP0143-NEW "-DCMAKE_POLICY_DEFAULT_CMP0143=NEW")
-endif()
+run_cmake(StartupProjectUseFolders)
+run_cmake(CMP0143-WARN)
+run_cmake_with_options(CMP0143-OLD "-DCMAKE_POLICY_DEFAULT_CMP0143=OLD")
+run_cmake_with_options(CMP0143-NEW "-DCMAKE_POLICY_DEFAULT_CMP0143=NEW")
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt
index 547fb1c..5741ae5 100644
--- a/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt
+++ b/Tests/RunCMake/add_custom_command/BadByproduct-stderr.txt
@@ -1,9 +1,3 @@
-CMake Error at BadByproduct.cmake:2 \(add_custom_command\):
-  BYPRODUCTS containing a "#" is not allowed.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
-
-
 CMake Error at BadByproduct.cmake:3 \(add_custom_command\):
   BYPRODUCTS containing a "<" is not allowed.
 Call Stack \(most recent call first\):
@@ -17,7 +11,7 @@
 
 (
 CMake Error at BadByproduct.cmake:5 \(add_custom_command\):
-  BYPRODUCTS containing a "#" is not allowed.
+  BYPRODUCTS containing a ">" is not allowed.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 
diff --git a/Tests/RunCMake/add_custom_command/BadByproduct.cmake b/Tests/RunCMake/add_custom_command/BadByproduct.cmake
index cf00f5b..4fe0b82 100644
--- a/Tests/RunCMake/add_custom_command/BadByproduct.cmake
+++ b/Tests/RunCMake/add_custom_command/BadByproduct.cmake
@@ -1,8 +1,8 @@
 set(CMAKE_DISABLE_SOURCE_CHANGES ON)
-add_custom_command(OUTPUT a BYPRODUCTS "a#")
+
 add_custom_command(OUTPUT b BYPRODUCTS "a<")
 add_custom_command(OUTPUT c BYPRODUCTS "a>")
-add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/#")
+add_custom_command(OUTPUT d BYPRODUCTS "$<CONFIG>/$<ANGLE-R>")
 add_custom_command(OUTPUT e BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/f)
 add_custom_command(OUTPUT f BYPRODUCTS "$<TARGET_PROPERTY:prop>")
 add_custom_command(OUTPUT h BYPRODUCTS "$<OUTPUT_CONFIG:h>")
diff --git a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt
index 2e43568..9d25949 100644
--- a/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt
+++ b/Tests/RunCMake/add_custom_command/BadOutput-stderr.txt
@@ -1,9 +1,3 @@
-CMake Error at BadOutput.cmake:2 \(add_custom_command\):
-  OUTPUT containing a "#" is not allowed.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
-
-
 CMake Error at BadOutput.cmake:3 \(add_custom_command\):
   OUTPUT containing a "<" is not allowed.
 Call Stack \(most recent call first\):
@@ -17,7 +11,7 @@
 
 (
 CMake Error at BadOutput.cmake:5 \(add_custom_command\):
-  OUTPUT containing a "#" is not allowed.
+  OUTPUT containing a ">" is not allowed.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 
diff --git a/Tests/RunCMake/add_custom_command/BadOutput.cmake b/Tests/RunCMake/add_custom_command/BadOutput.cmake
index f04bdd1..0f6bd07 100644
--- a/Tests/RunCMake/add_custom_command/BadOutput.cmake
+++ b/Tests/RunCMake/add_custom_command/BadOutput.cmake
@@ -1,8 +1,8 @@
 set(CMAKE_DISABLE_SOURCE_CHANGES ON)
-add_custom_command(OUTPUT "a#" COMMAND a)
+
 add_custom_command(OUTPUT "a<" COMMAND b)
 add_custom_command(OUTPUT "a>" COMMAND c)
-add_custom_command(OUTPUT "$<CONFIG>/#" COMMAND d)
+add_custom_command(OUTPUT "$<CONFIG>/$<ANGLE-R>" COMMAND d)
 add_custom_command(OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/e COMMAND f)
 add_custom_command(OUTPUT "$<TARGET_PROPERTY:prop>" COMMAND g)
 add_custom_command(OUTPUT "$<OUTPUT_CONFIG:h>" COMMAND h)
diff --git a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
index 713b269..46e7bae 100644
--- a/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
+++ b/Tests/RunCMake/add_custom_command/RunCMakeTest.cmake
@@ -19,7 +19,7 @@
 run_cmake(TargetLiteralQuotes)
 run_cmake(TargetNotInDir)
 
-if(${RunCMake_GENERATOR} MATCHES "Visual Studio ([^89]|[89][0-9])")
+if(RunCMake_GENERATOR MATCHES "Visual Studio")
   run_cmake(RemoveEmptyCommands)
 endif()
 
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt
index da9af49..b734ddd 100644
--- a/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt
+++ b/Tests/RunCMake/add_custom_target/BadByproduct-stderr.txt
@@ -1,9 +1,3 @@
-CMake Error at BadByproduct.cmake:2 \(add_custom_target\):
-  BYPRODUCTS containing a "#" is not allowed.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
-
-
 CMake Error at BadByproduct.cmake:3 \(add_custom_target\):
   BYPRODUCTS containing a "<" is not allowed.
 Call Stack \(most recent call first\):
@@ -17,7 +11,7 @@
 
 (
 CMake Error at BadByproduct.cmake:5 \(add_custom_target\):
-  BYPRODUCTS containing a "#" is not allowed.
+  BYPRODUCTS containing a ">" is not allowed.
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
 
diff --git a/Tests/RunCMake/add_custom_target/BadByproduct.cmake b/Tests/RunCMake/add_custom_target/BadByproduct.cmake
index c317b83..d29c02f 100644
--- a/Tests/RunCMake/add_custom_target/BadByproduct.cmake
+++ b/Tests/RunCMake/add_custom_target/BadByproduct.cmake
@@ -1,8 +1,8 @@
 set(CMAKE_DISABLE_SOURCE_CHANGES ON)
-add_custom_target(a BYPRODUCTS "a#" COMMAND b)
+
 add_custom_target(c BYPRODUCTS "a<" COMMAND d)
 add_custom_target(e BYPRODUCTS "a>" COMMAND f)
-add_custom_target(g BYPRODUCTS "$<CONFIG>/#" COMMAND h)
+add_custom_target(g BYPRODUCTS "$<CONFIG>/$<ANGLE-R>" COMMAND h)
 add_custom_target(i BYPRODUCTS ${CMAKE_CURRENT_SOURCE_DIR}/j COMMAND k)
 add_custom_target(l BYPRODUCTS "$<TARGET_PROPERTY:prop>" COMMAND m)
 add_custom_target(n BYPRODUCTS "$<OUTPUT_CONFIG:n>" COMMAND o)
diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
index 0b3576d..76dff4f 100644
--- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
@@ -16,7 +16,7 @@
 
 run_cmake(UserFallbackScript)
 
-if(RunCMake_GENERATOR MATCHES "^Visual Studio " AND NOT RunCMake_GENERATOR STREQUAL "Visual Studio 9 2008")
+if(RunCMake_GENERATOR MATCHES "Visual Studio")
   run_cmake(VsMSBuild)
 else()
   run_cmake(VsMSBuildMissing)
diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt
index 9627872..b1b0642 100644
--- a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt
+++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt
@@ -1,4 +1,4 @@
 CMake Error at .*AnyCommandAbnormalExit.cmake:[0-9]+ \(execute_process\):
   execute_process failed command indexes:
 
-    1: "Abnormal exit with child return code: Segmentation fault
+    1: "Abnormal exit with child return code:
diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake
index 1017e0f..ef63778 100644
--- a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake
+++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake
@@ -1,5 +1,4 @@
-execute_process(COMMAND "${Python_EXECUTABLE}" -c
-    "import os; os.kill(os.getpid(),11)"
+execute_process(COMMAND "${EXIT_CRASH_EXE}"
   COMMAND ${CMAKE_COMMAND} -E true
   COMMAND_ERROR_IS_FATAL ANY
   )
diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake
index e4a125d..1786d53 100644
--- a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake
+++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake
@@ -1,12 +1,10 @@
-execute_process(COMMAND "${Python_EXECUTABLE}" -c
-    "import os; os.kill(os.getpid(),11)"
+execute_process(COMMAND "${EXIT_CRASH_EXE}"
   COMMAND ${CMAKE_COMMAND} -E true
   RESULT_VARIABLE result
   )
 
 if(result EQUAL "0")
-  execute_process(COMMAND "${Python_EXECUTABLE}" -c
-      "import os; os.kill(os.getpid(),11)"
+  execute_process(COMMAND "${EXIT_CRASH_EXE}"
     COMMAND ${CMAKE_COMMAND} -E true
     COMMAND_ERROR_IS_FATAL LAST
     )
diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt
index c915e58..6db023b 100644
--- a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt
+++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt
@@ -1,2 +1,2 @@
 CMake Error at .*LastCommandAbnormalExit-2.cmake:[0-9]+ \(execute_process\):
-  execute_process Abnormal exit: Segmentation fault
+  execute_process Abnormal exit:
diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake
index 6c3fbf8..a0c3aaf 100644
--- a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake
+++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake
@@ -1,13 +1,11 @@
 execute_process(COMMAND ${CMAKE_COMMAND} -E true
-  COMMAND "${Python_EXECUTABLE}" -c
-    "import os; os.kill(os.getpid(),11)"
+  COMMAND "${EXIT_CRASH_EXE}"
   RESULT_VARIABLE result
   )
 
 if(NOT result EQUAL "0")
   execute_process(COMMAND ${CMAKE_COMMAND} -E true
-    COMMAND "${Python_EXECUTABLE}" -c
-      "import os; os.kill(os.getpid(),11)"
+    COMMAND "${EXIT_CRASH_EXE}"
     COMMAND_ERROR_IS_FATAL LAST
     )
 endif()
diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
index a3a3a9a..f77391a 100644
--- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake
+++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
@@ -41,10 +41,10 @@
 run_cmake_command(StdoutStderrNoexist ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/StderrNoexist.cmake)
 run_cmake_command(InOutErrDirectory ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/InOutErrDirectory.cmake)
 
-if(UNIX AND Python_EXECUTABLE)
-  run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake)
-  run_cmake_command(LastCommandAbnormalExit-1 ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-1.cmake)
-  run_cmake_command(LastCommandAbnormalExit-2 ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-2.cmake)
+if(EXIT_CRASH_EXE)
+  run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake)
+  run_cmake_command(LastCommandAbnormalExit-1 ${CMAKE_COMMAND} -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-1.cmake)
+  run_cmake_command(LastCommandAbnormalExit-2 ${CMAKE_COMMAND} -DEXIT_CRASH_EXE=${EXIT_CRASH_EXE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-2.cmake)
 endif()
 
 if(WIN32 OR CYGWIN)
diff --git a/Tests/RunCMake/exit_crash.c b/Tests/RunCMake/exit_crash.c
new file mode 100644
index 0000000..324ffad
--- /dev/null
+++ b/Tests/RunCMake/exit_crash.c
@@ -0,0 +1,14 @@
+int main(int argc, const char* argv[])
+{
+#ifndef __clang_analyzer__ /* Suppress clang-analyzer warnings */
+  /* Construct an invalid address that cannot be predicted by the
+     compiler/optimizer, and that is not NULL (which is undefined
+     behavior to dereference).  */
+  volatile int* invalidAddress = 0;
+  invalidAddress += argc ? 1 : 2;
+  (void)argv;
+  /* Write to the invalid address to cause SIGSEGV or similar.  */
+  *invalidAddress = 0;
+#endif
+  return 0;
+}
diff --git a/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake b/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake
index 33f519f..5e0310b 100644
--- a/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake
+++ b/Tests/RunCMake/file-DOWNLOAD/RunCMakeTest.cmake
@@ -27,9 +27,11 @@
 endif()
 
 run_cmake_with_options(TLS_VERSION-bad)
+if(CMake_TEST_TLS_VERIFY_URL_BAD)
+  run_cmake_with_options(TLS_VERIFY-bad -Durl=${CMake_TEST_TLS_VERIFY_URL_BAD})
+endif()
 
 if(CMake_TEST_TLS_VERIFY_URL)
-  run_cmake(TLS_VERIFY-bad)
   run_cmake_with_options(TLS_VERIFY-good -Durl=${CMake_TEST_TLS_VERIFY_URL})
   if(CMake_TEST_TLS_VERSION)
     run_cmake_with_options(TLS_VERSION-good -Durl=${CMake_TEST_TLS_VERIFY_URL} -Dtls_version=${CMake_TEST_TLS_VERSION})
diff --git a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake
index a90c2f4..7d50ece 100644
--- a/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake
+++ b/Tests/RunCMake/file-DOWNLOAD/TLS_VERIFY-bad.cmake
@@ -1,5 +1,6 @@
 function(download case)
-  file(DOWNLOAD https://expired.badssl.com ${ARGN} STATUS status LOG log)
+  # URL with semantics like https://expired.badssl.com is provided by caller
+  file(DOWNLOAD ${url} ${ARGN} STATUS status LOG log)
   message(STATUS "${case}: ${status}")
   if(case MATCHES "1$" AND NOT status MATCHES "^(35|60);")
     message("${log}")
diff --git a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
index 4fbf147..11be360 100644
--- a/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/include_external_msproject/RunCMakeTest.cmake
@@ -6,12 +6,13 @@
 run_cmake(CustomGuidTypePlatform)
 run_cmake(CustomConfig)
 
-if(RunCMake_GENERATOR MATCHES "Visual Studio ([^9]|9[0-9])")
+if(RunCMake_GENERATOR MATCHES "Visual Studio")
   run_cmake(SkipGetTargetFrameworkProperties)
   run_cmake(VSCSharpReference)
 endif()
 
-if(RunCMake_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])")
+if(RunCMake_GENERATOR MATCHES "^Visual Studio (1[6-9]|[2-9][0-9])"
+    AND NOT RunCMake_GENERATOR_TOOLSET MATCHES "^(v80|v90|v100|v110|v120)$")
   function(run_VSCSharpOnlyProject)
     set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/VSCSharpOnlyProject-build)
     run_cmake(VSCSharpOnlyProject)
diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake
index 0439b42..7b0aa85 100644
--- a/Tests/RunCMake/install/RunCMakeTest.cmake
+++ b/Tests/RunCMake/install/RunCMakeTest.cmake
@@ -8,7 +8,9 @@
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
   run_cmake(${case})
+  set(RunCMake_TEST_OUTPUT_MERGE 1)
   run_cmake_command(${case}-build ${CMAKE_COMMAND} --build . --config Debug)
+  unset(RunCMake_TEST_OUTPUT_MERGE)
   # Check "all" components.
   set(CMAKE_INSTALL_PREFIX ${RunCMake_TEST_BINARY_DIR}/root-all)
   run_cmake_command(${case}-all ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX} -DBUILD_TYPE=Debug -P cmake_install.cmake)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
index 88a7e63..97a96b4 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -135,4 +135,5 @@
     OR CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|visionOS|watchOS|Linux|BSD|MSYS|CYGWIN")
   run_cmake(feature-WHOLE_ARCHIVE)
   run_cmake_target(feature-WHOLE_ARCHIVE link-exe main)
+  run_cmake_target(feature-WHOLE_ARCHIVE circular-exe main_circular)
 endif()
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c
new file mode 100644
index 0000000..80ee413
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular1.c
@@ -0,0 +1,6 @@
+void circular2(void);
+
+void circular1(void)
+{
+  circular2();
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c
new file mode 100644
index 0000000..751bab5
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/circular2.c
@@ -0,0 +1,7 @@
+
+void circular1(void);
+
+void circular2(void)
+{
+  circular1();
+}
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt
new file mode 100644
index 0000000..fb4871c
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE-circular-exe-stderr-darwin.txt
@@ -0,0 +1 @@
+(ld: warning: ignoring duplicate libraries:)?
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
index e525325..5c599c9 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/feature-WHOLE_ARCHIVE.cmake
@@ -9,3 +9,13 @@
 
 add_executable(main main.c)
 target_link_libraries(main PRIVATE lib)
+
+
+add_library(circular1 STATIC circular1.c)
+add_library(circular2 STATIC circular2.c)
+
+target_link_libraries(circular1 PRIVATE circular2)
+target_link_libraries(circular2 PRIVATE circular1)
+
+add_executable(main_circular main_circular.c)
+target_link_libraries(main_circular PRIVATE $<LINK_LIBRARY:WHOLE_ARCHIVE,circular1>)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c
new file mode 100644
index 0000000..16ebee6
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/main_circular.c
@@ -0,0 +1,9 @@
+
+void circular1(void);
+
+int main(void)
+{
+  circular1();
+
+  return 0;
+}
diff --git a/bootstrap b/bootstrap
index 60e5638..3abeec6 100755
--- a/bootstrap
+++ b/bootstrap
@@ -684,8 +684,8 @@
                           (default)
   --system-cppdap         use system-installed cppdap library
   --no-system-cppdap      use cmake-provided cppdap library (default)
-  --system-curl           use system-installed curl library
-  --no-system-curl        use cmake-provided curl library (default)
+  --system-curl           use system-installed curl library (default on macOS)
+  --no-system-curl        use cmake-provided curl library (default elsewhere)
   --system-expat          use system-installed expat library
   --no-system-expat       use cmake-provided expat library (default)
   --system-jsoncpp        use system-installed jsoncpp library