Merge topic 'compile-commands-posix-paths'

971a8ded06 EXPORT_COMPILE_COMMANDS: Write absolute posix paths to compile_commands.json
ff119423b9 EXPORT_COMPILE_COMMANDS: Append missing newline to compile_commands.json
92fb080ed6 Tests/RunCMake/Swift: Fix CompileCommands expected regex
7d8b39226c cmGlobalNinjaGenerator: Remove obsolete TODO comment

Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Ben Boeckel <ben.boeckel@kitware.com>
Merge-request: !10357
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 82d0968..c8c5bfd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -830,6 +830,46 @@
     needs:
         - b:linux-aarch64-package
 
+b:sunos-i386-package:
+    extends:
+        - .sunos_package
+        - .sunos_release_i386
+        - .cmake_build_sunos_release
+        - .cmake_release_artifacts
+        - .linux_x86_64_tags
+        - .run_only_for_package
+    needs:
+        - p:doc-package
+    variables:
+        CMAKE_CI_ARTIFACTS_NAME: "artifacts-sunos-i386"
+
+u:sunos-i386-package:
+    extends:
+        - .rsync_upload_package
+        - .run_only_for_package
+    needs:
+        - b:sunos-i386-package
+
+b:sunos-sparc-package:
+    extends:
+        - .sunos_package
+        - .sunos_release_sparc
+        - .cmake_build_sunos_release
+        - .cmake_release_artifacts
+        - .linux_x86_64_tags
+        - .run_only_for_package
+    needs:
+        - p:doc-package
+    variables:
+        CMAKE_CI_ARTIFACTS_NAME: "artifacts-sunos-sparc"
+
+u:sunos-sparc-package:
+    extends:
+        - .rsync_upload_package
+        - .run_only_for_package
+    needs:
+        - b:sunos-sparc-package
+
 ## Sanitizer builds
 
 b:fedora41-asan:
diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml
index 95a0a26..0750634 100644
--- a/.gitlab/artifacts.yml
+++ b/.gitlab/artifacts.yml
@@ -78,6 +78,8 @@
             - ${CMAKE_CI_BUILD_DIR}/cmake-*-linux-x86_64.*
             - ${CMAKE_CI_BUILD_DIR}/cmake-*-linux-aarch64.*
             - ${CMAKE_CI_BUILD_DIR}/cmake-*-macos*-universal.*
+            - ${CMAKE_CI_BUILD_DIR}/cmake-*-sunos-i386.*
+            - ${CMAKE_CI_BUILD_DIR}/cmake-*-sunos-sparc.*
             - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-x86_64.*
             - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-i386.*
             - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-arm64.*
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 461b34e..93223bb 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -30,6 +30,24 @@
     variables:
         BOOTSTRAP_ARGS: '-- "-DCMake_DOC_ARTIFACT_PREFIX=$CI_PROJECT_DIR/build/install-doc"'
 
+.sunos_release_i386:
+    image: "kitware/cmake:build-sunos-i386-deps-2025-02-14"
+
+    variables:
+        GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
+        CMAKE_ARCH: i386
+
+.sunos_release_sparc:
+    image: "kitware/cmake:build-sunos-sparc-deps-2025-02-14"
+
+    variables:
+        GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
+        CMAKE_ARCH: sparc
+
+.sunos_package:
+    variables:
+        CMake_DOC_ARTIFACT_PREFIX: "$CI_PROJECT_DIR/build/install-doc"
+
 .needs_centos7_x86_64:
     needs:
         - b:centos7-x86_64
@@ -741,6 +759,33 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
+.cmake_build_sunos_release:
+    stage: build
+
+    script:
+        - *before_script_linux
+        # SunOS sysroot
+        - Utilities/Release/sunos/docker/sysroot.bash $CMAKE_ARCH
+        # Initial cache
+        - mkdir -p build/
+        - cp Utilities/Release/sunos/$CMAKE_ARCH/cache.txt build/CMakeCache.txt
+        # Make sccache available.
+        - source .gitlab/ci/sccache-env.sh
+        - echo "CMAKE_C_COMPILER_LAUNCHER:STRING=sccache" >> build/CMakeCache.txt
+        - echo "CMAKE_CXX_COMPILER_LAUNCHER:STRING=sccache" >> build/CMakeCache.txt
+        # Build
+        - cd build/
+        - cmake .. -GNinja
+          -DCMAKE_DOC_DIR=doc/cmake
+          -DCMake_DOC_ARTIFACT_PREFIX="$CMake_DOC_ARTIFACT_PREFIX"
+          -DCMAKE_TOOLCHAIN_FILE="$CI_PROJECT_DIR/Utilities/Release/sunos/$CMAKE_ARCH/toolchain.cmake"
+        - ninja
+        # Package
+        - cpack -G "TGZ;STGZ"
+        - sccache --show-stats
+
+    interruptible: true
+
 ### Documentation
 
 .cmake_org_help:
diff --git a/Help/command/cmake_minimum_required.rst b/Help/command/cmake_minimum_required.rst
index 47e8e6b..333e514 100644
--- a/Help/command/cmake_minimum_required.rst
+++ b/Help/command/cmake_minimum_required.rst
@@ -8,7 +8,7 @@
   cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])
 
 .. versionadded:: 3.12
-  The optional ``<policy_max>`` version.
+  The optional ``<policy_max>`` version behavior; ignored in older CMake.
 
 Sets the minimum required version of cmake for a project.
 Also updates the policy settings as explained below.
diff --git a/Help/command/string.rst b/Help/command/string.rst
index c510ff4..d86efc5 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -122,6 +122,12 @@
   string instead of the beginning of each repeated search.
   See policy :policy:`CMP0186`.
 
+  Zero-length matches are allowed in ``MATCHALL`` and ``REPLACE``.
+  Previously, they triggered an error.
+
+  The replacement expression may contain references to subexpressions that
+  didn't match anything. Previously, such references triggered an error.
+
 .. _`Regex Specification`:
 
 Regex Specification
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index 5f2cb22..907b31f 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -129,7 +129,13 @@
 * variable ``CMAKE_EXPERIMENTAL_INSTRUMENTATION`` to
 * value ``a37d1069-1972-4901-b9c9-f194aaf2b6e0``.
 
-To enable instrumentation at the user-level, files should be blaced under
+To enable instrumentation at the user-level, files should be placed under
 either
 ``<CMAKE_CONFIG_DIR>/instrumentation-a37d1069-1972-4901-b9c9-f194aaf2b6e0`` or
 ``<CMAKE_BINARY_DIR>/.cmake/instrumentation-a37d1069-1972-4901-b9c9-f194aaf2b6e0``.
+
+To include instrumentation data in CTest XML files (for submission to CDash),
+you need to set the following environment variables:
+
+* ``CTEST_USE_INSTRUMENTATION=1``
+* ``CTEST_EXPERIMENTAL_INSTRUMENTATION=a37d1069-1972-4901-b9c9-f194aaf2b6e0``
diff --git a/Help/envvar/CTEST_USE_INSTRUMENTATION.rst b/Help/envvar/CTEST_USE_INSTRUMENTATION.rst
new file mode 100644
index 0000000..c6c7f70
--- /dev/null
+++ b/Help/envvar/CTEST_USE_INSTRUMENTATION.rst
@@ -0,0 +1,15 @@
+CTEST_USE_INSTRUMENTATION
+-------------------------
+
+.. versionadded:: 4.0
+
+.. include:: ENV_VAR.txt
+
+.. note::
+
+   This feature is only available when experimental support for instrumentation
+   has been enabled by the ``CMAKE_EXPERIMENTAL_INSTRUMENTATION`` gate.
+
+Setting this environment variable enables
+:manual:`instrumentation <cmake-instrumentation(7)>` for CTest in
+:ref:`Dashboard Client` mode.
diff --git a/Help/envvar/CTEST_USE_VERBOSE_INSTRUMENTATION.rst b/Help/envvar/CTEST_USE_VERBOSE_INSTRUMENTATION.rst
new file mode 100644
index 0000000..d7f7477
--- /dev/null
+++ b/Help/envvar/CTEST_USE_VERBOSE_INSTRUMENTATION.rst
@@ -0,0 +1,17 @@
+CTEST_USE_VERBOSE_INSTRUMENTATION
+---------------------------------
+
+.. versionadded:: 4.0
+
+.. include:: ENV_VAR.txt
+
+.. note::
+
+   This feature is only available when experimental support for instrumentation
+   has been enabled by the ``CMAKE_EXPERIMENTAL_INSTRUMENTATION`` gate.
+
+Setting this environment variable causes CTest to report the full
+command line (including arguments) to CDash for each instrumented command.
+By default, CTest truncates the command line at the first space.
+
+See also :envvar:`CTEST_USE_INSTRUMENTATION`
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst
index fe3e703..140fc83 100644
--- a/Help/manual/cmake-env-variables.7.rst
+++ b/Help/manual/cmake-env-variables.7.rst
@@ -116,7 +116,9 @@
    /envvar/CTEST_OUTPUT_ON_FAILURE
    /envvar/CTEST_PARALLEL_LEVEL
    /envvar/CTEST_PROGRESS_OUTPUT
+   /envvar/CTEST_USE_INSTRUMENTATION
    /envvar/CTEST_USE_LAUNCHERS_DEFAULT
+   /envvar/CTEST_USE_VERBOSE_INSTRUMENTATION
    /envvar/DASHBOARD_TEST_FROM_CTEST
 
 Environment Variables for the CMake curses interface
diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst
index 7b2d1e6..d36bf62 100644
--- a/Help/manual/cmake-instrumentation.7.rst
+++ b/Help/manual/cmake-instrumentation.7.rst
@@ -53,7 +53,7 @@
 than the previous indexing.
 
 Indexing and can also be performed by manually invoking
-``ctest --collect-instrumentation``.
+``ctest --collect-instrumentation <build>``.
 
 Callbacks
 ---------
@@ -94,6 +94,37 @@
 the :envvar:`CMAKE_CONFIG_DIR` under
 ``<config_dir>/instrumentation/<version>/query/``.
 
+Enabling Instrumentation for CDash Submissions
+----------------------------------------------
+
+You can enable instrumentation when using CTest in :ref:`Dashboard Client`
+mode by setting the :envvar:`CTEST_USE_INSTRUMENTATION` environment variable
+to the current UUID for the ``CMAKE_EXPERIMENTAL_INSTRUMENTATION`` feature.
+Doing so automatically enables the ``dynamicSystemInformation`` query.
+
+The following table shows how each type of instrumented command gets mapped
+to a corresponding type of CTest XML file.
+
+=================================================== ==================
+:ref:`Snippet Role <cmake-instrumentation Data v1>` CTest XML File
+=================================================== ==================
+``configure``                                       ``Configure.xml``
+``generate``                                        ``Configure.xml``
+``compile``                                         ``Build.xml``
+``link``                                            ``Build.xml``
+``custom``                                          ``Build.xml``
+``build``                                           unused!
+``cmakeBuild``                                      ``Build.xml``
+``cmakeInstall``                                    ``Build.xml``
+``install``                                         ``Build.xml``
+``ctest``                                           ``Build.xml``
+``test``                                            ``Test.xml``
+=================================================== ==================
+
+By default the command line reported to CDash is truncated at the first space.
+You can instead choose to report the full command line (including arguments)
+by setting :envvar:`CTEST_USE_VERBOSE_INSTRUMENTATION` to 1.
+
 .. _`cmake-instrumentation API v1`:
 
 API v1
@@ -123,6 +154,10 @@
   files, they should never be removed by other processes. Data collected here
   remains until after `Indexing`_ occurs and all `Callbacks`_ are executed.
 
+``cdash/``
+  Holds temporary files used internally to generate XML content to be submitted
+  to CDash.
+
 .. _`cmake-instrumentation v1 Query Files`:
 
 v1 Query Files
@@ -270,7 +305,7 @@
 
   ``target``
     The CMake target associated with the command. Only included when ``role`` is
-    one of ``compile``, ``link``, ``custom``.
+    ``compile`` or ``link``.
 
   ``targetType``
     The :prop_tgt:`TYPE` of the target. Only included when ``role`` is
@@ -375,7 +410,7 @@
   The name of the hook responsible for generating the index file. In addition
   to the hooks that can be specified by one of the `v1 Query Files`_, this value may
   be set to ``manual`` if indexing is performed by invoking
-  ``ctest --collect-instrumentation``.
+  ``ctest --collect-instrumentation <build>``.
 
 ``snippets``
   Contains a list of `v1 Snippet File`_. This includes all snippet files
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 9f3de09..7c46a2a 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -98,6 +98,8 @@
 .. toctree::
    :maxdepth: 1
 
+   CMP0188: The FindGCCXML module is removed. </policy/CMP0188>
+   CMP0187: Include source file without an extension after the same name with an extension. </policy/CMP0187>
    CMP0186: Regular expressions match ^ at most once in repeated searches. </policy/CMP0186>
 
 Policies Introduced by CMake 4.0
diff --git a/Help/policy/CMP0187.rst b/Help/policy/CMP0187.rst
new file mode 100644
index 0000000..d833d84
--- /dev/null
+++ b/Help/policy/CMP0187.rst
@@ -0,0 +1,33 @@
+CMP0187
+-------
+
+.. versionadded:: 4.1
+
+Include source file without an extension after the same name with an extension.
+
+In CMake 4.0 and below, if two source files have the same filename and only one
+file has a file extension and the file with the extension is listed first, the
+file without the extension is omitted from the target.
+
+For example, the following library target only include ``hello.c`` in the
+target, but omits the file ``hello``.
+
+.. code-block:: cmake
+
+   add_library(library hello.c hello)
+
+If the file without the extension is listed before the file with the extension,
+both files are included in the target.
+
+Starting in CMake 4.1, CMake includes both files in the library target.
+
+This policy has no effect if :policy:`CMP0115` uses the ``OLD`` behavior.
+
+The ``OLD`` behavior for this policy is to omit the file without the extension.
+The ``NEW`` behavior for this policy is to include it.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0188.rst b/Help/policy/CMP0188.rst
new file mode 100644
index 0000000..6b4cb83
--- /dev/null
+++ b/Help/policy/CMP0188.rst
@@ -0,0 +1,21 @@
+CMP0188
+-------
+
+.. versionadded:: 4.1
+
+The :module:`FindGCCXML` module is removed.
+
+CMake 4.0 and below provide the :module:`FindGCCXML` module, but the GCC-XML
+tool has long been superseded by CastXML.  CMake 4.1 and above prefer to not
+provide the :module:`FindGCCXML` module.  This policy provides compatibility
+for projects that have not been ported away from it.
+
+The ``OLD`` behavior of this policy is for ``find_package(GCCXML)`` to load
+the deprecated module.  The ``NEW`` behavior is for ``find_package(GCCXML)``
+to fail as if the module does not exist.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.1
+.. |WARNS_OR_DOES_NOT_WARN| replace:: warns
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/release/4.0.rst b/Help/release/4.0.rst
index b1102d5..a7a7860 100644
--- a/Help/release/4.0.rst
+++ b/Help/release/4.0.rst
@@ -73,7 +73,7 @@
   See policy :policy:`CMP0184`.
 
 * The :variable:`CMAKE_POLICY_VERSION_MINIMUM` variable was added to
-  help pacakgers and end users try to configure existing projects that
+  help packagers and end users try to configure existing projects that
   have not been updated to work with supported CMake versions.
 
 * The :variable:`CMAKE_XCODE_SCHEME_LLDB_INIT_FILE` variable and corresponding
@@ -236,3 +236,8 @@
   now only shown when the message log level is set to ``VERBOSE``, ``DEBUG``,
   or ``TRACE`` via the   :option:`cmake --log-level` option or the
   :variable:`CMAKE_MESSAGE_LOG_LEVEL` cache variable.
+
+* Precompiled SunOS ``sparc`` and ``i386`` binaries are now provided
+  on `cmake.org`_.
+
+.. _`cmake.org`: https://cmake.org/download/
diff --git a/Help/release/dev/regex-fixes.rst b/Help/release/dev/regex-fixes.rst
index e979c03..82d1fad 100644
--- a/Help/release/dev/regex-fixes.rst
+++ b/Help/release/dev/regex-fixes.rst
@@ -3,3 +3,8 @@
 
 * Regular expressions match the ``^`` anchor at most once in repeated
   searches, at the start of the input.  See policy :policy:`CMP0186`.
+
+* References to unmatched groups are allowed, they are replaced with empty
+  strings.
+
+* Zero-length matches are always allowed.
diff --git a/Help/release/dev/remove-FindGCCXML.rst b/Help/release/dev/remove-FindGCCXML.rst
new file mode 100644
index 0000000..7f17b91
--- /dev/null
+++ b/Help/release/dev/remove-FindGCCXML.rst
@@ -0,0 +1,5 @@
+remove-FindGCCXML
+-----------------
+
+* The :module:`FindGCCXML` module has been deprecated via policy
+  :policy:`CMP0188`.  Port projects to CastXML instead.
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index c0877cc..cf9a055 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -1203,7 +1203,7 @@
   # when CUDA language is disabled
   if(NOT TARGET CUDA::cudart_static_deps)
     add_library(CUDA::cudart_static_deps IMPORTED INTERFACE)
-    if(UNIX AND (CMAKE_C_COMPILER OR CMAKE_CXX_COMPILER))
+    if(UNIX AND (CMAKE_C_COMPILER_LOADED OR CMAKE_CXX_COMPILER_LOADED))
       find_package(Threads REQUIRED)
       target_link_libraries(CUDA::cudart_static_deps INTERFACE Threads::Threads ${CMAKE_DL_LIBS})
     endif()
diff --git a/Modules/FindGCCXML.cmake b/Modules/FindGCCXML.cmake
index e6c7f24..4da19fe 100644
--- a/Modules/FindGCCXML.cmake
+++ b/Modules/FindGCCXML.cmake
@@ -5,17 +5,28 @@
 FindGCCXML
 ----------
 
+.. versionchanged:: 4.1
+  This module is available only if policy :policy:`CMP0188` is not set to ``NEW``.
+  Port projects to search for CastXML by calling ``find_program`` directly.
+
 Find the GCC-XML front-end executable.
 
-
-
 This module will define the following variables:
 
-::
-
-  GCCXML - the GCC-XML front-end executable.
+``GCCXML``
+  The GCC-XML front-end executable.
 #]=======================================================================]
 
+cmake_policy(GET CMP0188 _FindGCCXML_CMP0188)
+if(_FindGCCXML_CMP0188 STREQUAL "NEW")
+  message(FATAL_ERROR "The FindGCCXML module has been removed by policy CMP0188.")
+endif()
+
+if(_FindGCCXML_testing)
+  set(_FindGCCXML_included TRUE)
+  return()
+endif()
+
 find_program(GCCXML
   NAMES gccxml
         ../GCC_XML/gccxml
diff --git a/Modules/FindGettext.cmake b/Modules/FindGettext.cmake
index b982bb9..c38f9f5 100644
--- a/Modules/FindGettext.cmake
+++ b/Modules/FindGettext.cmake
@@ -52,7 +52,7 @@
                                [INSTALL_DESTINATION <destdir>]
                                LANGUAGES <lang>...)
 
-  This function creates a custom target "potfile" which processes the given
+  This function creates a custom target "potfiles" which processes the given
   .pot file to .mo files. Options:
 
   ``ALL``
diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake
index e66a42f..e8039aa 100644
--- a/Modules/FindMPI.cmake
+++ b/Modules/FindMPI.cmake
@@ -536,7 +536,7 @@
     # It has to be called as "compchk.sh <arch> <compiler>". Here, <arch> is one out of 32 (i686), 64 (ia64) or 32e (x86_64).
     # The compiler is identified by filename, and can be either the MPI compiler or the underlying compiler.
     # NOTE: It is vital to run this script while the environment variables are set up, otherwise it can check the wrong compiler.
-    if(MPI_COMPILE_CMDLINE MATCHES "^([^\" ]+/compchk.sh|\"[^\"]+/compchk.sh\") +([^ ]+)")
+    if(MPI_COMPILE_CMDLINE MATCHES "^([^\"\n ]+/compchk.sh|\"[^\"]+/compchk.sh\") +([^ ]+)")
       # Now CMAKE_MATCH_1 contains the path to the compchk.sh file and CMAKE_MATCH_2 the architecture flag.
       unset(COMPILER_CHECKER_OUTPUT)
       execute_process(
@@ -551,7 +551,7 @@
         endif()
       else()
         # Since the check passed, we can remove the compchk.sh script.
-        string(REGEX REPLACE "^([^\" ]+|\"[^\"]+\")/compchk.sh.*\n" "" MPI_COMPILE_CMDLINE "${MPI_COMPILE_CMDLINE}")
+        string(REGEX REPLACE "^([^\"\n ]+|\"[^\"]+\")/compchk.sh.*\n" "" MPI_COMPILE_CMDLINE "${MPI_COMPILE_CMDLINE}")
       endif()
     endif()
   endif()
@@ -624,10 +624,10 @@
       # Especially with M(VA)PICH-1, this appears to happen erroneously, and therefore we should translate
       # this output into an additional include directory and then drop it from the output.
       # noqa: spellcheck on
-      if(MPI_COMPILE_CMDLINE MATCHES "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h")
+      if(MPI_COMPILE_CMDLINE MATCHES "^ln -s ([^\"\n ]+|\"[^\"]+\") mpif.h")
         get_filename_component(MPI_INCLUDE_DIRS_WORK "${CMAKE_MATCH_1}" DIRECTORY)
-        string(REGEX REPLACE "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h\n" "" MPI_COMPILE_CMDLINE "${MPI_COMPILE_CMDLINE}")
-        string(REGEX REPLACE "^ln -s ([^\" ]+|\"[^\"]+\") mpif.h\n" "" MPI_LINK_CMDLINE "${MPI_LINK_CMDLINE}")
+        string(REGEX REPLACE "^ln -s ([^\"\n ]+|\"[^\"]+\") mpif.h\n" "" MPI_COMPILE_CMDLINE "${MPI_COMPILE_CMDLINE}")
+        string(REGEX REPLACE "^ln -s ([^\"\n ]+|\"[^\"]+\") mpif.h\n" "" MPI_LINK_CMDLINE "${MPI_LINK_CMDLINE}")
         string(REGEX REPLACE "\nrm -f mpif.h$" "" MPI_COMPILE_CMDLINE "${MPI_COMPILE_CMDLINE}")
         string(REGEX REPLACE "\nrm -f mpif.h$" "" MPI_LINK_CMDLINE "${MPI_LINK_CMDLINE}")
       endif()
@@ -645,7 +645,7 @@
   # on Windows seems to require any specific ones, either.
   if(NOT MSVC)
     # Extract compile options from the compile command line.
-    string(REGEX MATCHALL "(^| )-f([^\" ]+|\"[^\"]+\")" MPI_ALL_COMPILE_OPTIONS "${MPI_COMPILE_CMDLINE}")
+    string(REGEX MATCHALL "(^| )-f([^\"\n ]+|\"[^\"]+\")" MPI_ALL_COMPILE_OPTIONS "${MPI_COMPILE_CMDLINE}")
 
     foreach(_MPI_COMPILE_OPTION IN LISTS MPI_ALL_COMPILE_OPTIONS)
       string(REGEX REPLACE "^ " "" _MPI_COMPILE_OPTION "${_MPI_COMPILE_OPTION}")
@@ -673,7 +673,7 @@
   endif()
 
   # Same deal as above, for the definitions.
-  string(REGEX MATCHALL "(^| )${_MPI_PREPROCESSOR_FLAG_REGEX}-D *([^\" ]+|\"[^\"]+\")" MPI_ALL_COMPILE_DEFINITIONS "${MPI_COMPILE_CMDLINE}")
+  string(REGEX MATCHALL "(^| )${_MPI_PREPROCESSOR_FLAG_REGEX}-D *([^\"\n ]+|\"[^\"]+\")" MPI_ALL_COMPILE_DEFINITIONS "${MPI_COMPILE_CMDLINE}")
 
   foreach(_MPI_COMPILE_DEFINITION IN LISTS MPI_ALL_COMPILE_DEFINITIONS)
     string(REGEX REPLACE "^ ?${_MPI_PREPROCESSOR_FLAG_REGEX}-D *" "" _MPI_COMPILE_DEFINITION "${_MPI_COMPILE_DEFINITION}")
@@ -684,7 +684,7 @@
   endforeach()
 
   # Extract include paths from compile command line
-  string(REGEX MATCHALL "(^|\n| )${_MPI_PREPROCESSOR_FLAG_REGEX}${CMAKE_INCLUDE_FLAG_${LANG}} *([^\" ]+|\"[^\"]+\")"
+  string(REGEX MATCHALL "(^|\n| )${_MPI_PREPROCESSOR_FLAG_REGEX}${CMAKE_INCLUDE_FLAG_${LANG}} *([^\"\n ]+|\"[^\"]+\")"
     MPI_ALL_INCLUDE_PATHS "${MPI_COMPILE_CMDLINE}")
 
   # If extracting failed to work, we'll try using -showme:incdirs.
@@ -707,7 +707,7 @@
   endforeach()
 
   # The next step are linker flags and library directories. Here, we first take the flags given in raw -L or -LIBPATH: syntax.
-  string(REGEX MATCHALL "(^| )${CMAKE_LIBRARY_PATH_FLAG} *([^\" ]+|\"[^\"]+\")" MPI_DIRECT_LINK_PATHS "${MPI_LINK_CMDLINE}")
+  string(REGEX MATCHALL "(^| )${CMAKE_LIBRARY_PATH_FLAG} *([^\"\n ]+|\"[^\"]+\")" MPI_DIRECT_LINK_PATHS "${MPI_LINK_CMDLINE}")
   foreach(_MPI_LPATH IN LISTS MPI_DIRECT_LINK_PATHS)
     string(REGEX REPLACE "(^| )${CMAKE_LIBRARY_PATH_FLAG} *" "" _MPI_LPATH "${_MPI_LPATH}")
     list(APPEND MPI_ALL_LINK_PATHS "${_MPI_LPATH}")
@@ -715,7 +715,7 @@
 
   # If the link commandline hasn't been filtered (e.g. when using MSVC and /link), we need to extract the relevant parts first.
   if(NOT _MPI_FILTERED_LINK_INFORMATION)
-    string(REGEX MATCHALL "(^| )(-Wl,|-Xlinker +)([^\" ]+|\"[^\"]+\")" MPI_LINK_FLAGS "${MPI_LINK_CMDLINE}")
+    string(REGEX MATCHALL "(^| )(-Wl,|-Xlinker +)([^\"\n ]+|\"[^\"]+\")" MPI_LINK_FLAGS "${MPI_LINK_CMDLINE}")
 
     # In this case, we could also find some indirectly given linker paths, e.g. prefixed by -Xlinker or -Wl,
     # Since syntaxes like -Wl,-L -Wl,/my/path/to/lib are also valid, we parse these paths by first removing -Wl, and -Xlinker
@@ -724,7 +724,7 @@
 
     # Now we can parse the leftover output. Note that spaces can now be handled since the above example would reduce to
     # -L /my/path/to/lib and can be extracted correctly.
-    string(REGEX MATCHALL "^(${CMAKE_LIBRARY_PATH_FLAG},? *|--library-path=)([^\" ]+|\"[^\"]+\")"
+    string(REGEX MATCHALL "^(${CMAKE_LIBRARY_PATH_FLAG},? *|--library-path=)([^\"\n ]+|\"[^\"]+\")"
       MPI_INDIRECT_LINK_PATHS "${MPI_LINK_FLAGS_RAW}")
 
     foreach(_MPI_LPATH IN LISTS MPI_INDIRECT_LINK_PATHS)
@@ -733,7 +733,7 @@
     endforeach()
 
     # We need to remove the flags we extracted from the linker flag list now.
-    string(REGEX REPLACE "(^| )(-Wl,|-Xlinker +)(${CMAKE_LIBRARY_PATH_FLAG},? *(-Wl,|-Xlinker +)?|--library-path=)([^\" ]+|\"[^\"]+\")" ""
+    string(REGEX REPLACE "(^| )(-Wl,|-Xlinker +)(${CMAKE_LIBRARY_PATH_FLAG},? *(-Wl,|-Xlinker +)?|--library-path=)([^\"\n ]+|\"[^\"]+\")" ""
       MPI_LINK_CMDLINE_FILTERED "${MPI_LINK_CMDLINE}")
 
     # Some MPI implementations pass on options they themselves were built with. Since -z,noexecstack is a common
@@ -742,7 +742,7 @@
     string(REGEX REPLACE "(^| )-Xlinker +-z +-Xlinker +[^ ]+" "" MPI_LINK_CMDLINE_FILTERED "${MPI_LINK_CMDLINE_FILTERED}")
 
     # We only consider options of the form -Wl or -Xlinker:
-    string(REGEX MATCHALL "(^| )(-Wl,|-Xlinker +)([^\" ]+|\"[^\"]+\")" MPI_ALL_LINK_FLAGS "${MPI_LINK_CMDLINE_FILTERED}")
+    string(REGEX MATCHALL "(^| )(-Wl,|-Xlinker +)([^\"\n ]+|\"[^\"]+\")" MPI_ALL_LINK_FLAGS "${MPI_LINK_CMDLINE_FILTERED}")
 
     # As a next step, we assemble the linker flags extracted in a preliminary flags string
     foreach(_MPI_LINK_FLAG IN LISTS MPI_ALL_LINK_FLAGS)
@@ -755,7 +755,7 @@
     endforeach()
   else()
     # In the filtered case, we obtain the link time flags by just stripping the library paths.
-    string(REGEX REPLACE "(^| )${CMAKE_LIBRARY_PATH_FLAG} *([^\" ]+|\"[^\"]+\")" "" MPI_LINK_CMDLINE_FILTERED "${MPI_LINK_CMDLINE}")
+    string(REGEX REPLACE "(^| )${CMAKE_LIBRARY_PATH_FLAG} *([^\"\n ]+|\"[^\"]+\")" "" MPI_LINK_CMDLINE_FILTERED "${MPI_LINK_CMDLINE}")
   endif()
 
   # If we failed to extract any linker paths, we'll try using the -showme:libdirs option with the MPI compiler.
@@ -777,7 +777,7 @@
   # Extract the set of libraries to link against from the link command line
   # This only makes sense if CMAKE_LINK_LIBRARY_FLAG is defined, i.e. a -lxxxx syntax is supported by the compiler.
   if(CMAKE_LINK_LIBRARY_FLAG)
-    string(REGEX MATCHALL "(^| )${CMAKE_LINK_LIBRARY_FLAG}([^\" ]+|\"[^\"]+\")"
+    string(REGEX MATCHALL "(^| )${CMAKE_LINK_LIBRARY_FLAG}([^\"\n ]+|\"[^\"]+\")"
       MPI_LIBNAMES "${MPI_LINK_CMDLINE}")
 
     foreach(_MPI_LIB_NAME IN LISTS MPI_LIBNAMES)
@@ -800,7 +800,7 @@
   else()
     string(APPEND _MPI_LIB_SUFFIX_REGEX "|${CMAKE_SHARED_LIBRARY_SUFFIX}")
   endif()
-  set(_MPI_LIB_NAME_REGEX "(([^\" ]+(${_MPI_LIB_SUFFIX_REGEX}))|(\"[^\"]+(${_MPI_LIB_SUFFIX_REGEX})\"))( +|$)")
+  set(_MPI_LIB_NAME_REGEX "(([^\"\n ]+(${_MPI_LIB_SUFFIX_REGEX}))|(\"[^\"]+(${_MPI_LIB_SUFFIX_REGEX})\"))( +|$)")
   string(REPLACE "." "\\." _MPI_LIB_NAME_REGEX "${_MPI_LIB_NAME_REGEX}")
 
   string(REGEX MATCHALL "${_MPI_LIB_NAME_REGEX}" MPI_LIBNAMES "${MPI_LINK_CMDLINE}")
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 080e99f..c12e07a 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 4)
 set(CMake_VERSION_MINOR 0)
-set(CMake_VERSION_PATCH 20250215)
+set(CMake_VERSION_PATCH 20250220)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
@@ -24,14 +24,15 @@
   set(git_info [==[$Format:%h %s$]==])
 
   # Otherwise, try to identify the current development source version.
+  get_filename_component(git_toplevel "${CMAKE_CURRENT_LIST_DIR}" PATH)
   if(NOT git_info MATCHES "^([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]?[0-9a-f]?)[0-9a-f]* "
-      AND EXISTS ${CMake_SOURCE_DIR}/.git)
+      AND EXISTS "${git_toplevel}/.git")
     find_package(Git QUIET)
     if(GIT_FOUND)
       macro(_git)
         execute_process(
           COMMAND ${GIT_EXECUTABLE} ${ARGN}
-          WORKING_DIRECTORY ${CMake_SOURCE_DIR}
+          WORKING_DIRECTORY "${git_toplevel}"
           RESULT_VARIABLE _git_res
           OUTPUT_VARIABLE _git_out OUTPUT_STRIP_TRAILING_WHITESPACE
           ERROR_VARIABLE _git_err ERROR_STRIP_TRAILING_WHITESPACE
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index af54e56..9cc1110 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -11,6 +11,7 @@
 #include <cm/string_view>
 #include <cmext/algorithm>
 
+#include <cm3p/json/value.h>
 #include <cm3p/uv.h>
 
 #include "cmsys/Directory.hxx"
@@ -21,6 +22,9 @@
 #include "cmDuration.h"
 #include "cmFileTimeCache.h"
 #include "cmGeneratedFileStream.h"
+#include "cmInstrumentation.h"
+#include "cmInstrumentationQuery.h"
+#include "cmJSONState.h"
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmProcessOutput.h"
@@ -429,6 +433,11 @@
   } else {
     this->GenerateXMLLogScraped(xml);
   }
+
+  this->CTest->GetInstrumentation().CollectTimingData(
+    cmInstrumentationQuery::Hook::PrepareForCDash);
+  this->GenerateInstrumentationXML(xml);
+
   this->GenerateXMLFooter(xml, elapsed_build_time);
 
   if (!res || retVal || this->TotalErrors > 0) {
@@ -595,6 +604,88 @@
   }
 }
 
+void cmCTestBuildHandler::GenerateInstrumentationXML(cmXMLWriter& xml)
+{
+  // Record instrumentation data on a per-target basis.
+  cmsys::Directory targets_dir;
+  std::string targets_snippet_dir = cmStrCat(
+    this->CTest->GetInstrumentation().GetCDashDir(), "/build/targets");
+  if (targets_dir.Load(targets_snippet_dir) &&
+      targets_dir.GetNumberOfFiles() > 0) {
+    xml.StartElement("Targets");
+    for (unsigned int i = 0; i < targets_dir.GetNumberOfFiles(); i++) {
+      if (!targets_dir.FileIsDirectory(i)) {
+        continue;
+      }
+      std::string target_name = targets_dir.GetFile(i);
+      if (target_name == "." || target_name == "..") {
+        continue;
+      }
+      std::string target_type = "UNKNOWN";
+
+      xml.StartElement("Target");
+      xml.Attribute("name", target_name);
+
+      // Check if we have a link snippet for this target.
+      cmsys::Directory target_dir;
+      if (!target_dir.Load(targets_dir.GetFilePath(i))) {
+        cmSystemTools::Error(
+          cmStrCat("Error loading directory ", targets_dir.GetFilePath(i)));
+      }
+      Json::Value link_item;
+      for (unsigned int j = 0; j < target_dir.GetNumberOfFiles(); j++) {
+        std::string fname = target_dir.GetFile(j);
+        if (fname.rfind("link-", 0) == 0) {
+          std::string fpath = target_dir.GetFilePath(j);
+          cmJSONState parseState = cmJSONState(fpath, &link_item);
+          if (!parseState.errors.empty()) {
+            cmSystemTools::Error(parseState.GetErrorMessage(true));
+            break;
+          }
+
+          if (!link_item.isObject()) {
+            std::string error_msg =
+              cmStrCat("Expected snippet ", fpath, " to contain an object");
+            cmSystemTools::Error(error_msg);
+            break;
+          }
+          break;
+        }
+      }
+
+      // If so, parse targetType and targetLabels (optional) from it.
+      if (link_item.isMember("targetType")) {
+        target_type = link_item["targetType"].asString();
+      }
+
+      xml.Attribute("type", target_type);
+
+      if (link_item.isMember("targetLabels") &&
+          !link_item["targetLabels"].empty()) {
+        xml.StartElement("Labels");
+        for (auto const& json_label_item : link_item["targetLabels"]) {
+          xml.Element("Label", json_label_item.asString());
+        }
+        xml.EndElement(); // Labels
+      }
+
+      // Write instrumendation data for this target.
+      std::string target_subdir = cmStrCat("build/targets/", target_name);
+      this->CTest->ConvertInstrumentationSnippetsToXML(xml, target_subdir);
+      std::string target_dir_fullpath = cmStrCat(
+        this->CTest->GetInstrumentation().GetCDashDir(), '/', target_subdir);
+      if (cmSystemTools::FileIsDirectory(target_dir_fullpath)) {
+        cmSystemTools::RemoveADirectory(target_dir_fullpath);
+      }
+      xml.EndElement(); // Target
+    }
+    xml.EndElement(); // Targets
+  }
+
+  // Also record instrumentation data for custom commands (no target).
+  this->CTest->ConvertInstrumentationSnippetsToXML(xml, "build/commands");
+}
+
 void cmCTestBuildHandler::GenerateXMLFooter(cmXMLWriter& xml,
                                             cmDuration elapsed_build_time)
 {
diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h
index 9d9b847..a455346 100644
--- a/Source/CTest/cmCTestBuildHandler.h
+++ b/Source/CTest/cmCTestBuildHandler.h
@@ -85,6 +85,7 @@
   void GenerateXMLHeader(cmXMLWriter& xml);
   void GenerateXMLLaunched(cmXMLWriter& xml);
   void GenerateXMLLogScraped(cmXMLWriter& xml);
+  void GenerateInstrumentationXML(cmXMLWriter& xml);
   void GenerateXMLFooter(cmXMLWriter& xml, cmDuration elapsed_build_time);
   bool IsLaunchedErrorFile(char const* fname);
   bool IsLaunchedWarningFile(char const* fname);
diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx
index 820eeae..4657bbd 100644
--- a/Source/CTest/cmCTestConfigureCommand.cxx
+++ b/Source/CTest/cmCTestConfigureCommand.cxx
@@ -17,6 +17,8 @@
 #include "cmExecutionStatus.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
+#include "cmInstrumentation.h"
+#include "cmInstrumentationQuery.h"
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmStringAlgorithms.h"
@@ -203,6 +205,11 @@
   xml.Element("EndDateTime", endDateTime);
   xml.Element("EndConfigureTime", endTime);
   xml.Element("ElapsedMinutes", elapsedMinutes.count());
+
+  this->CTest->GetInstrumentation().CollectTimingData(
+    cmInstrumentationQuery::Hook::PrepareForCDash);
+  this->CTest->ConvertInstrumentationSnippetsToXML(xml, "configure");
+
   xml.EndElement(); // Configure
   this->CTest->EndXML(xml);
 
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index 2f25de7..200bd7d 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -268,7 +268,9 @@
 {
   auto instrumentation = cmInstrumentation(this->Reporter.OptionBuildDir);
   std::map<std::string, std::string> options;
-  options["target"] = this->Reporter.OptionTargetName;
+  if (this->Reporter.OptionTargetName != "TARGET_NAME") {
+    options["target"] = this->Reporter.OptionTargetName;
+  }
   options["source"] = this->Reporter.OptionSource;
   options["language"] = this->Reporter.OptionLanguage;
   options["targetType"] = this->Reporter.OptionTargetType;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 42dc0af..4076347 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -21,6 +21,7 @@
 #include "cmCTestMemCheckHandler.h"
 #include "cmCTestMultiProcessHandler.h"
 #include "cmDuration.h"
+#include "cmInstrumentation.h"
 #include "cmProcess.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -34,7 +35,6 @@
   , CTest(MultiTestHandler.CTest)
   , TestHandler(MultiTestHandler.TestHandler)
   , TestProperties(MultiTestHandler.Properties[Index])
-  , Instrumentation(cmSystemTools::GetLogicalWorkingDirectory())
 {
 }
 
@@ -664,8 +664,8 @@
     return false;
   }
   this->StartTime = this->CTest->CurrentTime();
-  if (this->Instrumentation.HasQuery()) {
-    this->Instrumentation.GetPreTestStats();
+  if (this->CTest->GetInstrumentation().HasQuery()) {
+    this->CTest->GetInstrumentation().GetPreTestStats();
   }
 
   return this->ForkProcess();
@@ -1016,12 +1016,13 @@
 
 void cmCTestRunTest::FinalizeTest(bool started)
 {
-  if (this->Instrumentation.HasQuery()) {
-    this->Instrumentation.InstrumentTest(
+  if (this->CTest->GetInstrumentation().HasQuery()) {
+    std::string data_file = this->CTest->GetInstrumentation().InstrumentTest(
       this->TestProperties->Name, this->ActualCommand, this->Arguments,
       this->TestProcess->GetExitValue(), this->TestProcess->GetStartTime(),
       this->TestProcess->GetSystemStartTime(),
       this->GetCTest()->GetConfigType());
+    this->TestResult.InstrumentationFile = data_file;
   }
   this->MultiTestHandler.FinishTestProcess(this->TestProcess->GetRunner(),
                                            started);
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index fd5e037..05a5f76 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -14,7 +14,6 @@
 #include "cmCTest.h"
 #include "cmCTestMultiProcessHandler.h"
 #include "cmCTestTestHandler.h"
-#include "cmInstrumentation.h"
 #include "cmProcess.h"
 
 /** \class cmRunTest
@@ -141,7 +140,6 @@
   int NumberOfRunsTotal = 1; // default to 1 run of the test
   bool RunAgain = false;     // default to not having to run again
   size_t TotalNumberOfTests;
-  cmInstrumentation Instrumentation;
 };
 
 inline int getNumWidth(size_t n)
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index afe505d..1f45b9d 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -41,6 +41,8 @@
 #include "cmExecutionStatus.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
+#include "cmInstrumentation.h"
+#include "cmInstrumentationQuery.h"
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -1381,6 +1383,9 @@
     return;
   }
 
+  this->CTest->GetInstrumentation().CollectTimingData(
+    cmInstrumentationQuery::Hook::PrepareForCDash);
+
   this->CTest->StartXML(xml, this->CMake, this->AppendXML);
   this->CTest->GenerateSubprojectsOutput(xml);
   xml.StartElement("Testing");
@@ -1395,7 +1400,6 @@
   for (cmCTestTestResult& result : this->TestResults) {
     this->WriteTestResultHeader(xml, result);
     xml.StartElement("Results");
-
     if (result.Status != cmCTestTestHandler::NOT_RUN) {
       if (result.Status != cmCTestTestHandler::COMPLETED ||
           result.ReturnValue) {
@@ -1473,6 +1477,15 @@
     xml.Content(result.Output);
     xml.EndElement(); // Value
     xml.EndElement(); // Measurement
+
+    if (!result.InstrumentationFile.empty()) {
+      std::string instrument_file_path =
+        cmStrCat(this->CTest->GetInstrumentation().GetCDashDir(), "/test/",
+                 result.InstrumentationFile);
+      this->CTest->ConvertInstrumentationJSONFileToXML(instrument_file_path,
+                                                       xml);
+    }
+
     xml.EndElement(); // Results
 
     this->AttachFiles(xml, result);
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 393b3d3..b97d6b7 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -192,6 +192,7 @@
     std::string CustomCompletionStatus;
     std::string Output;
     std::string TestMeasurementsOutput;
+    std::string InstrumentationFile;
     int TestCount = 0;
     cmCTestTestProperties* Properties = nullptr;
   };
@@ -250,6 +251,7 @@
                              cmCTestTestResult const& result);
   void WriteTestResultFooter(cmXMLWriter& xml,
                              cmCTestTestResult const& result);
+
   // Write attached test files into the xml
   void AttachFiles(cmXMLWriter& xml, cmCTestTestResult& result);
   void AttachFile(cmXMLWriter& xml, std::string const& file,
diff --git a/Source/cmBase32.cxx b/Source/cmBase32.cxx
index b11fd79..1a79caa 100644
--- a/Source/cmBase32.cxx
+++ b/Source/cmBase32.cxx
@@ -55,15 +55,15 @@
   size_t remain = static_cast<size_t>(end - input);
   if (remain != 0) {
     // Temporary source buffer filled up with 0s
-    unsigned char extended[blockSize];
+    unsigned char padded[blockSize];
     for (size_t ii = 0; ii != remain; ++ii) {
-      extended[ii] = input[ii];
+      padded[ii] = input[ii];
     }
     for (size_t ii = remain; ii != blockSize; ++ii) {
-      extended[ii] = 0;
+      padded[ii] = 0;
     }
 
-    Base32Encode5(extended, buffer);
+    Base32Encode5(padded, buffer);
     size_t numPad(0);
     switch (remain) {
       case 1:
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 0bb9260..2e0b07e 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -10,7 +10,6 @@
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
-#include <functional>
 #include <initializer_list>
 #include <iostream>
 #include <map>
@@ -27,6 +26,7 @@
 #include <cmext/string_view>
 
 #include <cm3p/curl/curl.h>
+#include <cm3p/json/value.h>
 #include <cm3p/uv.h>
 #include <cm3p/zlib.h>
 
@@ -115,6 +115,8 @@
   bool UseHTTP10 = false;
   bool PrintLabels = false;
   bool Failover = false;
+  bool UseVerboseInstrumentation = false;
+  cmJSONState parseState;
 
   bool FlushTestProgressLine = false;
 
@@ -195,6 +197,8 @@
 
   cmCTestTestOptions TestOptions;
   std::vector<std::string> CommandLineHttpHeaders;
+
+  std::unique_ptr<cmInstrumentation> Instrumentation;
 };
 
 struct tm* cmCTest::GetNightlyTime(std::string const& str, bool tomorrowtag)
@@ -320,6 +324,11 @@
   if (cmSystemTools::GetEnv("CTEST_PROGRESS_OUTPUT", envValue)) {
     this->Impl->TestProgressOutput = !cmIsOff(envValue);
   }
+  envValue.clear();
+  if (cmSystemTools::GetEnv("CTEST_USE_VERBOSE_INSTRUMENTATION", envValue)) {
+    this->Impl->UseVerboseInstrumentation = !cmIsOff(envValue);
+  }
+  envValue.clear();
 
   this->Impl->Parts[PartStart].SetName("Start");
   this->Impl->Parts[PartUpdate].SetName("Update");
@@ -2628,32 +2637,23 @@
   }
 #endif
 
-  cmInstrumentation instrumentation(
-    cmSystemTools::GetCurrentWorkingDirectory());
-  std::function<int()> doTest = [this, &cmakeAndTest, &runScripts,
-                                 &processSteps]() -> int {
-    // now what should cmake do? if --build-and-test was specified then
-    // we run the build and test handler and return
-    if (cmakeAndTest) {
-      return this->RunCMakeAndTest();
-    }
+  // now what should cmake do? if --build-and-test was specified then
+  // we run the build and test handler and return
+  if (cmakeAndTest) {
+    return this->RunCMakeAndTest();
+  }
 
-    // -S, -SP, and/or -SP was specified
-    if (!runScripts.empty()) {
-      return this->RunScripts(runScripts);
-    }
+  // -S, -SP, and/or -SP was specified
+  if (!runScripts.empty()) {
+    return this->RunScripts(runScripts);
+  }
 
-    // -D, -T, and/or -M was specified
-    if (processSteps) {
-      return this->ProcessSteps();
-    }
+  // -D, -T, and/or -M was specified
+  if (processSteps) {
+    return this->ProcessSteps();
+  }
 
-    return this->ExecuteTests();
-  };
-  int ret = instrumentation.InstrumentCommand("ctest", args,
-                                              [doTest]() { return doTest(); });
-  instrumentation.CollectTimingData(cmInstrumentationQuery::Hook::PostTest);
-  return ret;
+  return this->ExecuteTests(args);
 }
 
 int cmCTest::RunScripts(
@@ -2677,7 +2677,7 @@
   return res;
 }
 
-int cmCTest::ExecuteTests()
+int cmCTest::ExecuteTests(std::vector<std::string> const& args)
 {
   this->Impl->ExtraVerbose = this->Impl->Verbose;
   this->Impl->Verbose = true;
@@ -2722,7 +2722,14 @@
   }
 
   handler.SetVerbose(this->Impl->Verbose);
-  if (handler.ProcessHandler() < 0) {
+
+  cmInstrumentation instrumentation(this->GetBinaryDir());
+  auto processHandler = [&handler]() -> int {
+    return handler.ProcessHandler();
+  };
+  int ret = instrumentation.InstrumentCommand("ctest", args, processHandler);
+  instrumentation.CollectTimingData(cmInstrumentationQuery::Hook::PostTest);
+  if (ret < 0) {
     cmCTestLog(this, ERROR_MESSAGE, "Errors while running CTest\n");
     if (!this->Impl->OutputTestOutputOnTestFailure) {
       std::string const lastTestLog =
@@ -3673,3 +3680,128 @@
   }
   return true;
 }
+
+cmInstrumentation& cmCTest::GetInstrumentation()
+{
+  if (!this->Impl->Instrumentation) {
+    this->Impl->Instrumentation =
+      cm::make_unique<cmInstrumentation>(this->GetBinaryDir());
+  }
+  return *this->Impl->Instrumentation;
+}
+
+bool cmCTest::GetUseVerboseInstrumentation() const
+{
+  return this->Impl->UseVerboseInstrumentation;
+}
+
+void cmCTest::ConvertInstrumentationSnippetsToXML(cmXMLWriter& xml,
+                                                  std::string const& subdir)
+{
+  std::string data_dir =
+    cmStrCat(this->GetInstrumentation().GetCDashDir(), '/', subdir);
+
+  cmsys::Directory d;
+  if (!d.Load(data_dir) || d.GetNumberOfFiles() == 0) {
+    return;
+  }
+
+  xml.StartElement("Commands");
+
+  for (unsigned int i = 0; i < d.GetNumberOfFiles(); i++) {
+    std::string fpath = d.GetFilePath(i);
+    std::string fname = d.GetFile(i);
+    if (fname.rfind('.', 0) == 0) {
+      continue;
+    }
+    this->ConvertInstrumentationJSONFileToXML(fpath, xml);
+  }
+
+  xml.EndElement(); // Commands
+}
+
+bool cmCTest::ConvertInstrumentationJSONFileToXML(std::string const& fpath,
+                                                  cmXMLWriter& xml)
+{
+  Json::Value root;
+  this->Impl->parseState = cmJSONState(fpath, &root);
+  if (!this->Impl->parseState.errors.empty()) {
+    cmCTestLog(this, ERROR_MESSAGE,
+               this->Impl->parseState.GetErrorMessage(true) << std::endl);
+    return false;
+  }
+
+  if (root.type() != Json::objectValue) {
+    cmCTestLog(this, ERROR_MESSAGE,
+               "Expected object, found " << root.type() << " for "
+                                         << root.asString() << std::endl);
+    return false;
+  }
+
+  std::vector<std::string> required_members = {
+    "command",
+    "role",
+    "dynamicSystemInformation",
+  };
+  for (std::string const& required_member : required_members) {
+    if (!root.isMember(required_member)) {
+      cmCTestLog(this, ERROR_MESSAGE,
+                 fpath << " is missing the '" << required_member << "' key"
+                       << std::endl);
+      return false;
+    }
+  }
+
+  // Do not record command-level data for Test.xml files because
+  // it is redundant with information actually captured by CTest.
+  bool generating_test_xml = root["role"] == "test";
+  if (!generating_test_xml) {
+    std::string element_name = root["role"].asString();
+    element_name[0] = static_cast<char>(std::toupper(element_name[0]));
+    xml.StartElement(element_name);
+    std::vector<std::string> keys = root.getMemberNames();
+    for (auto const& key : keys) {
+      auto key_type = root[key].type();
+      if (key_type == Json::objectValue || key_type == Json::arrayValue) {
+        continue;
+      }
+      if (key == "role" || key == "target" || key == "targetType" ||
+          key == "targetLabels") {
+        continue;
+      }
+      // Truncate the full command line if verbose instrumentation
+      // was not requested.
+      if (key == "command" && !this->GetUseVerboseInstrumentation()) {
+        std::string command_str = root[key].asString();
+        std::string truncated = command_str.substr(0, command_str.find(' '));
+        if (command_str != truncated) {
+          truncated = cmStrCat(truncated, " (truncated)");
+        }
+        xml.Attribute(key.c_str(), truncated);
+        continue;
+      }
+      xml.Attribute(key.c_str(), root[key].asString());
+    }
+  }
+
+  // Record dynamicSystemInformation section as XML.
+  auto dynamic_information = root["dynamicSystemInformation"];
+  std::vector<std::string> keys = dynamic_information.getMemberNames();
+  for (auto const& key : keys) {
+    std::string measurement_name = key;
+    measurement_name[0] = static_cast<char>(std::toupper(measurement_name[0]));
+
+    xml.StartElement("NamedMeasurement");
+    xml.Attribute("type", "numeric/double");
+    xml.Attribute("name", measurement_name);
+    xml.Element("Value", dynamic_information[key].asString());
+    xml.EndElement(); // NamedMeasurement
+  }
+
+  if (!generating_test_xml) {
+    xml.EndElement(); // role
+  }
+
+  cmSystemTools::RemoveFile(fpath);
+  return true;
+}
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index c6bfede..fd6d8b1 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -21,6 +21,7 @@
 
 class cmake;
 class cmGeneratedFileStream;
+class cmInstrumentation;
 class cmMakefile;
 class cmValue;
 class cmXMLWriter;
@@ -391,6 +392,11 @@
   bool StartLogFile(char const* name, int submitIndex,
                     cmGeneratedFileStream& xofs);
 
+  void ConvertInstrumentationSnippetsToXML(cmXMLWriter& xml,
+                                           std::string const& subdir);
+  bool ConvertInstrumentationJSONFileToXML(std::string const& fpath,
+                                           cmXMLWriter& xml);
+
   void AddSiteProperties(cmXMLWriter& xml, cmake* cm);
 
   bool GetInteractiveDebugMode() const;
@@ -433,6 +439,9 @@
   cmCTestTestOptions const& GetTestOptions() const;
   std::vector<std::string> GetCommandLineHttpHeaders() const;
 
+  cmInstrumentation& GetInstrumentation();
+  bool GetUseVerboseInstrumentation() const;
+
 private:
   int GenerateNotesFile(cmake* cm, std::string const& files);
 
@@ -471,7 +480,7 @@
 
   int RunCMakeAndTest();
   int RunScripts(std::vector<std::pair<std::string, bool>> const& scripts);
-  int ExecuteTests();
+  int ExecuteTests(std::vector<std::string> const& args);
 
   struct Private;
   std::unique_ptr<Private> Impl;
diff --git a/Source/cmExportPackageInfoGenerator.cxx b/Source/cmExportPackageInfoGenerator.cxx
index 211347e..8616e10 100644
--- a/Source/cmExportPackageInfoGenerator.cxx
+++ b/Source/cmExportPackageInfoGenerator.cxx
@@ -127,9 +127,22 @@
 {
   if (!this->Requirements.empty()) {
     Json::Value& requirements = package["requires"];
+
+    // Build description for each requirement.
     for (auto const& requirement : this->Requirements) {
+      auto data = Json::Value{ Json::objectValue };
+
+      // Add required components.
+      if (!requirement.second.empty()) {
+        auto components = Json::Value{ Json::arrayValue };
+        for (std::string const& component : requirement.second) {
+          components.append(component);
+        }
+        data["components"] = components;
+      }
+
       // TODO: version, hint
-      requirements[requirement] = Json::Value{};
+      requirements[requirement.first] = data;
     }
   }
 }
@@ -257,10 +270,10 @@
       return false;
     }
 
+    std::string component = linkedName.substr(prefix.length());
+    this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
     // TODO: Record package version, hint.
-    this->Requirements.emplace(pkgName);
-    this->LinkTargets.emplace(
-      linkedName, cmStrCat(pkgName, ':', linkedName.substr(prefix.length())));
+    this->Requirements[pkgName].emplace(std::move(component));
     return true;
   }
 
@@ -278,16 +291,13 @@
       return false;
     }
 
-    auto pkgName =
-      cm::string_view{ linkNamespace.data(), linkNamespace.size() - 2 };
-
+    std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
+    std::string component = linkedTarget->GetExportName();
     if (pkgName == this->GetPackageName()) {
-      this->LinkTargets.emplace(linkedName,
-                                cmStrCat(':', linkedTarget->GetExportName()));
+      this->LinkTargets.emplace(linkedName, cmStrCat(':', component));
     } else {
-      this->Requirements.emplace(pkgName);
-      this->LinkTargets.emplace(
-        linkedName, cmStrCat(pkgName, ':', linkedTarget->GetExportName()));
+      this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
+      this->Requirements[pkgName].emplace(std::move(component));
     }
     return true;
   }
diff --git a/Source/cmExportPackageInfoGenerator.h b/Source/cmExportPackageInfoGenerator.h
index 6fe1e35..5014252 100644
--- a/Source/cmExportPackageInfoGenerator.h
+++ b/Source/cmExportPackageInfoGenerator.h
@@ -110,5 +110,5 @@
   std::vector<std::string> DefaultConfigurations;
 
   std::map<std::string, std::string> LinkTargets;
-  std::set<std::string> Requirements;
+  std::map<std::string, std::set<std::string>> Requirements;
 };
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index 7720485..2c51cf0 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -532,6 +532,7 @@
   this->DeprecatedFindModules["Boost"] = cmPolicies::CMP0167;
   this->DeprecatedFindModules["CUDA"] = cmPolicies::CMP0146;
   this->DeprecatedFindModules["Dart"] = cmPolicies::CMP0145;
+  this->DeprecatedFindModules["GCCXML"] = cmPolicies::CMP0188;
   this->DeprecatedFindModules["PythonInterp"] = cmPolicies::CMP0148;
   this->DeprecatedFindModules["PythonLibs"] = cmPolicies::CMP0148;
   this->DeprecatedFindModules["Qt"] = cmPolicies::CMP0084;
diff --git a/Source/cmIncludeCommand.cxx b/Source/cmIncludeCommand.cxx
index e54a8b7..0e8fa74 100644
--- a/Source/cmIncludeCommand.cxx
+++ b/Source/cmIncludeCommand.cxx
@@ -25,6 +25,7 @@
     DeprecatedModules["FindBoost"] = cmPolicies::CMP0167;
     DeprecatedModules["FindCUDA"] = cmPolicies::CMP0146;
     DeprecatedModules["FindDart"] = cmPolicies::CMP0145;
+    DeprecatedModules["FindGCCXML"] = cmPolicies::CMP0188;
     DeprecatedModules["FindPythonInterp"] = cmPolicies::CMP0148;
     DeprecatedModules["FindPythonLibs"] = cmPolicies::CMP0148;
     DeprecatedModules["WriteCompilerDetectionHeader"] = cmPolicies::CMP0120;
diff --git a/Source/cmInstrumentation.cxx b/Source/cmInstrumentation.cxx
index 6dffc4c..2c4414f 100644
--- a/Source/cmInstrumentation.cxx
+++ b/Source/cmInstrumentation.cxx
@@ -20,10 +20,12 @@
 #include "cmCryptoHash.h"
 #include "cmExperimental.h"
 #include "cmInstrumentationQuery.h"
+#include "cmJSONState.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTimestamp.h"
 #include "cmUVProcessChain.h"
+#include "cmValue.h"
 
 cmInstrumentation::cmInstrumentation(std::string const& binary_dir)
 {
@@ -53,6 +55,75 @@
     this->hasQuery = this->hasQuery ||
       this->ReadJSONQueries(cmStrCat(this->userTimingDirv1, "/query"));
   }
+
+  std::string envVal;
+  if (cmSystemTools::GetEnv("CTEST_USE_INSTRUMENTATION", envVal) &&
+      !cmIsOff(envVal)) {
+    if (cmSystemTools::GetEnv("CTEST_EXPERIMENTAL_INSTRUMENTATION", envVal)) {
+      std::string const uuid = cmExperimental::DataForFeature(
+                                 cmExperimental::Feature::Instrumentation)
+                                 .Uuid;
+      if (envVal == uuid) {
+        this->AddHook(cmInstrumentationQuery::Hook::PrepareForCDash);
+        this->AddQuery(
+          cmInstrumentationQuery::Query::DynamicSystemInformation);
+        this->cdashDir = cmStrCat(this->timingDirv1, "/cdash");
+        cmSystemTools::MakeDirectory(this->cdashDir);
+        cmSystemTools::MakeDirectory(cmStrCat(this->cdashDir, "/configure"));
+        cmSystemTools::MakeDirectory(cmStrCat(this->cdashDir, "/build"));
+        cmSystemTools::MakeDirectory(
+          cmStrCat(this->cdashDir, "/build/commands"));
+        cmSystemTools::MakeDirectory(
+          cmStrCat(this->cdashDir, "/build/targets"));
+        cmSystemTools::MakeDirectory(cmStrCat(this->cdashDir, "/test"));
+        this->cdashSnippetsMap = { {
+                                     "configure",
+                                     "configure",
+                                   },
+                                   {
+                                     "generate",
+                                     "configure",
+                                   },
+                                   {
+                                     "compile",
+                                     "build",
+                                   },
+                                   {
+                                     "link",
+                                     "build",
+                                   },
+                                   {
+                                     "custom",
+                                     "build",
+                                   },
+                                   {
+                                     "build",
+                                     "skip",
+                                   },
+                                   {
+                                     "cmakeBuild",
+                                     "build",
+                                   },
+                                   {
+                                     "cmakeInstall",
+                                     "build",
+                                   },
+                                   {
+                                     "install",
+                                     "build",
+                                   },
+                                   {
+                                     "ctest",
+                                     "build",
+                                   },
+                                   {
+                                     "test",
+                                     "test",
+                                   } };
+        this->hasQuery = true;
+      }
+    }
+  }
 }
 
 bool cmInstrumentation::ReadJSONQueries(std::string const& directory)
@@ -211,6 +282,11 @@
                                     cmSystemTools::OUTPUT_PASSTHROUGH);
   }
 
+  // Special case for CDash collation
+  if (this->HasHook(cmInstrumentationQuery::Hook::PrepareForCDash)) {
+    this->PrepareDataForCDash(directory, index_path);
+  }
+
   // Delete files
   for (auto const& f : index["snippets"]) {
     cmSystemTools::RemoveFile(cmStrCat(directory, "/", f.asString()));
@@ -308,7 +384,7 @@
   ftmp.close();
 }
 
-int cmInstrumentation::InstrumentTest(
+std::string cmInstrumentation::InstrumentTest(
   std::string const& name, std::string const& command,
   std::vector<std::string> const& args, int64_t result,
   std::chrono::steady_clock::time_point steadyStart,
@@ -331,11 +407,11 @@
     this->InsertDynamicSystemInformation(root, "after");
   }
 
-  std::string const& file_name =
+  std::string file_name =
     cmStrCat("test-", this->ComputeSuffixHash(command_str),
              this->ComputeSuffixTime(), ".json");
   this->WriteInstrumentationJson(root, "data", file_name);
-  return 1;
+  return file_name;
 }
 
 void cmInstrumentation::GetPreTestStats()
@@ -547,3 +623,107 @@
   this->CollectTimingData(cmInstrumentationQuery::Hook::PostBuild);
   return ret;
 }
+
+void cmInstrumentation::AddHook(cmInstrumentationQuery::Hook hook)
+{
+  this->hooks.insert(hook);
+}
+
+void cmInstrumentation::AddQuery(cmInstrumentationQuery::Query query)
+{
+  this->queries.insert(query);
+}
+
+std::string const& cmInstrumentation::GetCDashDir()
+{
+  return this->cdashDir;
+}
+
+/** Copy the snippets referred to by an index file to a separate
+ * directory where they will be parsed for submission to CDash.
+ **/
+void cmInstrumentation::PrepareDataForCDash(std::string const& data_dir,
+                                            std::string const& index_path)
+{
+  Json::Value root;
+  std::string error_msg;
+  cmJSONState parseState = cmJSONState(index_path, &root);
+  if (!parseState.errors.empty()) {
+    cmSystemTools::Error(parseState.GetErrorMessage(true));
+    return;
+  }
+
+  if (!root.isObject()) {
+    error_msg =
+      cmStrCat("Expected index file ", index_path, " to contain an object");
+    cmSystemTools::Error(error_msg);
+    return;
+  }
+
+  if (!root.isMember("snippets")) {
+    error_msg = cmStrCat("Expected index file ", index_path,
+                         " to have a key 'snippets'");
+    cmSystemTools::Error(error_msg);
+    return;
+  }
+
+  std::string dst_dir;
+  Json::Value snippets = root["snippets"];
+  for (auto const& snippet : snippets) {
+    // Parse the role of this snippet.
+    std::string snippet_str = snippet.asString();
+    std::string snippet_path = cmStrCat(data_dir, '/', snippet_str);
+    Json::Value snippet_root;
+    parseState = cmJSONState(snippet_path, &snippet_root);
+    if (!parseState.errors.empty()) {
+      cmSystemTools::Error(parseState.GetErrorMessage(true));
+      continue;
+    }
+    if (!snippet_root.isObject()) {
+      error_msg = cmStrCat("Expected snippet file ", snippet_path,
+                           " to contain an object");
+      cmSystemTools::Error(error_msg);
+      continue;
+    }
+    if (!snippet_root.isMember("role")) {
+      error_msg = cmStrCat("Expected snippet file ", snippet_path,
+                           " to have a key 'role'");
+      cmSystemTools::Error(error_msg);
+      continue;
+    }
+
+    std::string snippet_role = snippet_root["role"].asString();
+    auto map_element = this->cdashSnippetsMap.find(snippet_role);
+    if (map_element == this->cdashSnippetsMap.end()) {
+      std::string message =
+        "Unexpected snippet type encountered: " + snippet_role;
+      cmSystemTools::Message(message, "Warning");
+      continue;
+    }
+
+    if (map_element->second == "skip") {
+      continue;
+    }
+
+    if (map_element->second == "build") {
+      // We organize snippets on a per-target basis (when possible)
+      // for Build.xml.
+      if (snippet_root.isMember("target")) {
+        dst_dir = cmStrCat(this->cdashDir, "/build/targets/",
+                           snippet_root["target"].asString());
+        cmSystemTools::MakeDirectory(dst_dir);
+      } else {
+        dst_dir = cmStrCat(this->cdashDir, "/build/commands");
+      }
+    } else {
+      dst_dir = cmStrCat(this->cdashDir, '/', map_element->second);
+    }
+
+    std::string dst = cmStrCat(dst_dir, '/', snippet_str);
+    cmsys::Status copied = cmSystemTools::CopyFileAlways(snippet_path, dst);
+    if (!copied) {
+      error_msg = cmStrCat("Failed to copy ", snippet_path, " to ", dst);
+      cmSystemTools::Error(error_msg);
+    }
+  }
+}
diff --git a/Source/cmInstrumentation.h b/Source/cmInstrumentation.h
index 3a2f9d9..0382495 100644
--- a/Source/cmInstrumentation.h
+++ b/Source/cmInstrumentation.h
@@ -29,11 +29,13 @@
     cm::optional<std::map<std::string, std::string>> arrayOptions =
       cm::nullopt,
     bool reloadQueriesAfterCommand = false);
-  int InstrumentTest(std::string const& name, std::string const& command,
-                     std::vector<std::string> const& args, int64_t result,
-                     std::chrono::steady_clock::time_point steadyStart,
-                     std::chrono::system_clock::time_point systemStart,
-                     std::string config);
+  std::string InstrumentTest(std::string const& name,
+                             std::string const& command,
+                             std::vector<std::string> const& args,
+                             int64_t result,
+                             std::chrono::steady_clock::time_point steadyStart,
+                             std::chrono::system_clock::time_point systemStart,
+                             std::string config);
   void GetPreTestStats();
   void LoadQueries();
   bool HasQuery() const;
@@ -49,7 +51,10 @@
   int CollectTimingData(cmInstrumentationQuery::Hook hook);
   int SpawnBuildDaemon();
   int CollectTimingAfterBuild(int ppid);
+  void AddHook(cmInstrumentationQuery::Hook hook);
+  void AddQuery(cmInstrumentationQuery::Query query);
   std::string errorMsg;
+  std::string const& GetCDashDir();
 
 private:
   void WriteInstrumentationJson(Json::Value& index,
@@ -66,13 +71,17 @@
   static std::string GetCommandStr(std::vector<std::string> const& args);
   static std::string ComputeSuffixHash(std::string const& command_str);
   static std::string ComputeSuffixTime();
+  void PrepareDataForCDash(std::string const& data_dir,
+                           std::string const& index_path);
   std::string binaryDir;
   std::string timingDirv1;
   std::string userTimingDirv1;
+  std::string cdashDir;
   std::set<cmInstrumentationQuery::Query> queries;
   std::set<cmInstrumentationQuery::Hook> hooks;
   std::vector<std::string> callbacks;
   std::vector<std::string> queryFiles;
+  std::map<std::string, std::string> cdashSnippetsMap;
   Json::Value preTestStats;
   bool hasQuery = false;
 };
diff --git a/Source/cmInstrumentationQuery.cxx b/Source/cmInstrumentationQuery.cxx
index bee8b37..88872f6 100644
--- a/Source/cmInstrumentationQuery.cxx
+++ b/Source/cmInstrumentationQuery.cxx
@@ -19,8 +19,9 @@
   "staticSystemInformation", "dynamicSystemInformation"
 };
 std::vector<std::string> const cmInstrumentationQuery::HookString{
-  "postGenerate",   "preBuild", "postBuild",   "preCMakeBuild",
-  "postCMakeBuild", "postTest", "postInstall", "manual"
+  "postGenerate",  "preBuild",        "postBuild",
+  "preCMakeBuild", "postCMakeBuild",  "postTest",
+  "postInstall",   "prepareForCDash", "manual"
 };
 
 namespace ErrorMessages {
diff --git a/Source/cmInstrumentationQuery.h b/Source/cmInstrumentationQuery.h
index 93a9caf..b8d8936 100644
--- a/Source/cmInstrumentationQuery.h
+++ b/Source/cmInstrumentationQuery.h
@@ -28,6 +28,7 @@
     PostCMakeBuild,
     PostTest,
     PostInstall,
+    PrepareForCDash,
     Manual
   };
   static std::vector<std::string> const HookString;
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 5f5f383..c244975 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -558,7 +558,12 @@
          WARN)                                                                \
   SELECT(POLICY, CMP0186,                                                     \
          "Regular expressions match ^ at most once in repeated searches.", 4, \
-         1, 0, WARN)
+         1, 0, WARN)                                                          \
+  SELECT(POLICY, CMP0187,                                                     \
+         "Include source file without an extension after the same name with " \
+         "an extension.",                                                     \
+         4, 1, 0, WARN)                                                       \
+  SELECT(POLICY, CMP0188, "The FindGCCXML module is removed.", 4, 1, 0, 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/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx
index b7b9770..c9ba19e 100644
--- a/Source/cmSourceFileLocation.cxx
+++ b/Source/cmSourceFileLocation.cxx
@@ -9,11 +9,19 @@
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
+#include "cmPolicies.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmake.h"
 
-cmSourceFileLocation::cmSourceFileLocation() = default;
+// if CMP0187 and CMP0115 are NEW, then we assume that source files that do not
+// include a file extension are not ambiguous but intentionally do not have an
+// extension.
+bool NoAmbiguousExtensions(cmMakefile const& makefile)
+{
+  return makefile.GetPolicyStatus(cmPolicies::CMP0115) == cmPolicies::NEW &&
+    makefile.GetPolicyStatus(cmPolicies::CMP0187) == cmPolicies::NEW;
+}
 
 cmSourceFileLocation::cmSourceFileLocation(cmSourceFileLocation const& loc)
   : Makefile(loc.Makefile)
@@ -30,7 +38,12 @@
   : Makefile(mf)
 {
   this->AmbiguousDirectory = !cmSystemTools::FileIsFullPath(name);
-  this->AmbiguousExtension = true;
+  // If ambiguous extensions are allowed then the extension is assumed to be
+  // ambiguous unless the name has an extension, in which case
+  // `UpdateExtension` will update this. If ambiguous extensions are not
+  // allowed, then set this to false as the file extension must be provided or
+  // the file doesn't have an extension.
+  this->AmbiguousExtension = !NoAmbiguousExtensions(*mf);
   this->Directory = cmSystemTools::GetFilenamePath(name);
   if (cmSystemTools::FileIsFullPath(this->Directory)) {
     this->Directory = cmSystemTools::CollapseFullPath(this->Directory);
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index f923ba6..19ce9f6 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -251,15 +251,7 @@
   std::string output;
   if (re.find(input)) {
     status.GetMakefile().StoreMatches(re);
-    std::string::size_type l = re.start();
-    std::string::size_type r = re.end();
-    if (r - l == 0) {
-      std::string e = "sub-command REGEX, mode MATCH regex \"" + regex +
-        "\" matched an empty string.";
-      status.SetError(e);
-      return false;
-    }
-    output = input.substr(l, r - l);
+    output = re.match();
   }
 
   // Store the output in the provided variable.
@@ -298,22 +290,24 @@
   // Scan through the input for all matches.
   std::string output;
   std::string::size_type base = 0;
-  while (re.find(input, base, optAnchor)) {
+  unsigned optNonEmpty = 0;
+  while (re.find(input, base, optAnchor | optNonEmpty)) {
     status.GetMakefile().ClearMatches();
     status.GetMakefile().StoreMatches(re);
-    std::string::size_type l = re.start();
-    std::string::size_type r = re.end();
-    if (r - l == 0) {
-      std::string e = "sub-command REGEX, mode MATCHALL regex \"" + regex +
-        "\" matched an empty string.";
-      status.SetError(e);
-      return false;
-    }
-    if (!output.empty()) {
+    if (!output.empty() || optNonEmpty) {
       output += ";";
     }
     output += re.match();
-    base = r;
+    base = re.end();
+
+    if (re.start() == input.length()) {
+      break;
+    }
+    if (re.start() == re.end()) {
+      optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
+    } else {
+      optNonEmpty = 0;
+    }
   }
 
   // Store the output in the provided variable.
diff --git a/Source/cmStringReplaceHelper.cxx b/Source/cmStringReplaceHelper.cxx
index 5cd159e..025dfc7 100644
--- a/Source/cmStringReplaceHelper.cxx
+++ b/Source/cmStringReplaceHelper.cxx
@@ -33,25 +33,17 @@
   }
 
   // Scan through the input for all matches.
+  auto& re = this->RegularExpression;
   std::string::size_type base = 0;
-  while (this->RegularExpression.find(input, base, optAnchor)) {
+  unsigned optNonEmpty = 0;
+  while (re.find(input, base, optAnchor | optNonEmpty)) {
     if (this->Makefile) {
       this->Makefile->ClearMatches();
-      this->Makefile->StoreMatches(this->RegularExpression);
+      this->Makefile->StoreMatches(re);
     }
-    auto l2 = this->RegularExpression.start();
-    auto r = this->RegularExpression.end();
 
     // Concatenate the part of the input that was not matched.
-    output += input.substr(base, l2 - base);
-
-    // Make sure the match had some text.
-    if (r - l2 == 0) {
-      std::ostringstream error;
-      error << "regex \"" << this->RegExString << "\" matched an empty string";
-      this->ErrorString = error.str();
-      return false;
-    }
+    output += input.substr(base, re.start() - base);
 
     // Concatenate the replacement for the match.
     for (auto const& replacement : this->Replacements) {
@@ -61,10 +53,7 @@
       } else {
         // Replace with part of the match.
         auto n = replacement.Number;
-        auto start = this->RegularExpression.start(n);
-        if (start != std::string::npos) {
-          output += this->RegularExpression.match(n);
-        } else {
+        if (n > re.num_groups()) {
           std::ostringstream error;
           error << "replace expression \"" << this->ReplaceExpression
                 << "\" contains an out-of-range escape for regex \""
@@ -72,11 +61,21 @@
           this->ErrorString = error.str();
           return false;
         }
+        output += re.match(n);
       }
     }
 
     // Move past the match.
-    base = r;
+    base = re.end();
+
+    if (re.start() == input.length()) {
+      break;
+    }
+    if (re.start() == re.end()) {
+      optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
+    } else {
+      optNonEmpty = 0;
+    }
   }
 
   // Concatenate the text after the last match.
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 78d4655..f880884 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -2624,7 +2624,7 @@
     cmSystemTools::Error(this->Instrumentation->errorMsg);
     return 1;
   }
-  std::function<int()> doConfigure = [this]() -> int {
+  auto doConfigure = [this]() -> int {
     this->GlobalGenerator->Configure();
     return 0;
   };
@@ -3023,7 +3023,7 @@
   auto startTime = std::chrono::steady_clock::now();
 #if !defined(CMAKE_BOOTSTRAP)
   auto profilingRAII = this->CreateProfilingEntry("project", "generate");
-  std::function<int()> doGenerate = [this]() -> int {
+  auto doGenerate = [this]() -> int {
     if (!this->GlobalGenerator->Compute()) {
       return -1;
     }
@@ -3720,7 +3720,8 @@
 int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
                  std::string config, std::vector<std::string> nativeOptions,
                  cmBuildOptions& buildOptions, bool verbose,
-                 std::string const& presetName, bool listPresets)
+                 std::string const& presetName, bool listPresets,
+                 std::vector<std::string> const& args)
 {
   this->SetHomeDirectory("");
   this->SetHomeOutputDirectory("");
@@ -3959,16 +3960,38 @@
     return 1;
   }
 
+#if !defined(CMAKE_BOOTSTRAP)
+  cmInstrumentation instrumentation(dir);
+  if (!instrumentation.errorMsg.empty()) {
+    cmSystemTools::Error(instrumentation.errorMsg);
+    return 1;
+  }
+  instrumentation.CollectTimingData(
+    cmInstrumentationQuery::Hook::PreCMakeBuild);
+#endif
+
   this->GlobalGenerator->PrintBuildCommandAdvice(std::cerr, jobs);
   std::stringstream ostr;
   // `cmGlobalGenerator::Build` logs metadata about what directory and commands
   // are being executed to the `output` parameter. If CMake is verbose, print
   // this out.
   std::ostream& verbose_ostr = verbose ? std::cout : ostr;
-  int buildresult = this->GlobalGenerator->Build(
-    jobs, "", dir, projName, targets, verbose_ostr, "", config, buildOptions,
-    verbose, cmDuration::zero(), cmSystemTools::OUTPUT_PASSTHROUGH,
-    nativeOptions);
+  auto doBuild = [this, jobs, dir, projName, targets, &verbose_ostr, config,
+                  buildOptions, verbose, nativeOptions]() -> int {
+    return this->GlobalGenerator->Build(
+      jobs, "", dir, projName, targets, verbose_ostr, "", config, buildOptions,
+      verbose, cmDuration::zero(), cmSystemTools::OUTPUT_PASSTHROUGH,
+      nativeOptions);
+  };
+
+#if !defined(CMAKE_BOOTSTRAP)
+  int buildresult =
+    instrumentation.InstrumentCommand("cmakeBuild", args, doBuild);
+  instrumentation.CollectTimingData(
+    cmInstrumentationQuery::Hook::PostCMakeBuild);
+#else
+  int buildresult = doBuild();
+#endif
 
   return buildresult;
 }
diff --git a/Source/cmake.h b/Source/cmake.h
index 49231f4..829bb7c 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -640,7 +640,8 @@
   int Build(int jobs, std::string dir, std::vector<std::string> targets,
             std::string config, std::vector<std::string> nativeOptions,
             cmBuildOptions& buildOptions, bool verbose,
-            std::string const& presetName, bool listPresets);
+            std::string const& presetName, bool listPresets,
+            std::vector<std::string> const& args);
 
   //! run the --open option
   bool Open(std::string const& dir, bool dryRun);
diff --git a/Source/cmakemain.cxx b/Source/cmakemain.cxx
index 80a0ce8..60b59ad 100644
--- a/Source/cmakemain.cxx
+++ b/Source/cmakemain.cxx
@@ -705,27 +705,12 @@
     cmakemainProgressCallback(msg, prog, &cm);
   });
 
-  cmInstrumentation instrumentation(dir);
-  if (!instrumentation.errorMsg.empty()) {
-    cmSystemTools::Error(instrumentation.errorMsg);
-    return 1;
-  }
   cmBuildOptions buildOptions(cleanFirst, false, resolveMode);
-  std::function<int()> doBuild = [&cm, &jobs, &dir, &targets, &config,
-                                  &nativeOptions, &buildOptions, &verbose,
-                                  &presetName, &listPresets]() {
-    return cm.Build(jobs, dir, std::move(targets), std::move(config),
-                    std::move(nativeOptions), buildOptions, verbose,
-                    presetName, listPresets);
-  };
-  instrumentation.CollectTimingData(
-    cmInstrumentationQuery::Hook::PreCMakeBuild);
   std::vector<std::string> cmd;
   cm::append(cmd, av, av + ac);
-  int ret = instrumentation.InstrumentCommand("cmakeBuild", cmd, doBuild);
-  instrumentation.CollectTimingData(
-    cmInstrumentationQuery::Hook::PostCMakeBuild);
-  return ret;
+  return cm.Build(jobs, dir, std::move(targets), std::move(config),
+                  std::move(nativeOptions), buildOptions, verbose, presetName,
+                  listPresets, cmd);
 #endif
 }
 
@@ -986,8 +971,7 @@
     }
   }
 
-  std::function<int()> doInstall = [&handler, &verbose, &jobs,
-                                    &instrumentation]() -> int {
+  auto doInstall = [&handler, &verbose, &jobs, &instrumentation]() -> int {
     int ret_ = 0;
     if (handler.IsParallel()) {
       ret_ = handler.Install(jobs, instrumentation);
diff --git a/Tests/CMakeTests/StringTest.cmake.in b/Tests/CMakeTests/StringTest.cmake.in
index 6a94cc5..ca2ee02 100644
--- a/Tests/CMakeTests/StringTest.cmake.in
+++ b/Tests/CMakeTests/StringTest.cmake.in
@@ -84,7 +84,7 @@
 # Execute each test listed in StringTestScript.cmake:
 #
 set(scriptname "@CMAKE_CURRENT_SOURCE_DIR@/StringTestScript.cmake")
-set(number_of_tests_expected 72)
+set(number_of_tests_expected 70)
 
 include("@CMAKE_CURRENT_SOURCE_DIR@/ExecuteScriptTests.cmake")
 execute_all_script_tests(${scriptname} number_of_tests_executed)
diff --git a/Tests/CMakeTests/StringTestScript.cmake b/Tests/CMakeTests/StringTestScript.cmake
index 7c45857..84b404c 100644
--- a/Tests/CMakeTests/StringTestScript.cmake
+++ b/Tests/CMakeTests/StringTestScript.cmake
@@ -73,9 +73,6 @@
 elseif(testname STREQUAL regex_match_bad_regex) # fail
   string(REGEX MATCH "(.*" v input)
 
-elseif(testname STREQUAL regex_match_empty_string) # fail
-  string(REGEX MATCH "x*" v "")
-
 elseif(testname STREQUAL regex_match_no_match) # pass
   string(REGEX MATCH "xyz" v "abc")
   message(STATUS "v='${v}'")
@@ -87,9 +84,6 @@
 elseif(testname STREQUAL regex_matchall_bad_regex) # fail
   string(REGEX MATCHALL "(.*" v input)
 
-elseif(testname STREQUAL regex_matchall_empty_string) # fail
-  string(REGEX MATCHALL "x*" v "")
-
 elseif(testname STREQUAL regex_replace_ends_with_backslash) # fail
   string(REGEX REPLACE "input" "output\\" v input1 input2 input3 input4)
 
@@ -107,15 +101,16 @@
 elseif(testname STREQUAL regex_replace_bad_regex) # fail
   string(REGEX REPLACE "this (.*" "with that" v input)
 
-elseif(testname STREQUAL regex_replace_empty_string) # fail
-  string(REGEX REPLACE "x*" "that" v "")
-
 elseif(testname STREQUAL regex_replace_index_too_small) # fail
   string(REGEX REPLACE "^this (.*)$" "with \\1 \\-1" v "this input")
 
 elseif(testname STREQUAL regex_replace_index_too_large) # fail
   string(REGEX REPLACE "^this (.*)$" "with \\1 \\2" v "this input")
 
+elseif(testname STREQUAL regex_replace_index_no_match) # pass
+  string(REGEX REPLACE "^(this (.*)|(that .*))$" "with \\1 \\2 \\3" v "this input")
+  message(STATUS "v='${v}'")
+
 elseif(testname STREQUAL compare_no_mode) # fail
   string(COMPARE)
 
diff --git a/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-build-result.txt b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-build-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-build-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt
new file mode 100644
index 0000000..ac1885a
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Deprecation Warning at CMP0187-NEW-CMP0115-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0115 will be removed from a future version
+  of CMake\.
diff --git a/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD.cmake b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD.cmake
new file mode 100644
index 0000000..ee45dfb
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-NEW-CMP0115-OLD.cmake
@@ -0,0 +1,4 @@
+cmake_policy(SET CMP0115 OLD)
+cmake_policy(SET CMP0187 NEW)
+
+include(CMP0187.cmake)
diff --git a/Tests/RunCMake/CMP0187/CMP0187-NEW-build-result.txt b/Tests/RunCMake/CMP0187/CMP0187-NEW-build-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-NEW-build-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMP0187/CMP0187-NEW.cmake b/Tests/RunCMake/CMP0187/CMP0187-NEW.cmake
new file mode 100644
index 0000000..a140325
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-NEW.cmake
@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0187 NEW)
+
+include(CMP0187.cmake)
diff --git a/Tests/RunCMake/CMP0187/CMP0187-OLD-build-result.txt b/Tests/RunCMake/CMP0187/CMP0187-OLD-build-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-OLD-build-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMP0187/CMP0187-OLD.cmake b/Tests/RunCMake/CMP0187/CMP0187-OLD.cmake
new file mode 100644
index 0000000..d48b4bb
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187-OLD.cmake
@@ -0,0 +1,3 @@
+cmake_policy(SET CMP0187 OLD)
+
+include(CMP0187.cmake)
diff --git a/Tests/RunCMake/CMP0187/CMP0187.cmake b/Tests/RunCMake/CMP0187/CMP0187.cmake
new file mode 100644
index 0000000..029d0c9
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMP0187.cmake
@@ -0,0 +1,4 @@
+add_custom_command(OUTPUT z.h COMMAND ${CMAKE_COMMAND} -E true)
+add_custom_command(OUTPUT z COMMAND ${CMAKE_COMMAND} -E false)
+
+add_library(lib INTERFACE z z.h)
diff --git a/Tests/RunCMake/CMP0187/CMakeLists.txt b/Tests/RunCMake/CMP0187/CMakeLists.txt
new file mode 100644
index 0000000..7e8fde0
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 4.0)
+
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0187/RunCMakeTest.cmake b/Tests/RunCMake/CMP0187/RunCMakeTest.cmake
new file mode 100644
index 0000000..f28cc70
--- /dev/null
+++ b/Tests/RunCMake/CMP0187/RunCMakeTest.cmake
@@ -0,0 +1,34 @@
+include(RunCMake)
+
+block()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0187-NEW-build)
+  run_cmake_with_options(CMP0187-NEW "-DCMAKE_POLICY_DEFAULT_CMP0187=NEW")
+
+  if(RunCMake_GENERATOR MATCHES "Ninja.*")
+    set(RunCMake_TEST_NO_CLEAN 1)
+    # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command
+    run_cmake_command(CMP0187-NEW-build ${CMAKE_COMMAND} --build .)
+  endif()
+endblock()
+
+block()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0187-OLD-build)
+  run_cmake_with_options(CMP0187-OLD "-DCMAKE_POLICY_DEFAULT_CMP0187=OLD")
+
+  if(RunCMake_GENERATOR MATCHES "Ninja.*")
+    set(RunCMake_TEST_NO_CLEAN 1)
+    # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command
+    run_cmake_command(CMP0187-OLD-build ${CMAKE_COMMAND} --build .)
+  endif()
+endblock()
+
+block()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/CMP0187-NEW-CMP0115-OLD-build)
+  run_cmake(CMP0187-NEW-CMP0115-OLD)
+
+  if(RunCMake_GENERATOR MATCHES "Ninja.*")
+    set(RunCMake_TEST_NO_CLEAN 1)
+    # -n: dry-run to avoid actually compiling, -v: verbose to capture executed command
+    run_cmake_command(CMP0187-NEW-CMP0115-OLD-build ${CMAKE_COMMAND} --build .)
+  endif()
+endblock()
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index e664ca3..fdf26ce 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -179,6 +179,7 @@
 add_RunCMake_test(CMP0170)
 add_RunCMake_test(CMP0171)
 add_RunCMake_test(CMP0173)
+add_RunCMake_test(CMP0187)
 
 # The test for Policy 65 requires the use of the
 # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
@@ -396,6 +397,7 @@
 add_RunCMake_test(CompilerTest ${CMake_TEST_LANG_VARS})
 set_property(TEST RunCMake.CompilerTest APPEND PROPERTY LABELS "CUDA" "HIP" "ISPC" "Fortran")
 add_RunCMake_test(Configure -DMSVC_IDE=${MSVC_IDE})
+add_RunCMake_test(CpsExportImport)
 add_RunCMake_test(DisallowedCommands)
 if("${CMAKE_GENERATOR}" MATCHES "Unix Makefiles|Ninja")
   add_RunCMake_test(ExportCompileCommands)
@@ -605,6 +607,9 @@
 add_RunCMake_test(ctest_environment)
 add_RunCMake_test(ctest_empty_binary_directory)
 add_RunCMake_test(ctest_fixtures)
+if(CMAKE_GENERATOR MATCHES "Make|Ninja")
+  add_RunCMake_test(ctest_instrumentation)
+endif()
 add_RunCMake_test(define_property)
 add_RunCMake_test(file -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
 add_RunCMake_test(file-CHMOD -DMSYS=${MSYS})
diff --git a/Tests/RunCMake/CMakeRelease/FileTable-stdout.txt b/Tests/RunCMake/CMakeRelease/FileTable-stdout.txt
index 2960eeb..b8e2639 100644
--- a/Tests/RunCMake/CMakeRelease/FileTable-stdout.txt
+++ b/Tests/RunCMake/CMakeRelease/FileTable-stdout.txt
@@ -13,6 +13,10 @@
 "cmake-@version@-macos-universal\.tar\.gz"
 "cmake-@version@-macos10\.10-universal\.dmg"
 "cmake-@version@-macos10\.10-universal\.tar\.gz"
+"cmake-@version@-sunos-i386\.sh"
+"cmake-@version@-sunos-i386\.tar\.gz"
+"cmake-@version@-sunos-sparc\.sh"
+"cmake-@version@-sunos-sparc\.tar\.gz"
 "cmake-@version@-windows-i386\.msi"
 "cmake-@version@-windows-i386\.zip"
 "cmake-@version@-windows-x86_64\.msi"
@@ -28,6 +32,8 @@
 "cmake-@version@-macos-universal\.dmg"
 -- query: \.files\[\] \| select\(\(\.os\[\] \| \. == "macos10\.10"\) and \(\.class == "archive"\)\) \| \.name
 "cmake-@version@-macos10\.10-universal\.tar\.gz"
+-- query: \.files\[\] \| select\(\(\.os\[\] \| \. == "sunos"\) and \(\.architecture\[\] \| \. == "sparc"\) and \(\.class == "archive"\)\) \| \.name
+"cmake-@version@-sunos-sparc\.tar\.gz"
 -- query: \.files\[\] \| select\(\(\.os\[\] \| \. == "windows"\) and \(\.architecture\[\] \| \. == "i386"\) and \(\.class == "installer"\)\) \| \.name
 "cmake-@version@-windows-i386\.msi"
 -- query: \.files\[\] \| select\(\.architecture\[\] \| \. == "x86_64"\) \| \.name
diff --git a/Tests/RunCMake/CMakeRelease/FileTable.cmake b/Tests/RunCMake/CMakeRelease/FileTable.cmake
index f46535c..a579457 100644
--- a/Tests/RunCMake/CMakeRelease/FileTable.cmake
+++ b/Tests/RunCMake/CMakeRelease/FileTable.cmake
@@ -11,6 +11,7 @@
     ".files[] | select(.os[] | . == \"source\") | .name"
     ".files[] | select((.os[] | . == \"macOS\") and (.class == \"volume\")) | .name"
     ".files[] | select((.os[] | . == \"macos10.10\") and (.class == \"archive\")) | .name"
+    ".files[] | select((.os[] | . == \"sunos\") and (.architecture[] | . == \"sparc\") and (.class == \"archive\")) | .name"
     ".files[] | select((.os[] | . == \"windows\") and (.architecture[] | . == \"i386\") and (.class == \"installer\")) | .name"
     ".files[] | select(.architecture[] | . == \"x86_64\") | .name"
     ".files[] | select([.macOSmin] | inside([\"10.10\", \"10.11\", \"10.12\"])) | .name"
diff --git a/Tests/RunCMake/CpsExportImport/CMakeLists.txt b/Tests/RunCMake/CpsExportImport/CMakeLists.txt
new file mode 100644
index 0000000..955802c
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 4.0)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CpsExportImport/RunCMakeTest.cmake b/Tests/RunCMake/CpsExportImport/RunCMakeTest.cmake
new file mode 100644
index 0000000..dff26eb
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/RunCMakeTest.cmake
@@ -0,0 +1,24 @@
+include(RunCMake)
+
+set(RunCMake_TEST_OPTIONS
+  -Wno-dev
+  "-DCMAKE_EXPERIMENTAL_EXPORT_PACKAGE_INFO:STRING=b80be207-778e-46ba-8080-b23bba22639e"
+  "-DCMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES:STRING=e82e467b-f997-4464-8ace-b00808fff261"
+  )
+
+function(build_project test)
+  set(RunCMake_TEST_NO_CLEAN FALSE)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
+  endif()
+
+  run_cmake(${test})
+
+  set(RunCMake_TEST_NO_CLEAN TRUE)
+  run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Release)
+  run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --config Release)
+endfunction()
+
+build_project(TestLibrary)
+build_project(TestExecutable)
diff --git a/Tests/RunCMake/CpsExportImport/TestExecutable.cmake b/Tests/RunCMake/CpsExportImport/TestExecutable.cmake
new file mode 100644
index 0000000..cced129
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/TestExecutable.cmake
@@ -0,0 +1,12 @@
+project(TestLibrary C)
+
+set(CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}/../install")
+set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/../install")
+
+find_package(libb REQUIRED COMPONENTS libb)
+
+add_executable(app app.c)
+
+target_link_libraries(app PUBLIC libb::libb)
+
+install(TARGETS app DESTINATION bin)
diff --git a/Tests/RunCMake/CpsExportImport/TestLibrary.cmake b/Tests/RunCMake/CpsExportImport/TestLibrary.cmake
new file mode 100644
index 0000000..a984f2b
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/TestLibrary.cmake
@@ -0,0 +1,14 @@
+project(TestLibrary C)
+
+set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/../install")
+
+add_library(liba SHARED liba.c)
+add_library(libb SHARED libb.c)
+
+target_link_libraries(libb PUBLIC liba)
+
+install(TARGETS liba EXPORT liba DESTINATION lib)
+install(PACKAGE_INFO liba DESTINATION cps EXPORT liba)
+
+install(TARGETS libb EXPORT libb DESTINATION lib)
+install(PACKAGE_INFO libb DESTINATION cps EXPORT libb)
diff --git a/Tests/RunCMake/CpsExportImport/app.c b/Tests/RunCMake/CpsExportImport/app.c
new file mode 100644
index 0000000..9ad7829
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/app.c
@@ -0,0 +1,13 @@
+#include <stdio.h>
+
+extern
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+int ask(void);
+
+int main(void)
+{
+  printf("%i\n", ask());
+  return 0;
+}
diff --git a/Tests/RunCMake/CpsExportImport/liba.c b/Tests/RunCMake/CpsExportImport/liba.c
new file mode 100644
index 0000000..a2e2267
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/liba.c
@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int answer(void)
+{
+  return 42;
+}
diff --git a/Tests/RunCMake/CpsExportImport/libb.c b/Tests/RunCMake/CpsExportImport/libb.c
new file mode 100644
index 0000000..8ba04af
--- /dev/null
+++ b/Tests/RunCMake/CpsExportImport/libb.c
@@ -0,0 +1,13 @@
+extern
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+int answer(void);
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int ask(void)
+{
+  return answer();
+}
diff --git a/Tests/RunCMake/Instrumentation/check-data-dir.cmake b/Tests/RunCMake/Instrumentation/check-data-dir.cmake
index f2e8235..a20d2cd 100644
--- a/Tests/RunCMake/Instrumentation/check-data-dir.cmake
+++ b/Tests/RunCMake/Instrumentation/check-data-dir.cmake
@@ -107,6 +107,15 @@
       snippet_error(${snippet} "Unexpected config: ${config}")
     endif()
   endif()
+
+  # Verify command args were passed
+  if (filename MATCHES "^cmakeBuild|^ctest")
+    string(JSON command GET "${contents}" command)
+    if (NOT command MATCHES "-.* Debug")
+      snippet_error(${snippet} "Command value missing passed arguments")
+    endif()
+  endif()
+
 endforeach()
 
 # Verify that listed snippets match expected roles
diff --git a/Tests/RunCMake/Instrumentation/verify-snippet.cmake b/Tests/RunCMake/Instrumentation/verify-snippet.cmake
index ec997e3..33f9414 100644
--- a/Tests/RunCMake/Instrumentation/verify-snippet.cmake
+++ b/Tests/RunCMake/Instrumentation/verify-snippet.cmake
@@ -49,7 +49,6 @@
     has_key("${snippet}" "${contents}" language)
     has_key("${snippet}" "${contents}" config)
   elseif (filename MATCHES "^custom-*")
-    has_key("${snippet}" "${contents}" target)
     has_key("${snippet}" "${contents}" outputs)
     has_key("${snippet}" "${contents}" outputSizes)
   elseif (filename MATCHES "^test-*")
diff --git a/Tests/RunCMake/PackageInfo/Requirements-check.cmake b/Tests/RunCMake/PackageInfo/Requirements-check.cmake
index 59a212f..a082355 100644
--- a/Tests/RunCMake/PackageInfo/Requirements-check.cmake
+++ b/Tests/RunCMake/PackageInfo/Requirements-check.cmake
@@ -8,8 +8,10 @@
 
 file(READ "${out_dir}/bar.cps" content)
 expect_value("${content}" "bar" "name")
-expect_null("${content}" "requires" "foo")
-expect_null("${content}" "requires" "test")
+expect_array("${content}"      1 "requires"  "foo" "components")
+expect_value("${content}" "libb" "requires"  "foo" "components" 0)
+expect_array("${content}"      1 "requires" "test" "components")
+expect_value("${content}" "liba" "requires" "test" "components" 0)
 expect_value("${content}" "interface" "components" "libc" "type")
 expect_value("${content}" "interface" "components" "libd" "type")
 
diff --git a/Tests/RunCMake/ctest_instrumentation/CMakeLists.txt.in b/Tests/RunCMake/ctest_instrumentation/CMakeLists.txt.in
new file mode 100644
index 0000000..057f708
--- /dev/null
+++ b/Tests/RunCMake/ctest_instrumentation/CMakeLists.txt.in
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.10)
+@CASE_CMAKELISTS_PREFIX_CODE@
+project(CTestInstrumentation@CASE_NAME@)
+if(USE_INSTRUMENTATION)
+  set(CMAKE_EXPERIMENTAL_INSTRUMENTATION "a37d1069-1972-4901-b9c9-f194aaf2b6e0")
+endif()
+include(CTest)
+add_executable(main main.c)
+add_test(NAME main COMMAND main)
+@CASE_CMAKELISTS_SUFFIX_CODE@
diff --git a/Tests/RunCMake/ctest_instrumentation/InstrumentationInCTestXML-check.cmake b/Tests/RunCMake/ctest_instrumentation/InstrumentationInCTestXML-check.cmake
new file mode 100644
index 0000000..643515b
--- /dev/null
+++ b/Tests/RunCMake/ctest_instrumentation/InstrumentationInCTestXML-check.cmake
@@ -0,0 +1,41 @@
+foreach(xml_type Configure Build Test)
+  file(GLOB xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/${xml_type}.xml")
+  if(xml_file)
+    file(READ "${xml_file}" xml_content)
+    if(NOT xml_content MATCHES "AfterHostMemoryUsed")
+      set(RunCMake_TEST_FAILED "'AfterHostMemoryUsed' not found in ${xml_type}.xml")
+    endif()
+    if(NOT xml_type STREQUAL "Test")
+      if(NOT xml_content MATCHES "<Commands>")
+        set(RunCMake_TEST_FAILED "<Commands> element not found in ${xml_type}.xml")
+      endif()
+    endif()
+    if (xml_type STREQUAL "Build")
+      if(NOT xml_content MATCHES "<Targets>")
+        set(RunCMake_TEST_FAILED "<Targets> element not found in Build.xml")
+      endif()
+      if(NOT xml_content MATCHES "<Target name=\"main\" type=\"EXECUTABLE\">")
+        set(RunCMake_TEST_FAILED "<Target> element for 'main' not found in Build.xml")
+      endif()
+      if(NOT xml_content MATCHES "<Compile")
+        set(RunCMake_TEST_FAILED "<Compile> element not found in Build.xml")
+      endif()
+      if(NOT xml_content MATCHES "<Link")
+        set(RunCMake_TEST_FAILED "<Link> element not found in Build.xml")
+      endif()
+      if(NOT xml_content MATCHES "<CmakeBuild")
+        set(RunCMake_TEST_FAILED "<CmakeBuild> element not found in Build.xml")
+      endif()
+    endif()
+  else()
+    set(RunCMake_TEST_FAILED "${xml_type}.xml not found")
+  endif()
+endforeach()
+
+foreach(dir_to_check "configure" "test" "build/targets" "build/commands")
+  file(GLOB leftover_cdash_snippets
+    "${RunCMake_TEST_BINARY_DIR}/.cmake/instrumentation-a37d1069-1972-4901-b9c9-f194aaf2b6e0/v1/cdash/${dir_to_check}/*")
+  if(leftover_cdash_snippets)
+    set(RunCMake_TEST_FAILED "Leftover snippets found in cdash dir: ${leftover_cdash_snippets}")
+  endif()
+endforeach()
diff --git a/Tests/RunCMake/ctest_instrumentation/NoInstrumentationInCTestXML-check.cmake b/Tests/RunCMake/ctest_instrumentation/NoInstrumentationInCTestXML-check.cmake
new file mode 100644
index 0000000..f026f76
--- /dev/null
+++ b/Tests/RunCMake/ctest_instrumentation/NoInstrumentationInCTestXML-check.cmake
@@ -0,0 +1,11 @@
+foreach(xml_type Configure Build Test)
+  file(GLOB xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/${xml_type}.xml")
+  if(xml_file)
+    file(READ "${xml_file}" xml_content)
+    if(xml_content MATCHES "AfterHostMemoryUsed")
+      set(RunCMake_TEST_FAILED "'AfterHostMemoryUsed' found in ${xml_type}.xml")
+    endif()
+  else()
+    set(RunCMake_TEST_FAILED "${xml_type}.xml not found")
+  endif()
+endforeach()
diff --git a/Tests/RunCMake/ctest_instrumentation/RunCMakeTest.cmake b/Tests/RunCMake/ctest_instrumentation/RunCMakeTest.cmake
new file mode 100644
index 0000000..7424b17
--- /dev/null
+++ b/Tests/RunCMake/ctest_instrumentation/RunCMakeTest.cmake
@@ -0,0 +1,22 @@
+include(RunCTest)
+
+function(run_InstrumentationInCTestXML USE_INSTRUMENTATION)
+  if(USE_INSTRUMENTATION)
+    set(ENV{CTEST_USE_INSTRUMENTATION} "1")
+    set(ENV{CTEST_EXPERIMENTAL_INSTRUMENTATION} "a37d1069-1972-4901-b9c9-f194aaf2b6e0")
+    set(RunCMake_USE_INSTRUMENTATION TRUE)
+    set(CASE_NAME InstrumentationInCTestXML)
+  else()
+    set(ENV{CTEST_USE_INSTRUMENTATION} "0")
+    set(ENV{CTEST_EXPERIMENTAL_INSTRUMENTATION} "0")
+    set(RunCMake_USE_INSTRUMENTATION FALSE)
+    set(CASE_NAME NoInstrumentationInCTestXML)
+  endif()
+  configure_file(${RunCMake_SOURCE_DIR}/main.c
+                 ${RunCMake_BINARY_DIR}/${CASE_NAME}/main.c COPYONLY)
+  run_ctest("${CASE_NAME}")
+  unset(RunCMake_USE_LAUNCHERS)
+  unset(RunCMake_USE_INSTRUMENTATION)
+endfunction()
+run_InstrumentationInCTestXML(ON)
+run_InstrumentationInCTestXML(OFF)
diff --git a/Tests/RunCMake/ctest_instrumentation/main.c b/Tests/RunCMake/ctest_instrumentation/main.c
new file mode 100644
index 0000000..8488f4e
--- /dev/null
+++ b/Tests/RunCMake/ctest_instrumentation/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/ctest_instrumentation/test.cmake.in b/Tests/RunCMake/ctest_instrumentation/test.cmake.in
new file mode 100644
index 0000000..41b9612
--- /dev/null
+++ b/Tests/RunCMake/ctest_instrumentation/test.cmake.in
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(CTEST_SITE                          "test-site")
+set(CTEST_BUILD_NAME                    "test-build-name")
+set(CTEST_SOURCE_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@")
+set(CTEST_BINARY_DIRECTORY              "@RunCMake_BINARY_DIR@/@CASE_NAME@-build")
+set(CTEST_CMAKE_GENERATOR               "@RunCMake_GENERATOR@")
+set(CTEST_CMAKE_GENERATOR_PLATFORM      "@RunCMake_GENERATOR_PLATFORM@")
+set(CTEST_CMAKE_GENERATOR_TOOLSET       "@RunCMake_GENERATOR_TOOLSET@")
+set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
+set(CTEST_USE_LAUNCHERS                 TRUE)
+set(CTEST_USE_INSTRUMENTATION           "@RunCMake_USE_INSTRUMENTATION@")
+
+ctest_start(Experimental)
+ctest_configure(OPTIONS "-DUSE_INSTRUMENTATION=${CTEST_USE_INSTRUMENTATION}")
+ctest_build()
+ctest_test()
diff --git a/Tests/RunCMake/find_package/CMP0188-NEW-stderr.txt b/Tests/RunCMake/find_package/CMP0188-NEW-stderr.txt
new file mode 100644
index 0000000..bf62647
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0188-NEW-stderr.txt
@@ -0,0 +1,4 @@
+^CMake Warning at CMP0188-NEW\.cmake:[0-9]+ \(find_package\):
+  No "FindGCCXML\.cmake" found in CMAKE_MODULE_PATH\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/CMP0188-NEW.cmake b/Tests/RunCMake/find_package/CMP0188-NEW.cmake
new file mode 100644
index 0000000..940169d
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0188-NEW.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0188 NEW)
+set(_FindGCCXML_testing TRUE)
+find_package(GCCXML MODULE)
+
+if(_FindGCCXML_included)
+  message(FATAL_ERROR "FindGCCXML.cmake erroneously included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0188-OLD.cmake b/Tests/RunCMake/find_package/CMP0188-OLD.cmake
new file mode 100644
index 0000000..dc48c43
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0188-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0188 OLD)
+set(_FindGCCXML_testing TRUE)
+find_package(GCCXML MODULE)
+
+if(NOT _FindGCCXML_included)
+  message(FATAL_ERROR "FindGCCXML.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt b/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt
new file mode 100644
index 0000000..de3b9f8
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0188-WARN-stderr.txt
@@ -0,0 +1,8 @@
+CMake Warning \(dev\) at CMP0188-WARN\.cmake:[0-9]+ \(find_package\):
+  Policy CMP0188 is not set: The FindGCCXML module is removed\.  Run "cmake
+  --help-policy CMP0188" for policy details\.  Use the cmake_policy command to
+  set the policy and suppress this warning\.
+
+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/find_package/CMP0188-WARN.cmake b/Tests/RunCMake/find_package/CMP0188-WARN.cmake
new file mode 100644
index 0000000..5931da5
--- /dev/null
+++ b/Tests/RunCMake/find_package/CMP0188-WARN.cmake
@@ -0,0 +1,6 @@
+set(_FindGCCXML_testing TRUE)
+find_package(GCCXML MODULE)
+
+if(NOT _FindGCCXML_included)
+  message(FATAL_ERROR "FindGCCXML.cmake not included")
+endif()
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index 0552535..76a0d10 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -57,6 +57,9 @@
 run_cmake(CMP0167-OLD)
 run_cmake(CMP0167-WARN)
 run_cmake(CMP0167-NEW)
+run_cmake(CMP0188-OLD)
+run_cmake(CMP0188-WARN)
+run_cmake(CMP0188-NEW)
 run_cmake(WrongVersionRange)
 run_cmake(EmptyVersionRange)
 run_cmake(VersionRangeWithEXACT)
diff --git a/Tests/RunCMake/include/CMP0188-NEW-name-result.txt b/Tests/RunCMake/include/CMP0188-NEW-name-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-NEW-name-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0188-NEW-name-stderr.txt b/Tests/RunCMake/include/CMP0188-NEW-name-stderr.txt
new file mode 100644
index 0000000..0a86d22
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-NEW-name-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at CMP0188-NEW-name\.cmake:[0-9]+ \(include\):
+  include could not find requested file:
+
+    FindGCCXML
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0188-NEW-name.cmake b/Tests/RunCMake/include/CMP0188-NEW-name.cmake
new file mode 100644
index 0000000..3cce95c
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-NEW-name.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0188 NEW)
+include(FindGCCXML)
diff --git a/Tests/RunCMake/include/CMP0188-NEW-path-result.txt b/Tests/RunCMake/include/CMP0188-NEW-path-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-NEW-path-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/include/CMP0188-NEW-path-stderr.txt b/Tests/RunCMake/include/CMP0188-NEW-path-stderr.txt
new file mode 100644
index 0000000..f030d7c
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-NEW-path-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Error at [^
+]*/Modules/FindGCCXML.cmake:[0-9]+ \(message\):
+  The FindGCCXML module has been removed by policy CMP0188\.
+Call Stack \(most recent call first\):
+  CMP0188-NEW-path\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/include/CMP0188-NEW-path.cmake b/Tests/RunCMake/include/CMP0188-NEW-path.cmake
new file mode 100644
index 0000000..efc195a
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-NEW-path.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0188 NEW)
+include(${CMAKE_ROOT}/Modules/FindGCCXML.cmake)
diff --git a/Tests/RunCMake/include/CMP0188-OLD.cmake b/Tests/RunCMake/include/CMP0188-OLD.cmake
new file mode 100644
index 0000000..34d5ad4
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-OLD.cmake
@@ -0,0 +1,7 @@
+cmake_policy(SET CMP0188 OLD)
+set(_FindGCCXML_testing 1)
+include(FindGCCXML)
+
+if(NOT _FindGCCXML_included)
+  message(FATAL_ERROR "FindGCCXML.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/CMP0188-WARN-stderr.txt b/Tests/RunCMake/include/CMP0188-WARN-stderr.txt
new file mode 100644
index 0000000..c07bb89
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-WARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Warning \(dev\) at CMP0188-WARN\.cmake:[0-9]+ \(include\):
+  Policy CMP0188 is not set: The FindGCCXML module is removed\.  Run "cmake
+  --help-policy CMP0188" for policy details\.  Use the cmake_policy command to
+  set the policy and suppress this warning\.
+
+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/include/CMP0188-WARN.cmake b/Tests/RunCMake/include/CMP0188-WARN.cmake
new file mode 100644
index 0000000..99d3557
--- /dev/null
+++ b/Tests/RunCMake/include/CMP0188-WARN.cmake
@@ -0,0 +1,7 @@
+# Do not set CMP0188.
+set(_FindGCCXML_testing 1)
+include(FindGCCXML)
+
+if(NOT _FindGCCXML_included)
+  message(FATAL_ERROR "FindGCCXML.cmake not included")
+endif()
diff --git a/Tests/RunCMake/include/RunCMakeTest.cmake b/Tests/RunCMake/include/RunCMakeTest.cmake
index 68a08bb..994f2a5 100644
--- a/Tests/RunCMake/include/RunCMakeTest.cmake
+++ b/Tests/RunCMake/include/RunCMakeTest.cmake
@@ -25,3 +25,8 @@
 run_cmake(CMP0167-WARN)
 run_cmake(CMP0167-NEW-name)
 run_cmake(CMP0167-NEW-path)
+
+run_cmake(CMP0188-OLD)
+run_cmake(CMP0188-WARN)
+run_cmake(CMP0188-NEW-name)
+run_cmake(CMP0188-NEW-path)
diff --git a/Tests/RunCMake/string/RegexEmptyMatch.cmake b/Tests/RunCMake/string/RegexEmptyMatch.cmake
new file mode 100644
index 0000000..1510137
--- /dev/null
+++ b/Tests/RunCMake/string/RegexEmptyMatch.cmake
@@ -0,0 +1,143 @@
+cmake_policy(SET CMP0186 NEW)
+
+function(check_output name expected)
+  set(output "${${name}}")
+  if(NOT output STREQUAL expected)
+    message(FATAL_ERROR "\"string(REGEX)\" set ${name} to \"${output}\", expected \"${expected}\"")
+  endif()
+endfunction()
+
+# Zero-length matches in REGEX MATCH
+
+string(REGEX MATCH "" out "")
+check_output(out "")
+
+string(REGEX MATCH "" out "a")
+check_output(out "")
+
+string(REGEX MATCH "a*" out "")
+check_output(out "")
+
+string(REGEX MATCH "a*" out "a")
+check_output(out "a")
+
+string(REGEX MATCH "a*" out "b")
+check_output(out "")
+
+string(REGEX MATCH "a*" out "ba")
+check_output(out "")
+
+# Zero-length matches in REGEX MATCHALL
+
+string(REGEX MATCHALL "" out "")
+check_output(out "")
+
+string(REGEX MATCHALL "" out "ab")
+check_output(out ";;")
+
+string(REGEX MATCHALL "^" out "ab")
+check_output(out "")
+
+string(REGEX MATCHALL "(^|,)" out "a,b")
+check_output(out ";,")
+
+string(REGEX MATCHALL "(,|^)" out "a,b")
+check_output(out ";,")
+
+string(REGEX MATCHALL "(^|)" out "")
+check_output(out "")
+
+string(REGEX MATCHALL "(^|)" out "ab")
+check_output(out ";;")
+
+string(REGEX MATCHALL "a|^" out "ab")
+check_output(out "a")
+
+string(REGEX MATCHALL "$" out "ab")
+check_output(out "")
+
+string(REGEX MATCHALL "($|,)" out "a,b")
+check_output(out ",;")
+
+string(REGEX MATCHALL "(,|$)" out "a,b")
+check_output(out ",;")
+
+string(REGEX MATCHALL "(|$)" out "")
+check_output(out "")
+
+string(REGEX MATCHALL "(|$)" out "ab")
+check_output(out ";;")
+
+string(REGEX MATCHALL "(b|)" out "abc")
+check_output(out ";b;;")
+
+string(REGEX MATCHALL "(|b)" out "abc")
+check_output(out ";;b;;")
+
+string(REGEX MATCHALL "a*" out "aaa")
+check_output(out "aaa;")
+
+string(REGEX MATCHALL "(a)?(b)?" out "")
+check_output(out "")
+
+string(REGEX MATCHALL "(a)?(b)?" out "abba")
+check_output(out "ab;b;a;")
+
+# Zero-length matches in REGEX REPLACE
+
+string(REGEX REPLACE "" "" out "")
+check_output(out "")
+
+string(REGEX REPLACE "" "x" out "")
+check_output(out "x")
+
+string(REGEX REPLACE "" "x" out "ab")
+check_output(out "xaxbx")
+
+string(REGEX REPLACE "^" "x" out "ab")
+check_output(out "xab")
+
+string(REGEX REPLACE "(^|,)" "x" out "a,b")
+check_output(out "xaxb")
+
+string(REGEX REPLACE "(,|^)" "x" out "a,b")
+check_output(out "xaxb")
+
+string(REGEX REPLACE "(^|)" "x" out "")
+check_output(out "x")
+
+string(REGEX REPLACE "(^|)" "x" out "ab")
+check_output(out "xaxbx")
+
+string(REGEX REPLACE "a|^" "x" out "ab")
+check_output(out "xb")
+
+string(REGEX REPLACE "$" "x" out "ab")
+check_output(out "abx")
+
+string(REGEX REPLACE "($|,)" "x" out "a,b")
+check_output(out "axbx")
+
+string(REGEX REPLACE "(,|$)" "x" out "a,b")
+check_output(out "axbx")
+
+string(REGEX REPLACE "(|$)" "x" out "")
+check_output(out "x")
+
+string(REGEX REPLACE "(|$)" "x" out "ab")
+check_output(out "xaxbx")
+
+string(REGEX REPLACE "(b|)" "x" out "abc")
+check_output(out "xaxxcx")
+
+string(REGEX REPLACE "(|b)" "x" out "abc")
+check_output(out "xaxxxcx")
+
+string(REGEX REPLACE "a*" "x" out "aaa")
+check_output(out "xx")
+
+string(REGEX REPLACE "(a)?(b)?" "x" out "")
+check_output(out "x")
+
+string(REGEX REPLACE "(a)?(b)?" "x" out "abba")
+check_output(out "xxxx")
diff --git a/Tests/RunCMake/string/RunCMakeTest.cmake b/Tests/RunCMake/string/RunCMakeTest.cmake
index e352fcb..91a03da 100644
--- a/Tests/RunCMake/string/RunCMakeTest.cmake
+++ b/Tests/RunCMake/string/RunCMakeTest.cmake
@@ -35,6 +35,7 @@
 
 run_cmake(RegexClear)
 run_cmake(RegexMultiMatchClear)
+run_cmake(RegexEmptyMatch)
 run_cmake(CMP0186)
 
 run_cmake(UTF-16BE)
diff --git a/Utilities/Release/README.rst b/Utilities/Release/README.rst
index d5bbd2b..ac671c0 100644
--- a/Utilities/Release/README.rst
+++ b/Utilities/Release/README.rst
@@ -79,6 +79,12 @@
 
 .. _`kitware/cmake Docker Hub Repository`: https://hub.docker.com/r/kitware/cmake
 
+SunOS
+-----
+
+The ``sunos/`` directory contains infrastructure to cross-compile
+CMake binaries to SunOS from Linux hosts.
+
 macOS
 -----
 
diff --git a/Utilities/Release/files-v1.json.in b/Utilities/Release/files-v1.json.in
index 2f860d2..05561c7 100644
--- a/Utilities/Release/files-v1.json.in
+++ b/Utilities/Release/files-v1.json.in
@@ -60,6 +60,30 @@
       "macOSmin": "10.10"
     },
     {
+      "os": ["sunos", "SunOS"],
+      "architecture": ["i386"],
+      "class": "installer",
+      "name": "cmake-@version@-sunos-i386.sh"
+    },
+    {
+      "os": ["sunos", "SunOS"],
+      "architecture": ["i386"],
+      "class": "archive",
+      "name": "cmake-@version@-sunos-i386.tar.gz"
+    },
+    {
+      "os": ["sunos", "SunOS"],
+      "architecture": ["sparc"],
+      "class": "installer",
+      "name": "cmake-@version@-sunos-sparc.sh"
+    },
+    {
+      "os": ["sunos", "SunOS"],
+      "architecture": ["sparc"],
+      "class": "archive",
+      "name": "cmake-@version@-sunos-sparc.tar.gz"
+    },
+    {
       "os": ["windows", "Windows"],
       "architecture": ["i386"],
       "class": "installer",
diff --git a/Utilities/Release/files-v1.rst b/Utilities/Release/files-v1.rst
index f0064e8..b8dbe6f 100644
--- a/Utilities/Release/files-v1.rst
+++ b/Utilities/Release/files-v1.rst
@@ -70,6 +70,9 @@
     ``Windows``, ``windows``
       Windows packages.
 
+    ``SunOS``, ``sunos``
+      SunOS packages.
+
   ``architecture``
     A JSON array of strings naming the architecture(s) for which the
     package file is built, possibly using multiple alternative spellings.
@@ -79,6 +82,7 @@
     On Windows, architecture names include ``x86_64``, ``i386``, and ``arm64``.
     On macOS, universal binary packages list all architectures,
     e.g. ``["arm64","x86_64"]``.
+    On SunOS, architecture names include ``i386`` and ``sparc``.
 
   ``class``
     A JSON string naming the class of package.  The value is one of:
diff --git a/Utilities/Release/sunos/docker/Dockerfile b/Utilities/Release/sunos/docker/Dockerfile
new file mode 100644
index 0000000..626dff4
--- /dev/null
+++ b/Utilities/Release/sunos/docker/Dockerfile
@@ -0,0 +1,53 @@
+# syntax=docker/dockerfile:1
+
+ARG BASE_IMAGE=debian:12
+
+FROM ${BASE_IMAGE} AS apt-cache
+# Populate APT cache w/ the fresh metadata and prefetch packages.
+# Use an empty `docker-clean` file to "hide" the image-provided
+# file to disallow removing packages after `apt-get` operations.
+RUN --mount=type=tmpfs,target=/var/log \
+    --mount=type=bind,source=docker-clean,target=/etc/apt/apt.conf.d/docker-clean \
+    --mount=type=bind,source=base.lst,target=/root/base.lst \
+    --mount=type=bind,source=openssl.lst,target=/root/openssl.lst \
+    apt-get update \
+ && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
+
+FROM ${BASE_IMAGE} AS base
+ARG ARCH=sparc
+RUN --mount=type=bind,source=base.lst,target=/root/base.lst \
+    --mount=type=bind,source=docker-clean,target=/etc/apt/apt.conf.d/docker-clean \
+    --mount=type=cache,from=apt-cache,source=/var/lib/apt/lists,target=/var/lib/apt/lists \
+    --mount=type=cache,from=apt-cache,source=/var/cache/apt,target=/var/cache/apt,sharing=private \
+    --mount=type=tmpfs,target=/var/log \
+    --mount=type=tmpfs,target=/tmp \
+    apt-get install -y $(grep '^[^#]\+$' /root/base.lst)
+RUN --mount=type=bind,source=base.bash,target=/root/base.bash \
+    --mount=type=tmpfs,target=/tmp \
+    /root/base.bash $ARCH
+
+FROM base AS sysroot
+ARG SYSROOT_URL
+ARG SYSROOT_SHA256SUM
+RUN --mount=type=bind,source=sysroot.bash,target=/root/sysroot.bash \
+    --mount=type=tmpfs,target=/tmp \
+    /root/sysroot.bash $ARCH
+
+FROM sysroot AS openssl
+RUN --mount=type=bind,source=openssl.lst,target=/root/openssl.lst \
+    --mount=type=bind,source=docker-clean,target=/etc/apt/apt.conf.d/docker-clean \
+    --mount=type=cache,from=apt-cache,source=/var/lib/apt/lists,target=/var/lib/apt/lists \
+    --mount=type=cache,from=apt-cache,source=/var/cache/apt,target=/var/cache/apt,sharing=private \
+    --mount=type=tmpfs,target=/var/log \
+    --mount=type=tmpfs,target=/tmp \
+    apt-get install -y $(grep '^[^#]\+$' /root/openssl.lst)
+RUN --mount=type=bind,source=openssl.bash,target=/root/openssl.bash \
+    --mount=type=bind,source=openssl.patch,target=/root/openssl.patch \
+    --mount=type=bind,from=sysroot,source=/opt/cross/sysroot,target=/opt/cross/sysroot \
+    --mount=type=tmpfs,target=/tmp \
+    /root/openssl.bash $ARCH
+
+FROM base
+LABEL maintainer="Brad King <brad.king@kitware.com>"
+RUN --mount=type=bind,from=openssl,source=/root,target=/root \
+    tar xzf /root/openssl.tar.gz -C /
diff --git a/Utilities/Release/sunos/docker/base.bash b/Utilities/Release/sunos/docker/base.bash
new file mode 100755
index 0000000..0c707d0
--- /dev/null
+++ b/Utilities/Release/sunos/docker/base.bash
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+set -e
+
+arch="$1"
+readonly arch
+
+case "$arch" in
+    i386)
+        tarball="gcc-9.5.0-linux-x86_64-cross-sunos-i386.tar.xz"
+        sha256sum="3cd3c989483051e741dd9f39170842d22e5c43cd25628d2b0c57890a3f235883"
+        ;;
+    sparc)
+        tarball="gcc-9.5.0-linux-x86_64-cross-sunos-sparc.tar.xz"
+        sha256sum="853454ef4e787895786fdb21e56a3ba9c121ffe6116467a75f2c3eb09f3c88b4"
+        ;;
+    *)
+        echo >&2 "Unknown architecture: $arch"
+        exit 1
+        ;;
+esac
+readonly tarball
+readonly sha256sum
+
+cd /tmp
+
+curl -OL "https://gitlab.kitware.com/api/v4/projects/6955/packages/generic/gcc-solaris/v9.5.0-20250212.0/$tarball"
+echo "$sha256sum  $tarball" > gcc.sha256sum
+sha256sum --check gcc.sha256sum
+
+tar xJf "$tarball" -C /
diff --git a/Utilities/Release/sunos/docker/base.lst b/Utilities/Release/sunos/docker/base.lst
new file mode 100644
index 0000000..6a9a3ec
--- /dev/null
+++ b/Utilities/Release/sunos/docker/base.lst
@@ -0,0 +1,3 @@
+curl
+git
+xz-utils
diff --git a/Utilities/Release/sunos/docker/docker-clean b/Utilities/Release/sunos/docker/docker-clean
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Utilities/Release/sunos/docker/docker-clean
diff --git a/Utilities/Release/sunos/docker/openssl.bash b/Utilities/Release/sunos/docker/openssl.bash
new file mode 100755
index 0000000..86f9ad8
--- /dev/null
+++ b/Utilities/Release/sunos/docker/openssl.bash
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+
+set -e
+
+arch="$1"
+readonly arch
+
+case "$arch" in
+    i386)
+        target=i386-pc-solaris2.10
+        openssl_target=solaris-x86-gcc
+        ldlibs=
+        ;;
+    sparc)
+        target=sparc-sun-solaris2.10
+        openssl_target=solaris-sparcv8-gcc
+        ldlibs=-latomic
+        ;;
+    *)
+        echo >&2 "Unknown architecture: $arch"
+        exit 1
+        ;;
+esac
+readonly target
+readonly openssl_target
+readonly ldlibs
+
+readonly sha256sum="e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf"
+readonly filename="openssl-3.4.0"
+readonly tarball="$filename.tar.gz"
+
+cd /tmp
+
+curl -OL "https://github.com/openssl/openssl/releases/download/$filename/$tarball"
+echo "$sha256sum  $tarball" > openssl.sha256sum
+sha256sum --check openssl.sha256sum
+
+tar xzf "$tarball"
+
+prefix="/opt/cross/openssl/$target"
+cd "$filename"
+patch -p0 < "${BASH_SOURCE%/*}/openssl.patch"
+env \
+  LDLIBS="$ldlibs" \
+  LDFLAGS="-Wl,-z,noexecstack" \
+  ./Configure \
+    --prefix="$prefix" \
+    --cross-compile-prefix="/opt/cross/bin/$target-" \
+    --api=1.1.1 \
+    "$openssl_target" \
+    no-deprecated \
+    no-shared
+if ! make -j $(nproc) >make.log 2>&1; then
+    tail -1000 make.log
+    exit 1
+fi
+if ! make install_sw >>make.log 2>&1; then
+    tail -1000 make.log
+    exit 1
+fi
+
+tar czf /root/openssl.tar.gz -C / "$prefix"
diff --git a/Utilities/Release/sunos/docker/openssl.lst b/Utilities/Release/sunos/docker/openssl.lst
new file mode 100644
index 0000000..96fd60f
--- /dev/null
+++ b/Utilities/Release/sunos/docker/openssl.lst
@@ -0,0 +1,4 @@
+m4
+make
+patch
+perl
diff --git a/Utilities/Release/sunos/docker/openssl.patch b/Utilities/Release/sunos/docker/openssl.patch
new file mode 100644
index 0000000..98f8362
--- /dev/null
+++ b/Utilities/Release/sunos/docker/openssl.patch
@@ -0,0 +1,22 @@
+--- crypto/sleep.orig
++++ crypto/sleep.c
+@@ -10,6 +10,8 @@
+ #include <openssl/crypto.h>
+ #include "internal/e_os.h"
+
++#define OPENSSL_USE_USLEEP
++
+ /* system-specific variants defining OSSL_sleep() */
+ #if defined(OPENSSL_SYS_UNIX) || defined(__DJGPP__)
+
+--- providers/implementations/rands/seeding/rand_unix.c.orig
++++ providers/implementations/rands/seeding/rand_unix.c
+@@ -84,8 +84,6 @@
+ #     define OSSL_POSIX_TIMER_OKAY
+ #    endif
+ #   endif
+-#  else
+-#   define OSSL_POSIX_TIMER_OKAY
+ #  endif
+ # endif
+ #endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS))
diff --git a/Utilities/Release/sunos/docker/sysroot.bash b/Utilities/Release/sunos/docker/sysroot.bash
new file mode 100755
index 0000000..87ec01e
--- /dev/null
+++ b/Utilities/Release/sunos/docker/sysroot.bash
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+set -e
+
+arch="$1"
+readonly arch
+
+case "$arch" in
+    i386)
+        tarball="sysroot-i386-pc-solaris2.10-sunos5.10-1.tar.xz"
+        sha256sum="1b9251699f4e412ba5b0fde9c0fb96ceef6b8a1f47f0c1f2146ba0ba9da458b8"
+        ;;
+    sparc)
+        tarball="sysroot-sparc-sun-solaris2.10-sunos5.10-1.tar.xz"
+        sha256sum="e6c668a63dc00de443d07cbe2be779335642ffe1b818ba85d23ab543982aaf23"
+        ;;
+    *)
+        echo >&2 "Unknown architecture: $arch"
+        exit 1
+        ;;
+esac
+# To build externally, provide a Solaris sysroot tarball:
+#   --build-arg SYSROOT_URL=...
+#   --build-arg SYSROOT_SHA256SUM=...
+# The tarball must contain one of:
+#   sysroot/i386-pc-solaris2.10/{lib,usr/lib,usr/include}
+#   sysroot/sparc-sun-solaris2.10/{lib,usr/lib,usr/include}
+# The content may be retrieved from a real Solaris host.
+if test -n "$SYSROOT_URL"; then
+    url="$SYSROOT_URL"
+    if test -n "$SYSROOT_SHA256SUM"; then
+        sha256sum="$SYSROOT_SHA256SUM"
+    else
+        sha256sum=""
+    fi
+    tarball=$(basename "$url")
+else
+    # This URL is only visible inside of Kitware's network.
+    url="https://cmake.org/files/dependencies/internal/sunos/$tarball"
+fi
+readonly url
+readonly tarball
+readonly sha256sum
+
+cd /tmp
+
+curl -OL "$url"
+if test -n "$sha256sum"; then
+    echo "$sha256sum  $tarball" > sysroot.sha256sum
+    sha256sum --check sysroot.sha256sum
+fi
+
+tar xf "$tarball" -C /opt/cross
diff --git a/Utilities/Release/sunos/i386/cache.txt b/Utilities/Release/sunos/i386/cache.txt
new file mode 100644
index 0000000..6bb2c27
--- /dev/null
+++ b/Utilities/Release/sunos/i386/cache.txt
@@ -0,0 +1,25 @@
+CMAKE_BUILD_TYPE:STRING=Release
+
+# Link C++ library statically.
+CMAKE_EXE_LINKER_FLAGS:STRING=-static-libstdc++ -static-libgcc -Wl,-z,noexecstack
+
+# Enable ssl support in curl
+CMAKE_USE_OPENSSL:BOOL=ON
+OPENSSL_USE_STATIC_LIBS:BOOL=ON
+OpenSSL_ROOT:PATH=/opt/cross/openssl/i386-pc-solaris2.10
+
+# Enable ccmake
+BUILD_CursesDialog:BOOL=ON
+
+# Disable cmake-gui
+BUILD_QtDialog:BOOL=OFF
+
+# Disable tests.
+BUILD_TESTING:BOOL=OFF
+CMake_TEST_INSTALL:BOOL=OFF
+
+# Disable unnecessary dependency.
+CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:BOOL=ON
+
+# CPack package file name component for this platform.
+CPACK_SYSTEM_NAME:STRING=sunos-i386
diff --git a/Utilities/Release/sunos/i386/toolchain.cmake b/Utilities/Release/sunos/i386/toolchain.cmake
new file mode 100644
index 0000000..59ce6b5
--- /dev/null
+++ b/Utilities/Release/sunos/i386/toolchain.cmake
@@ -0,0 +1,6 @@
+set(CMAKE_SYSTEM_NAME SunOS)
+set(CMAKE_SYSTEM_VERSION 5.10)
+set(CMAKE_SYSTEM_PROCESSOR i386)
+set(CMAKE_SYSROOT /opt/cross/sysroot/i386-pc-solaris2.10)
+set(CMAKE_C_COMPILER /opt/cross/bin/i386-pc-solaris2.10-gcc)
+set(CMAKE_CXX_COMPILER /opt/cross/bin/i386-pc-solaris2.10-g++)
diff --git a/Utilities/Release/sunos/sparc/cache.txt b/Utilities/Release/sunos/sparc/cache.txt
new file mode 100644
index 0000000..eb12c14
--- /dev/null
+++ b/Utilities/Release/sunos/sparc/cache.txt
@@ -0,0 +1,26 @@
+CMAKE_BUILD_TYPE:STRING=Release
+
+# Link C++ library statically.
+CMAKE_EXE_LINKER_FLAGS:STRING=-static-libstdc++ -static-libgcc -Wl,-z,noexecstack
+
+# Enable ssl support in curl
+CMAKE_USE_OPENSSL:BOOL=ON
+OPENSSL_USE_STATIC_LIBS:BOOL=ON
+OpenSSL_ROOT:PATH=/opt/cross/openssl/sparc-sun-solaris2.10
+_OPENSSL_STATIC_LIBRARIES:STRING=/opt/cross/sparc-sun-solaris2.10/lib/libatomic.a
+
+# Enable ccmake
+BUILD_CursesDialog:BOOL=ON
+
+# Disable cmake-gui
+BUILD_QtDialog:BOOL=OFF
+
+# Disable tests.
+BUILD_TESTING:BOOL=OFF
+CMake_TEST_INSTALL:BOOL=OFF
+
+# Disable unnecessary dependency.
+CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:BOOL=ON
+
+# CPack package file name component for this platform.
+CPACK_SYSTEM_NAME:STRING=sunos-sparc
diff --git a/Utilities/Release/sunos/sparc/toolchain.cmake b/Utilities/Release/sunos/sparc/toolchain.cmake
new file mode 100644
index 0000000..789df8d
--- /dev/null
+++ b/Utilities/Release/sunos/sparc/toolchain.cmake
@@ -0,0 +1,6 @@
+set(CMAKE_SYSTEM_NAME SunOS)
+set(CMAKE_SYSTEM_VERSION 5.10)
+set(CMAKE_SYSTEM_PROCESSOR sparc)
+set(CMAKE_SYSROOT /opt/cross/sysroot/sparc-sun-solaris2.10)
+set(CMAKE_C_COMPILER /opt/cross/bin/sparc-sun-solaris2.10-gcc)
+set(CMAKE_CXX_COMPILER /opt/cross/bin/sparc-sun-solaris2.10-g++)