Merge topic 'ExternalData-add-httpheader'

5106f34eed ExternalData: Add option to specify HTTP headers

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !10275
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index 0e32a9c..1b6a555 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -88,9 +88,9 @@
     mode", "package configuration files", and so forth refer equally to both
     CPS and CMake-script files.  However, some features of ``find_package``
     are not supported at this time when a CPS file is found.  In particular,
-    no attempt to validate whether a candidate ``.cps`` file matches
-    ``VERSION`` or ``COMPONENTS`` requirements is performed at this time.
-    (We expect to implement these features in the near future.)
+    if a ``VERSION`` requirement is specified, only ``.cps`` files which do not
+    provide version information will be rejected.  (We expect to implement
+    proper version validation in the near future.)
 
     Search is implemented in a manner that will tend to prefer |CPS| files
     over CMake-script config files in most cases.  Specifying ``CONFIGS``
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index f4f2971..0165d6a 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -98,6 +98,7 @@
 .. toctree::
    :maxdepth: 1
 
+   CMP0185: FindRuby no longer provides upper-case RUBY_* variables. </policy/CMP0185>
    CMP0184: MSVC runtime checks flags are selected by an abstraction. </policy/CMP0184>
    CMP0183: add_feature_info() supports full Condition Syntax. </policy/CMP0183>
    CMP0182: Create shared library archives by default on AIX. </policy/CMP0182>
diff --git a/Help/policy/CMP0185.rst b/Help/policy/CMP0185.rst
new file mode 100644
index 0000000..2020d21
--- /dev/null
+++ b/Help/policy/CMP0185.rst
@@ -0,0 +1,25 @@
+CMP0185
+-------
+
+.. versionadded:: 4.0
+
+:module:`FindRuby` no longer provides upper-case ``RUBY_*`` variables.
+
+:module:`FindRuby` in CMake 3.31 and below provided result variables
+named with an upper-case ``RUBY_`` prefix.  CMake 3.18 renamed them
+with a ``Ruby_`` prefix to match the name of the package, but continued
+to provide the upper-case ``RUBY_`` variables for compatibility.
+
+CMake 4.0 and above prefer to provide only ``Ruby_`` variables and no
+longer provide ``RUBY_`` variables.  This policy provides compatibility
+with projects that have not been updated.
+
+The ``OLD`` behavior for this policy is to provide both ``Ruby_`` and
+``RUBY_`` variables.  The ``NEW`` behavior for this policy is to provide
+only ``Ruby_`` variables and not ``RUBY_`` variables.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/release/dev/FindRuby-compat-vars.rst b/Help/release/dev/FindRuby-compat-vars.rst
new file mode 100644
index 0000000..1a08695
--- /dev/null
+++ b/Help/release/dev/FindRuby-compat-vars.rst
@@ -0,0 +1,5 @@
+FindRuby-compat-vars
+--------------------
+
+* The :module:`FindRuby` module no longer provides variables with the
+  upper-case ``RUBY_`` prefix.  See policy :policy:`CMP0185`.
diff --git a/Help/release/dev/cpack-tar.rst b/Help/release/dev/cpack-tar.rst
index d929239..7613830 100644
--- a/Help/release/dev/cpack-tar.rst
+++ b/Help/release/dev/cpack-tar.rst
@@ -1,5 +1,5 @@
 cpack-tar
 ---------
 
-* The :cpack_gen:`CPack Archive Generator` learned to generated `.tar`
+* The :cpack_gen:`CPack Archive Generator` learned to generated ``.tar``
   packages without compression.
diff --git a/Help/variable/CMAKE_EXPORT_SARIF.rst b/Help/variable/CMAKE_EXPORT_SARIF.rst
index 8532377..09ed40b 100644
--- a/Help/variable/CMAKE_EXPORT_SARIF.rst
+++ b/Help/variable/CMAKE_EXPORT_SARIF.rst
@@ -7,7 +7,7 @@
 
 If enabled, CMake will generate a SARIF log file containing diagnostic messages
 output by CMake when running in a project. By default, the log file is written
-to `.cmake/sarif/cmake.sarif`, but the location can be changed by setting the
+to ``.cmake/sarif/cmake.sarif``, but the location can be changed by setting the
 command-line option :option:`cmake --sarif-output` to the desired path.
 
 The Static Analysis Results Interchange Format (SARIF) is a JSON-based standard
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index 009340d..967ea20 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -1094,17 +1094,7 @@
       # The offset to the PE signature is stored at 0x3c.
       file(READ ${file} peoffsethex LIMIT 1 OFFSET 60 HEX)
       if(NOT peoffsethex STREQUAL "")
-        string(SUBSTRING "${peoffsethex}" 0 1 peoffsethex1)
-        string(SUBSTRING "${peoffsethex}" 1 1 peoffsethex2)
-        set(peoffsetexpression "${peoffsethex1} * 16 + ${peoffsethex2}")
-        string(REPLACE "a" "10" peoffsetexpression "${peoffsetexpression}")
-        string(REPLACE "b" "11" peoffsetexpression "${peoffsetexpression}")
-        string(REPLACE "c" "12" peoffsetexpression "${peoffsetexpression}")
-        string(REPLACE "d" "13" peoffsetexpression "${peoffsetexpression}")
-        string(REPLACE "e" "14" peoffsetexpression "${peoffsetexpression}")
-        string(REPLACE "f" "15" peoffsetexpression "${peoffsetexpression}")
-        math(EXPR peoffset "${peoffsetexpression}")
-
+        math(EXPR peoffset "0x${peoffsethex}")
         file(READ ${file} peheader LIMIT 6 OFFSET ${peoffset} HEX)
         if(peheader STREQUAL "50450000a201")
           set(ARCHITECTURE_ID "SH3")
diff --git a/Modules/CTestUseLaunchers.cmake b/Modules/CTestUseLaunchers.cmake
index 5c544f8..eeea992 100644
--- a/Modules/CTestUseLaunchers.cmake
+++ b/Modules/CTestUseLaunchers.cmake
@@ -46,7 +46,7 @@
 
 if(CTEST_USE_LAUNCHERS)
   set(__launch_common_options
-    "--target-name <TARGET_NAME> --build-dir <CMAKE_CURRENT_BINARY_DIR>")
+    "--target-name <TARGET_NAME> --current-build-dir <CMAKE_CURRENT_BINARY_DIR>")
 
   set(__launch_compile_options
     "${__launch_common_options} --output <OBJECT> --source <SOURCE> --language <LANGUAGE>")
diff --git a/Modules/FindOpenSSL.cmake b/Modules/FindOpenSSL.cmake
index 2c32091..f7a87df 100644
--- a/Modules/FindOpenSSL.cmake
+++ b/Modules/FindOpenSSL.cmake
@@ -573,37 +573,6 @@
   _OpenSSL_add_dependencies( OPENSSL_LIBRARIES )
 endif()
 
-function(from_hex HEX DEC)
-  string(TOUPPER "${HEX}" HEX)
-  set(_res 0)
-  string(LENGTH "${HEX}" _strlen)
-
-  while (_strlen GREATER 0)
-    math(EXPR _res "${_res} * 16")
-    string(SUBSTRING "${HEX}" 0 1 NIBBLE)
-    string(SUBSTRING "${HEX}" 1 -1 HEX)
-    if (NIBBLE STREQUAL "A")
-      math(EXPR _res "${_res} + 10")
-    elseif (NIBBLE STREQUAL "B")
-      math(EXPR _res "${_res} + 11")
-    elseif (NIBBLE STREQUAL "C")
-      math(EXPR _res "${_res} + 12")
-    elseif (NIBBLE STREQUAL "D")
-      math(EXPR _res "${_res} + 13")
-    elseif (NIBBLE STREQUAL "E")
-      math(EXPR _res "${_res} + 14")
-    elseif (NIBBLE STREQUAL "F")
-      math(EXPR _res "${_res} + 15")
-    else()
-      math(EXPR _res "${_res} + ${NIBBLE}")
-    endif()
-
-    string(LENGTH "${HEX}" _strlen)
-  endwhile()
-
-  set(${DEC} ${_res} PARENT_SCOPE)
-endfunction()
-
 if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h")
   file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str
        REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*")
@@ -620,16 +589,14 @@
            "\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}")
     list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR)
     list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR)
-    from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR)
+    math(EXPR OPENSSL_VERSION_MINOR "0x${OPENSSL_VERSION_MINOR}")
     list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX)
-    from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX)
+    math(EXPR OPENSSL_VERSION_FIX "0x${OPENSSL_VERSION_FIX}")
     list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH)
 
     if (NOT OPENSSL_VERSION_PATCH STREQUAL "00")
-      from_hex("${OPENSSL_VERSION_PATCH}" _tmp)
       # 96 is the ASCII code of 'a' minus 1
-      math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96")
-      unset(_tmp)
+      math(EXPR OPENSSL_VERSION_PATCH_ASCII "0x${OPENSSL_VERSION_PATCH} + 96")
       # Once anyone knows how OpenSSL would call the patch versions beyond 'z'
       # this should be updated to handle that, too. This has not happened yet
       # so it is simply ignored here for now.
diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake
index 2bd1b74..bac96f0 100644
--- a/Modules/FindRuby.cmake
+++ b/Modules/FindRuby.cmake
@@ -44,21 +44,22 @@
 
 .. versionchanged:: 3.18
   Previous versions of CMake used the ``RUBY_`` prefix for all variables.
-  The following variables are provided for compatibility reasons,
-  don't use them in new code:
 
-``RUBY_EXECUTABLE``
-  same as Ruby_EXECUTABLE.
-``RUBY_INCLUDE_DIRS``
-  same as Ruby_INCLUDE_DIRS.
-``RUBY_INCLUDE_PATH``
-  same as Ruby_INCLUDE_DIRS.
-``RUBY_LIBRARY``
-  same as Ruby_LIBRARY.
-``RUBY_VERSION``
-  same as Ruby_VERSION.
-``RUBY_FOUND``
-  same as Ruby_FOUND.
+.. deprecated:: 4.0
+  The following variables are deprecated.  See policy :policy:`CMP0185`.
+
+  ``RUBY_EXECUTABLE``
+    same as ``Ruby_EXECUTABLE``.
+  ``RUBY_INCLUDE_DIRS``
+    same as ``Ruby_INCLUDE_DIRS``.
+  ``RUBY_INCLUDE_PATH``
+    same as ``Ruby_INCLUDE_DIRS``.
+  ``RUBY_LIBRARY``
+    same as ``Ruby_LIBRARY``.
+  ``RUBY_VERSION``
+    same as ``Ruby_VERSION``.
+  ``RUBY_FOUND``
+    same as ``Ruby_FOUND``.
 
 Hints
 ^^^^^
@@ -85,45 +86,39 @@
     or that the ``RBENV_ROOT`` environment variable is defined.
 #]=======================================================================]
 
-# Backwards compatibility
-# Define camel case versions of input variables
-foreach (UPPER
-         RUBY_EXECUTABLE
-         RUBY_LIBRARY
-         RUBY_INCLUDE_DIR
-         RUBY_CONFIG_INCLUDE_DIR)
-  if (DEFINED ${UPPER})
-    string(REPLACE "RUBY_" "Ruby_" Camel ${UPPER})
-    if (NOT DEFINED ${Camel})
-      set(${Camel} ${${UPPER}})
+cmake_policy(GET CMP0185 _Ruby_CMP0185)
+
+if(NOT _Ruby_CMP0185 STREQUAL "NEW")
+  # Backwards compatibility
+  # Define camel case versions of input variables
+  foreach (UPPER
+           RUBY_EXECUTABLE
+           RUBY_LIBRARY
+           RUBY_INCLUDE_DIR
+           RUBY_CONFIG_INCLUDE_DIR)
+    if (DEFINED ${UPPER})
+      string(REPLACE "RUBY_" "Ruby_" Camel ${UPPER})
+      if (NOT DEFINED ${Camel})
+        set(${Camel} ${${UPPER}})
+      endif ()
     endif ()
-  endif ()
-endforeach ()
+  endforeach ()
+endif()
 
-#   Ruby_ARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"archdir"@:>@)'`
-#   Ruby_SITEARCHDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitearchdir"@:>@)'`
-#   Ruby_SITEDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"sitelibdir"@:>@)'`
-#   Ruby_LIBDIR=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"libdir"@:>@)'`
-#   Ruby_LIBRUBYARG=`$RUBY -r rbconfig -e 'printf("%s",Config::CONFIG@<:@"LIBRUBYARG_SHARED"@:>@)'`
-
-# uncomment the following line to get debug output for this file
+# Uncomment the following line to get debug output for this file
 # set(CMAKE_MESSAGE_LOG_LEVEL DEBUG)
 
 # Determine the list of possible names of the ruby executable depending
 # on which version of ruby is required
 set(_Ruby_POSSIBLE_EXECUTABLE_NAMES ruby)
 
-# Set name of possible executables, ignoring the minor
-# Eg:
-# 3.2.6 => from ruby34 to ruby32 included
-# 3.2   => from ruby34 to ruby32 included
-# 3     => from ruby34 to ruby30 included
-# empty => from ruby34 to ruby18 included
+# If the user has not specified a Ruby version, create a list of Ruby versions
+# to check going from 1.8 to 3.4
 if (NOT Ruby_FIND_VERSION_EXACT)
   foreach (_ruby_version RANGE 34 18 -1)
     string(SUBSTRING "${_ruby_version}" 0 1 _ruby_major_version)
     string(SUBSTRING "${_ruby_version}" 1 1 _ruby_minor_version)
-    # Append both rubyX.Y and rubyXY (eg: ruby2.7 ruby27)
+    # Append both rubyX.Y and rubyXY (eg: ruby3.4 ruby34)
     list(APPEND _Ruby_POSSIBLE_EXECUTABLE_NAMES ruby${_ruby_major_version}.${_ruby_minor_version} ruby${_ruby_major_version}${_ruby_minor_version})
   endforeach ()
 endif ()
@@ -137,6 +132,8 @@
   set(Ruby_FIND_VIRTUALENV "FIRST")
 endif ()
 
+# Validate the found Ruby interpreter to make sure that it is
+# callable and that its version matches the requested version
 function(_RUBY_VALIDATE_INTERPRETER result_var path)
   # Get the interpreter version
   execute_process(COMMAND "${path}" -e "puts RUBY_VERSION"
@@ -167,21 +164,25 @@
   set(${result_var} TRUE PARENT_SCOPE)
 endfunction()
 
+# Query Ruby RBConfig module for the specified variable (_RUBY_CONFIG_VAR)
 function(_RUBY_CONFIG_VAR RBVAR OUTVAR)
   execute_process(COMMAND ${Ruby_EXECUTABLE} -r rbconfig -e "print RbConfig::CONFIG['${RBVAR}']"
                   RESULT_VARIABLE _Ruby_SUCCESS
                   OUTPUT_VARIABLE _Ruby_OUTPUT
                   ERROR_QUIET)
+
+  # Config was deprecated in Ruby 1.9 and then removed in Ruby 2 - so this is for ancient code
   if (_Ruby_SUCCESS OR _Ruby_OUTPUT STREQUAL "")
     execute_process(COMMAND ${Ruby_EXECUTABLE} -r rbconfig -e "print Config::CONFIG['${RBVAR}']"
                     RESULT_VARIABLE _Ruby_SUCCESS
                     OUTPUT_VARIABLE _Ruby_OUTPUT
                     ERROR_QUIET)
   endif ()
+
   set(${OUTVAR} "${_Ruby_OUTPUT}" PARENT_SCOPE)
 endfunction()
 
-####  Check RVM virtual environment ###
+# Check for RVM virtual environments
 function(_RUBY_CHECK_RVM)
   if (NOT DEFINED ENV{MY_RUBY_HOME})
     return()
@@ -203,7 +204,7 @@
   endif ()
 endfunction()
 
-####  Check RBENV virtual environment ###
+# Check for RBENV virtual environments
 function(_RUBY_CHECK_RBENV)
   find_program(Ruby_RBENV_EXECUTABLE
                NAMES rbenv
@@ -237,7 +238,7 @@
   endif ()
 endfunction()
 
-####  Check system installed Ruby ###
+# Check system installed Ruby
 function(_RUBY_CHECK_SYSTEM)
   find_program(Ruby_EXECUTABLE
                NAMES ${_Ruby_POSSIBLE_EXECUTABLE_NAMES}
@@ -259,11 +260,13 @@
   endif ()
 endif ()
 
-# Check for system installed Ruby
+# Fallback to system installed Ruby
 if (NOT Ruby_EXECUTABLE AND NOT Ruby_FIND_VIRTUALENV STREQUAL "ONLY")
   _RUBY_CHECK_SYSTEM()
 endif ()
 
+# We found a new Ruby or a Ruby that is different than the last one we found.
+# So reload a number of variables by querying the Ruby interpreter.
 if (Ruby_EXECUTABLE AND NOT Ruby_EXECUTABLE STREQUAL "${_Ruby_EXECUTABLE_LAST_QUERIED}")
   # query the ruby version
   _RUBY_CONFIG_VAR("MAJOR" Ruby_VERSION_MAJOR)
@@ -382,7 +385,7 @@
   set(Ruby_INCLUDE_DIRS ${Ruby_INCLUDE_DIRS} ${Ruby_CONFIG_INCLUDE_DIR})
 endif ()
 
-# Determine the list of possible names for the ruby library
+# Determine the list of possible names for the Ruby shared library
 set(_Ruby_POSSIBLE_LIB_NAMES ruby ruby-static ruby${_Ruby_VERSION_SHORT} ruby${_Ruby_VERSION_SHORT_NODOT} ruby${_Ruby_NODOT_VERSION} ruby-${_Ruby_VERSION_SHORT} ruby-${Ruby_VERSION})
 
 if (WIN32)
@@ -449,32 +452,34 @@
     Ruby_CONFIG_INCLUDE_DIR
 )
 
-# Set some variables for compatibility with previous version of this file (no need to provide a CamelCase version of that...)
-set(RUBY_POSSIBLE_LIB_PATH ${_Ruby_POSSIBLE_LIB_DIR})
-set(RUBY_RUBY_LIB_PATH ${Ruby_RUBY_LIB_DIR})
-set(RUBY_INCLUDE_PATH ${Ruby_INCLUDE_DIRS})
+if(NOT _Ruby_CMP0185 STREQUAL "NEW")
+  # Set some variables for compatibility with previous version of this file (no need to provide a CamelCase version of that...)
+  set(RUBY_POSSIBLE_LIB_PATH ${_Ruby_POSSIBLE_LIB_DIR})
+  set(RUBY_RUBY_LIB_PATH ${Ruby_RUBY_LIB_DIR})
+  set(RUBY_INCLUDE_PATH ${Ruby_INCLUDE_DIRS})
 
-# Backwards compatibility
-# Define upper case versions of output variables
-foreach (Camel
-         Ruby_EXECUTABLE
-         Ruby_INCLUDE_DIRS
-         Ruby_LIBRARY
-         Ruby_VERSION
-         Ruby_VERSION_MAJOR
-         Ruby_VERSION_MINOR
-         Ruby_VERSION_PATCH
+  # Backwards compatibility
+  # Define upper case versions of output variables
+  foreach (Camel
+           Ruby_EXECUTABLE
+           Ruby_INCLUDE_DIRS
+           Ruby_LIBRARY
+           Ruby_VERSION
+           Ruby_VERSION_MAJOR
+           Ruby_VERSION_MINOR
+           Ruby_VERSION_PATCH
 
-         Ruby_ARCH_DIR
-         Ruby_ARCH
-         Ruby_HDR_DIR
-         Ruby_ARCHHDR_DIR
-         Ruby_RUBY_LIB_DIR
-         Ruby_SITEARCH_DIR
-         Ruby_SITELIB_DIR
-         Ruby_HAS_VENDOR_RUBY
-         Ruby_VENDORARCH_DIR
-         Ruby_VENDORLIB_DIR)
-  string(TOUPPER ${Camel} UPPER)
-  set(${UPPER} ${${Camel}})
-endforeach ()
+           Ruby_ARCH_DIR
+           Ruby_ARCH
+           Ruby_HDR_DIR
+           Ruby_ARCHHDR_DIR
+           Ruby_RUBY_LIB_DIR
+           Ruby_SITEARCH_DIR
+           Ruby_SITELIB_DIR
+           Ruby_HAS_VENDOR_RUBY
+           Ruby_VENDORARCH_DIR
+           Ruby_VENDORLIB_DIR)
+    string(TOUPPER ${Camel} UPPER)
+    set(${UPPER} ${${Camel}})
+  endforeach ()
+endif()
diff --git a/Modules/Platform/Linker/Apple-AppleClang-Swift.cmake b/Modules/Platform/Linker/Apple-AppleClang-Swift.cmake
new file mode 100644
index 0000000..633cc90
--- /dev/null
+++ b/Modules/Platform/Linker/Apple-AppleClang-Swift.cmake
@@ -0,0 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Apple-AppleClang)
+
+__apple_linker_appleclang(Swift)
diff --git a/Modules/Platform/Linker/Apple-Swift.cmake b/Modules/Platform/Linker/Apple-Swift.cmake
new file mode 100644
index 0000000..f5404fa
--- /dev/null
+++ b/Modules/Platform/Linker/Apple-Swift.cmake
@@ -0,0 +1,5 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# AppleClang is the default linker
+include(Platform/Linker/Apple-AppleClang-Swift)
diff --git a/Modules/Platform/Linker/Linux-GNU-Swift.cmake b/Modules/Platform/Linker/Linux-GNU-Swift.cmake
new file mode 100644
index 0000000..06929ef
--- /dev/null
+++ b/Modules/Platform/Linker/Linux-GNU-Swift.cmake
@@ -0,0 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Linux-GNU)
+
+__linux_linker_gnu(Swift)
diff --git a/Modules/Platform/Linker/Linux-GNUgold-Swift.cmake b/Modules/Platform/Linker/Linux-GNUgold-Swift.cmake
new file mode 100644
index 0000000..b9b6d15
--- /dev/null
+++ b/Modules/Platform/Linker/Linux-GNUgold-Swift.cmake
@@ -0,0 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Linux-GNU-Swift)
+
+set(CMAKE_Swift_PLATFORM_LINKER_ID GNUgold)
diff --git a/Modules/Platform/Linker/Linux-LLD-Swift.cmake b/Modules/Platform/Linker/Linux-LLD-Swift.cmake
new file mode 100644
index 0000000..3428034
--- /dev/null
+++ b/Modules/Platform/Linker/Linux-LLD-Swift.cmake
@@ -0,0 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Linux-LLD)
+
+__linux_linker_lld(Swift)
diff --git a/Modules/Platform/Linker/Linux-Swift.cmake b/Modules/Platform/Linker/Linux-Swift.cmake
new file mode 100644
index 0000000..638f3c7
--- /dev/null
+++ b/Modules/Platform/Linker/Linux-Swift.cmake
@@ -0,0 +1,5 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# GNU is the default linker
+include(Platform/Linker/Linux-GNU-CXX)
diff --git a/Modules/Platform/Linker/Windows-MSVC-Swift.cmake b/Modules/Platform/Linker/Windows-MSVC-Swift.cmake
new file mode 100644
index 0000000..c29af13
--- /dev/null
+++ b/Modules/Platform/Linker/Windows-MSVC-Swift.cmake
@@ -0,0 +1,6 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Windows-MSVC)
+
+__windows_linker_msvc(Swift)
diff --git a/Modules/Platform/Linker/Windows-Swift.cmake b/Modules/Platform/Linker/Windows-Swift.cmake
new file mode 100644
index 0000000..9478d04
--- /dev/null
+++ b/Modules/Platform/Linker/Windows-Swift.cmake
@@ -0,0 +1,5 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# MSVC is the default linker
+include(Platform/Linker/Windows-MSVC-Swift)
diff --git a/Modules/Platform/Linker/WindowsPhone-MSVC-Swift.cmake b/Modules/Platform/Linker/WindowsPhone-MSVC-Swift.cmake
new file mode 100644
index 0000000..44d5b2d
--- /dev/null
+++ b/Modules/Platform/Linker/WindowsPhone-MSVC-Swift.cmake
@@ -0,0 +1,4 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Windows-MSVC-Swift)
diff --git a/Modules/Platform/Linker/WindowsPhone-Swift.cmake b/Modules/Platform/Linker/WindowsPhone-Swift.cmake
new file mode 100644
index 0000000..6d59769
--- /dev/null
+++ b/Modules/Platform/Linker/WindowsPhone-Swift.cmake
@@ -0,0 +1,5 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# MSVC is the default linker
+include(Platform/Linker/WindowsPhone-MSVC-Swift)
diff --git a/Modules/Platform/Linker/WindowsStore-MSVC-Swift.cmake b/Modules/Platform/Linker/WindowsStore-MSVC-Swift.cmake
new file mode 100644
index 0000000..44d5b2d
--- /dev/null
+++ b/Modules/Platform/Linker/WindowsStore-MSVC-Swift.cmake
@@ -0,0 +1,4 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Platform/Linker/Windows-MSVC-Swift)
diff --git a/Modules/Platform/Linker/WindowsStore-Swift.cmake b/Modules/Platform/Linker/WindowsStore-Swift.cmake
new file mode 100644
index 0000000..9262564
--- /dev/null
+++ b/Modules/Platform/Linker/WindowsStore-Swift.cmake
@@ -0,0 +1,5 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# MSVC is the default linker
+include(Platform/Linker/WindowsStore-MSVC-Swift)
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index b32d8fd..a9d03e2 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,7 +1,7 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
 set(CMake_VERSION_MINOR 31)
-set(CMake_VERSION_PATCH 20250201)
+set(CMake_VERSION_PATCH 20250204)
 #set(CMake_VERSION_RC 0)
 set(CMake_VERSION_IS_DIRTY 0)
 
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index 2ab2976..bb0b41f 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -68,6 +68,7 @@
     DoingCommandType,
     DoingRole,
     DoingBuildDir,
+    DoingCurrentBuildDir,
     DoingCount,
     DoingFilterPrefix
   };
@@ -95,6 +96,8 @@
       doing = DoingRole;
     } else if (strcmp(arg, "--build-dir") == 0) {
       doing = DoingBuildDir;
+    } else if (strcmp(arg, "--current-build-dir") == 0) {
+      doing = DoingCurrentBuildDir;
     } else if (strcmp(arg, "--filter-prefix") == 0) {
       doing = DoingFilterPrefix;
     } else if (doing == DoingOutput) {
@@ -121,6 +124,9 @@
     } else if (doing == DoingBuildDir) {
       this->Reporter.OptionBuildDir = arg;
       doing = DoingNone;
+    } else if (doing == DoingCurrentBuildDir) {
+      this->Reporter.OptionCurrentBuildDir = arg;
+      doing = DoingNone;
     } else if (doing == DoingFilterPrefix) {
       this->Reporter.OptionFilterPrefix = arg;
       doing = DoingNone;
diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx
index 1d887c8..77046a3 100644
--- a/Source/CTest/cmCTestLaunchReporter.cxx
+++ b/Source/CTest/cmCTestLaunchReporter.cxx
@@ -78,12 +78,12 @@
 
 void cmCTestLaunchReporter::LoadLabels()
 {
-  if (this->OptionBuildDir.empty() || this->OptionTargetName.empty()) {
+  if (this->OptionCurrentBuildDir.empty() || this->OptionTargetName.empty()) {
     return;
   }
 
   // Labels are listed in per-target files.
-  std::string fname = cmStrCat(this->OptionBuildDir, "/CMakeFiles/",
+  std::string fname = cmStrCat(this->OptionCurrentBuildDir, "/CMakeFiles/",
                                this->OptionTargetName, ".dir/Labels.txt");
 
   // We are interested in per-target labels for this source file.
diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h
index cf8838e..31cd9f2 100644
--- a/Source/CTest/cmCTestLaunchReporter.h
+++ b/Source/CTest/cmCTestLaunchReporter.h
@@ -37,6 +37,7 @@
   std::string OptionTargetLabels;
   std::string OptionTargetName;
   std::string OptionTargetType;
+  std::string OptionCurrentBuildDir;
   std::string OptionBuildDir;
   std::string OptionFilterPrefix;
   std::string OptionCommandType;
diff --git a/Source/cmCreateTestSourceList.cxx b/Source/cmCreateTestSourceList.cxx
index 29ff530..3b69217 100644
--- a/Source/cmCreateTestSourceList.cxx
+++ b/Source/cmCreateTestSourceList.cxx
@@ -93,7 +93,8 @@
       tests_func_name.end();
     tests_func_name.push_back(func_name);
     if (!already_declared) {
-      forwardDeclareCode += cmStrCat("int ", func_name, "(int, char*[]);\n");
+      forwardDeclareCode +=
+        cmStrCat("extern int ", func_name, "(int, char*[]);\n");
     }
   }
 
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index eceda91..cd1938e 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -1846,7 +1846,7 @@
 }
 
 cmFindPackageCommand::AppendixMap cmFindPackageCommand::FindAppendices(
-  std::string const& base) const
+  std::string const& base, cmPackageInfoReader const& baseReader) const
 {
   AppendixMap appendices;
 
@@ -1865,9 +1865,11 @@
       }
 
       std::unique_ptr<cmPackageInfoReader> reader =
-        cmPackageInfoReader::Read(extra, this->CpsReader.get());
+        cmPackageInfoReader::Read(extra, &baseReader);
       if (reader && reader->GetName() == this->Name) {
-        appendices.emplace(extra, std::move(reader));
+        std::vector<std::string> components = reader->GetComponentNames();
+        Appendix appendix{ std::move(reader), std::move(components) };
+        appendices.emplace(extra, std::move(appendix));
       }
     }
   }
@@ -1894,27 +1896,55 @@
 
 bool cmFindPackageCommand::ReadPackage()
 {
-  // Resolve any transitive dependencies.
+  // Resolve any transitive dependencies for the root file.
   if (!FindPackageDependencies(this->FileFound, *this->CpsReader,
                                this->Required)) {
     return false;
   }
 
+  auto const hasComponentsRequested =
+    !this->RequiredComponents.empty() || !this->OptionalComponents.empty();
+
   cmMakefile::CallRAII scope{ this->Makefile, this->FileFound, this->Status };
 
-  // Locate appendices.
-  cmFindPackageCommand::AppendixMap appendices =
-    this->FindAppendices(this->FileFound);
+  // Loop over appendices.
+  auto iter = this->CpsAppendices.begin();
+  while (iter != this->CpsAppendices.end()) {
+    bool required = false;
+    bool important = false;
 
-  auto iter = appendices.begin();
-  while (iter != appendices.end()) {
-    bool providesRequiredComponents = false; // TODO
-    bool required = providesRequiredComponents && this->Required;
-    if (!this->FindPackageDependencies(iter->first, *iter->second, required)) {
-      if (providesRequiredComponents) {
+    // Check if this appendix provides any requested components.
+    if (hasComponentsRequested) {
+      auto providesAny = [&iter](
+                           std::set<std::string> const& desiredComponents) {
+        return std::any_of(iter->second.Components.begin(),
+                           iter->second.Components.end(),
+                           [&desiredComponents](std::string const& component) {
+                             return cm::contains(desiredComponents, component);
+                           });
+      };
+
+      if (providesAny(this->RequiredComponents)) {
+        important = true;
+        required = this->Required;
+      } else if (!providesAny(this->OptionalComponents)) {
+        // This appendix doesn't provide any requested components; remove it
+        // from the set to be imported.
+        iter = this->CpsAppendices.erase(iter);
+        continue;
+      }
+    }
+
+    // Resolve any transitive dependencies for the appendix.
+    if (!this->FindPackageDependencies(iter->first, iter->second, required)) {
+      if (important) {
+        // Some dependencies are missing, and we need(ed) this appendix; fail.
         return false;
       }
-      iter = appendices.erase(iter);
+
+      // Some dependencies are missing, but we don't need this appendix; remove
+      // it from the set to be imported.
+      iter = this->CpsAppendices.erase(iter);
     } else {
       ++iter;
     }
@@ -1926,10 +1956,11 @@
   }
 
   // Import targets from appendices.
-  for (auto const& appendix : appendices) {
+  // NOLINTNEXTLINE(readability-use-anyofallof)
+  for (auto const& appendix : this->CpsAppendices) {
     cmMakefile::CallRAII appendixScope{ this->Makefile, appendix.first,
                                         this->Status };
-    if (!this->ImportPackageTargets(appendix.first, *appendix.second)) {
+    if (!this->ImportPackageTargets(appendix.first, appendix.second)) {
       return false;
     }
   }
@@ -1969,6 +2000,8 @@
 
     // Try to find the requirement; fail if we can't.
     if (!fp.FindPackage() || fp.FileFound.empty()) {
+      this->SetError(cmStrCat("could not find "_s, dep.Name,
+                              ", required by "_s, this->Name, '.'));
       return false;
     }
   }
@@ -2687,29 +2720,63 @@
       cm::optional<std::string> cpsVersion = reader->GetVersion();
       if (cpsVersion) {
         // TODO: Implement version check for CPS
-        this->VersionFound = (version = std::move(*cpsVersion));
-
-        std::vector<unsigned> const& versionParts = reader->ParseVersion();
-        this->VersionFoundCount = static_cast<unsigned>(versionParts.size());
-        switch (this->VersionFoundCount) {
-          case 4:
-            this->VersionFoundTweak = versionParts[3];
-            CM_FALLTHROUGH;
-          case 3:
-            this->VersionFoundPatch = versionParts[2];
-            CM_FALLTHROUGH;
-          case 2:
-            this->VersionFoundMinor = versionParts[1];
-            CM_FALLTHROUGH;
-          case 1:
-            this->VersionFoundMajor = versionParts[0];
-            CM_FALLTHROUGH;
-          default:
-            break;
-        }
+        result = true;
+      } else {
+        result = this->Version.empty();
       }
-      this->CpsReader = std::move(reader);
-      result = true;
+
+      if (result) {
+        // Locate appendices.
+        cmFindPackageCommand::AppendixMap appendices =
+          this->FindAppendices(config_file, *reader);
+
+        // Collect available components.
+        std::set<std::string> allComponents;
+
+        std::vector<std::string> const& rootComponents =
+          reader->GetComponentNames();
+        allComponents.insert(rootComponents.begin(), rootComponents.end());
+
+        for (auto const& appendix : appendices) {
+          allComponents.insert(appendix.second.Components.begin(),
+                               appendix.second.Components.end());
+        }
+
+        // Verify that all required components are available.
+        std::vector<std::string> missingComponents;
+        std::set_difference(this->RequiredComponents.begin(),
+                            this->RequiredComponents.end(),
+                            allComponents.begin(), allComponents.end(),
+                            std::back_inserter(missingComponents));
+        if (!missingComponents.empty()) {
+          result = false;
+        }
+
+        if (result && cpsVersion) {
+          this->VersionFound = (version = std::move(*cpsVersion));
+
+          std::vector<unsigned> const& versionParts = reader->ParseVersion();
+          this->VersionFoundCount = static_cast<unsigned>(versionParts.size());
+          switch (this->VersionFoundCount) {
+            case 4:
+              this->VersionFoundTweak = versionParts[3];
+              CM_FALLTHROUGH;
+            case 3:
+              this->VersionFoundPatch = versionParts[2];
+              CM_FALLTHROUGH;
+            case 2:
+              this->VersionFoundMinor = versionParts[1];
+              CM_FALLTHROUGH;
+            case 1:
+              this->VersionFoundMajor = versionParts[0];
+              CM_FALLTHROUGH;
+            default:
+              break;
+          }
+        }
+        this->CpsReader = std::move(reader);
+        this->CpsAppendices = std::move(appendices);
+      }
     }
   } else {
     // Get the filename without the .cmake extension.
diff --git a/Source/cmFindPackageCommand.h b/Source/cmFindPackageCommand.h
index 4ced5d3..75a4243 100644
--- a/Source/cmFindPackageCommand.h
+++ b/Source/cmFindPackageCommand.h
@@ -141,9 +141,16 @@
   bool ReadListFile(std::string const& f, PolicyScopeRule psr);
   bool ReadPackage();
 
-  using AppendixMap =
-    std::map<std::string, std::unique_ptr<cmPackageInfoReader>>;
-  AppendixMap FindAppendices(std::string const& base) const;
+  struct Appendix
+  {
+    std::unique_ptr<cmPackageInfoReader> Reader;
+    std::vector<std::string> Components;
+
+    operator cmPackageInfoReader&() const { return *this->Reader; }
+  };
+  using AppendixMap = std::map<std::string, Appendix>;
+  AppendixMap FindAppendices(std::string const& base,
+                             cmPackageInfoReader const& baseReader) const;
   bool FindPackageDependencies(std::string const& fileName,
                                cmPackageInfoReader const& reader,
                                bool required);
@@ -299,6 +306,7 @@
   std::vector<ConfigFileInfo> ConsideredConfigs;
 
   std::unique_ptr<cmPackageInfoReader> CpsReader;
+  AppendixMap CpsAppendices;
 
   friend struct std::hash<ConfigFileInfo>;
 };
diff --git a/Source/cmPackageInfoReader.cxx b/Source/cmPackageInfoReader.cxx
index ceefb6f..a5349ca 100644
--- a/Source/cmPackageInfoReader.cxx
+++ b/Source/cmPackageInfoReader.cxx
@@ -481,6 +481,18 @@
   return requirements;
 }
 
+std::vector<std::string> cmPackageInfoReader::GetComponentNames() const
+{
+  std::vector<std::string> componentNames;
+
+  Json::Value const& components = this->Data["components"];
+  for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
+    componentNames.emplace_back(ci.name());
+  }
+
+  return componentNames;
+}
+
 std::string cmPackageInfoReader::ResolvePath(std::string path) const
 {
   cmSystemTools::ConvertToUnixSlashes(path);
diff --git a/Source/cmPackageInfoReader.h b/Source/cmPackageInfoReader.h
index bcecd0e..d31cee9 100644
--- a/Source/cmPackageInfoReader.h
+++ b/Source/cmPackageInfoReader.h
@@ -50,6 +50,7 @@
   std::vector<unsigned> ParseVersion() const;
 
   std::vector<cmPackageRequirement> GetRequirements() const;
+  std::vector<std::string> GetComponentNames() const;
 
   /// Create targets for components specified in the CPS file.
   bool ImportTargets(cmMakefile* makefile, cmExecutionStatus& status);
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 60fa0b0..d8a63a6 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -552,6 +552,9 @@
          "add_feature_info() supports full Condition Syntax.", 4, 0, 0, WARN) \
   SELECT(POLICY, CMP0184,                                                     \
          "MSVC runtime check flags are selected by an abstraction.", 4, 0, 0, \
+         WARN)                                                                \
+  SELECT(POLICY, CMP0185,                                                     \
+         "FindRuby no longer provides upper-case RUBY_* variables.", 4, 0, 0, \
          WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index 3f2dc7f..f9499b5 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -2675,13 +2675,14 @@
     std::string launcher;
     if (mf->IsOn("CTEST_USE_LAUNCHERS")) {
       launcher =
-        cmStrCat("\"", cmSystemTools::GetCTestCommand(), "\" --launch ");
+        cmStrCat("\"", cmSystemTools::GetCTestCommand(), "\" --launch ",
+                 "--current-build-dir <CMAKE_CURRENT_BINARY_DIR> ");
     } else {
       launcher =
         cmStrCat("\"", cmSystemTools::GetCTestCommand(), "\" --instrument ");
     }
     std::string common_args =
-      cmStrCat(" --target-name <TARGET_NAME> ", "--build-dir \"",
+      cmStrCat(" --target-name <TARGET_NAME> --build-dir \"",
                this->State->GetBinaryDirectory(), "\" ");
     this->State->SetGlobalProperty(
       "RULE_LAUNCH_COMPILE",
diff --git a/Tests/CMakeLib/testCMExtEnumSet.cxx b/Tests/CMakeLib/testCMExtEnumSet.cxx
index c08ea11..218b837 100644
--- a/Tests/CMakeLib/testCMExtEnumSet.cxx
+++ b/Tests/CMakeLib/testCMExtEnumSet.cxx
@@ -68,21 +68,6 @@
       ++failed;
     }
   }
-  {
-    enum class Test : std::uint8_t
-    {
-      A,
-      B,
-      C,
-      D,
-      cm_count = D
-    };
-    cm::enum_set<Test> testSet1;
-
-    if (testSet1.size() != 0 || testSet1.max_size() != 4) {
-      ++failed;
-    }
-  }
 }
 
 void testIteration()
@@ -94,10 +79,9 @@
     A,
     B,
     C,
-    D,
-    cm_count = D
+    D
   };
-  cm::enum_set<Test> testSet{ Test::A, Test::C, Test::B };
+  cm::enum_set<Test, 4> testSet{ Test::A, Test::C, Test::B };
 
   if (testSet.size() != 3) {
     ++failed;
@@ -134,8 +118,7 @@
     B,
     C,
     D,
-    E,
-    cm_count = E
+    E
   };
 
   {
@@ -281,32 +264,42 @@
     }
   }
   {
-    cm::enum_set<Test> testSet1;
-    cm::enum_set<Test> testSet2{ Test::A, Test::C, Test::B };
+    using ESet = cm::enum_set<Test, 5>;
+    ESet testSet1;
+    ESet testSet2{ Test::A, Test::C, Test::B };
 
     testSet1.set();
     if (testSet1.size() != 5 || testSet1.size() != testSet1.max_size()) {
       ++failed;
     }
-    testSet1.flip(Test::D | Test::E);
+    testSet1.flip({ Test::D, Test::E });
     if (testSet1.size() != 3 || testSet1 != testSet2) {
       ++failed;
     }
-    testSet1.flip(Test::D);
-    testSet2 += Test::D;
+    testSet1.flip(Test::D | Test::E);
+    testSet2 += Test::D + Test::E;
+    if (testSet1.size() != 5 || testSet1 != testSet2) {
+      ++failed;
+    }
+    testSet1.flip(Test::E);
+    testSet2 -= Test::E;
     if (testSet1.size() != 4 || testSet1 != testSet2) {
       ++failed;
     }
     testSet1 ^= { Test::A, Test::B, Test::E, Test::D };
-    testSet2 = Test::C + Test::E;
+    testSet2 = { Test::C, Test::E };
     if (testSet1.size() != 2 || testSet1 != testSet2) {
       ++failed;
     }
-    testSet1 ^= Test::A | Test::B | Test::E;
+    testSet1 ^= { Test::A, Test::B, Test::E };
     testSet2 = { Test::A, Test::B, Test::C };
     if (testSet1.size() != 3 || testSet1 != testSet2) {
       ++failed;
     }
+    testSet2 = Test::A | Test::B | Test::C;
+    if (testSet1.size() != 3 || testSet1 != testSet2) {
+      ++failed;
+    }
   }
 }
 
@@ -320,8 +313,7 @@
       A,
       B,
       C,
-      D,
-      cm_count = D
+      D
     };
 
     cm::enum_set<Test> testSet;
@@ -353,11 +345,10 @@
       A,
       B,
       C,
-      D,
-      cm_count = D
+      D
     };
 
-    cm::enum_set<Test> testSet;
+    cm::enum_set<Test, 4> testSet;
 
     if (!testSet.none()) {
       ++failed;
@@ -382,8 +373,7 @@
       A,
       B,
       C,
-      D,
-      cm_count = D
+      D
     };
 
     cm::enum_set<Test> testSet1;
diff --git a/Tests/CommandLineTest/CMakeLists.txt b/Tests/CommandLineTest/CMakeLists.txt
index 0d7b748..6c88574 100644
--- a/Tests/CommandLineTest/CMakeLists.txt
+++ b/Tests/CommandLineTest/CMakeLists.txt
@@ -19,9 +19,7 @@
 EXEC_CMAKE_COMMAND(--help add_executable)
 EXEC_CMAKE_COMMAND(--help-command add_executable)
 EXEC_CMAKE_COMMAND(--help-full "${CMAKE_CURRENT_BINARY_DIR}/cmake.txt")
-EXEC_CMAKE_COMMAND(--help-man "${CMAKE_CURRENT_BINARY_DIR}/cmake.man")
-EXEC_CMAKE_COMMAND(--help-html "${CMAKE_CURRENT_BINARY_DIR}/cmake.html")
-EXEC_CMAKE_COMMAND(--copyright "${CMAKE_CURRENT_BINARY_DIR}/Copyright.txt")
+EXEC_CMAKE_COMMAND(--help-manual "cmake(1)" "${CMAKE_CURRENT_BINARY_DIR}/cmake.man")
 EXEC_CMAKE_COMMAND(--version "${CMAKE_CURRENT_BINARY_DIR}/version.txt")
 
 add_executable(CommandLineTest CommandLineTest.cxx)
@@ -49,10 +47,8 @@
 EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -N -I 10-)
 EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -N -I 3,4)
 EXEC_CTEST_COMMAND(--help)
-EXEC_CTEST_COMMAND(--copyright)
 EXEC_CTEST_COMMAND(--help-full "${CMAKE_CURRENT_BINARY_DIR}/ctest.txt")
-EXEC_CTEST_COMMAND(--help-man "${CMAKE_CURRENT_BINARY_DIR}/ctest.man")
-EXEC_CTEST_COMMAND(--help-html "${CMAKE_CURRENT_BINARY_DIR}/ctest.html")
+EXEC_CTEST_COMMAND(--help-manual "ctest(1)" "${CMAKE_CURRENT_BINARY_DIR}/ctest.man")
 EXEC_CTEST_COMMAND(--version)
 
 if(THIS_SHOULD_BE_SET)
diff --git a/Tests/FindPackageCpsTest/CMakeLists.txt b/Tests/FindPackageCpsTest/CMakeLists.txt
index 19f1bf3..58de268 100644
--- a/Tests/FindPackageCpsTest/CMakeLists.txt
+++ b/Tests/FindPackageCpsTest/CMakeLists.txt
@@ -185,3 +185,49 @@
 elseif(NOT TARGET Bar::Target2)
   message(SEND_ERROR "Bar::Target2 missing !")
 endif()
+
+###############################################################################
+# Test requesting components from a package.
+
+find_package(ComponentTest
+  COMPONENTS Target1 Target2
+  OPTIONAL_COMPONENTS Target4 Target6)
+if(NOT ComponentTest_FOUND)
+  message(SEND_ERROR "ComponentTest not found !")
+elseif(NOT TARGET ComponentTest::Target1)
+  message(SEND_ERROR "ComponentTest::Target1 missing !")
+elseif(NOT TARGET ComponentTest::Target2)
+  message(SEND_ERROR "ComponentTest::Target2 missing !")
+elseif(NOT TARGET ComponentTest::Target3)
+  message(SEND_ERROR "ComponentTest::Target3 missing !")
+elseif(NOT TARGET ComponentTest::Target4)
+  message(SEND_ERROR "ComponentTest::Target4 missing !")
+elseif(NOT TARGET ComponentTest::Target5)
+  message(SEND_ERROR "ComponentTest::Target5 missing !")
+elseif(TARGET ComponentTest::Target6)
+  message(SEND_ERROR "ComponentTest::Target6 exists ?!")
+elseif(TARGET ComponentTest::Target7)
+  message(SEND_ERROR "ComponentTest::Target7 exists ?!")
+elseif(TARGET ComponentTest::Target8)
+  message(SEND_ERROR "ComponentTest::Target8 exists ?!")
+endif()
+
+###############################################################################
+# Test requesting components from a dependency.
+
+find_package(TransitiveTest)
+if(NOT TransitiveTest_FOUND)
+  message(SEND_ERROR "TransitiveTest not found !")
+elseif(NOT TransitiveDep_FOUND)
+  message(SEND_ERROR "TransitiveTest's TransitiveDep not found !")
+elseif(NOT TARGET TransitiveDep::Target1)
+  message(SEND_ERROR "TransitiveDep::Target1 missing !")
+elseif(NOT TARGET TransitiveDep::Target2)
+  message(SEND_ERROR "TransitiveDep::Target2 missing !")
+elseif(NOT TARGET TransitiveDep::Target3)
+  message(SEND_ERROR "TransitiveDep::Target3 missing !")
+elseif(TARGET TransitiveDep::Target4)
+  message(SEND_ERROR "TransitiveDep::Target4 exists ?!")
+elseif(TARGET TransitiveDep::Target5)
+  message(SEND_ERROR "TransitiveDep::Target5 exists ?!")
+endif()
diff --git a/Tests/FindPackageCpsTest/cps/ComponentTest-extra1.cps b/Tests/FindPackageCpsTest/cps/ComponentTest-extra1.cps
new file mode 100644
index 0000000..267ee49
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/ComponentTest-extra1.cps
@@ -0,0 +1,12 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "components": {
+    "Target2": {
+      "type": "interface"
+    },
+    "Target3": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/ComponentTest-extra2.cps b/Tests/FindPackageCpsTest/cps/ComponentTest-extra2.cps
new file mode 100644
index 0000000..a1072c8
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/ComponentTest-extra2.cps
@@ -0,0 +1,12 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "components": {
+    "Target4": {
+      "type": "interface"
+    },
+    "Target5": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/ComponentTest-extra3.cps b/Tests/FindPackageCpsTest/cps/ComponentTest-extra3.cps
new file mode 100644
index 0000000..683911f
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/ComponentTest-extra3.cps
@@ -0,0 +1,15 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "requires": {
+    "DoesNotExist": null
+  },
+  "components": {
+    "Target6": {
+      "type": "interface"
+    },
+    "Target7": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/ComponentTest-extra4.cps b/Tests/FindPackageCpsTest/cps/ComponentTest-extra4.cps
new file mode 100644
index 0000000..badd41a
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/ComponentTest-extra4.cps
@@ -0,0 +1,9 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "components": {
+    "Target8": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/ComponentTest.cps b/Tests/FindPackageCpsTest/cps/ComponentTest.cps
new file mode 100644
index 0000000..763d60e
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/ComponentTest.cps
@@ -0,0 +1,10 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "cps_path": "@prefix@/cps",
+  "components": {
+    "Target1": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/TransitiveDep-extra1.cps b/Tests/FindPackageCpsTest/cps/TransitiveDep-extra1.cps
new file mode 100644
index 0000000..312a804
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/TransitiveDep-extra1.cps
@@ -0,0 +1,12 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveDep",
+  "components": {
+    "Target2": {
+      "type": "interface"
+    },
+    "Target3": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/TransitiveDep-extra2.cps b/Tests/FindPackageCpsTest/cps/TransitiveDep-extra2.cps
new file mode 100644
index 0000000..b1dd4bd
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/TransitiveDep-extra2.cps
@@ -0,0 +1,9 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveDep",
+  "components": {
+    "Target4": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/TransitiveDep-extra3.cps b/Tests/FindPackageCpsTest/cps/TransitiveDep-extra3.cps
new file mode 100644
index 0000000..6c307ba
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/TransitiveDep-extra3.cps
@@ -0,0 +1,12 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "requires": {
+    "DoesNotExist": null
+  },
+  "components": {
+    "Target5": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/TransitiveDep.cps b/Tests/FindPackageCpsTest/cps/TransitiveDep.cps
new file mode 100644
index 0000000..1af27bb
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/TransitiveDep.cps
@@ -0,0 +1,10 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveDep",
+  "cps_path": "@prefix@/cps",
+  "components": {
+    "Target1": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/FindPackageCpsTest/cps/TransitiveTest.cps b/Tests/FindPackageCpsTest/cps/TransitiveTest.cps
new file mode 100644
index 0000000..4c5e31e
--- /dev/null
+++ b/Tests/FindPackageCpsTest/cps/TransitiveTest.cps
@@ -0,0 +1,11 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveTest",
+  "cps_path": "@prefix@/cps",
+  "requires": {
+    "TransitiveDep": {
+      "components": [ "Target2" ]
+    }
+  },
+  "components": {}
+}
diff --git a/Tests/FindRuby/Test/CMakeLists.txt b/Tests/FindRuby/Test/CMakeLists.txt
index dcf3ec3..fb5ae37 100644
--- a/Tests/FindRuby/Test/CMakeLists.txt
+++ b/Tests/FindRuby/Test/CMakeLists.txt
@@ -2,11 +2,24 @@
 project(TestRuby LANGUAGES C)
 include(CTest)
 
+cmake_policy(SET CMP0185 NEW)
+
 find_package(Ruby 1.9.9 REQUIRED)
 if (NOT Ruby_FOUND)
   message (FATAL_ERROR "Failed to find Ruby >=1.9.9")
 endif()
 
+foreach(var_CMP0185
+    RUBY_EXECUTABLE
+    RUBY_INCLUDE_DIRS
+    RUBY_LIBRARY
+    RUBY_VERSION
+    )
+  if(DEFINED ${var_CMP0185})
+    message(FATAL_ERROR "Pre-CMP0185 result variable is set: ${var_CMP0185}")
+  endif()
+endforeach()
+
 add_executable(ruby_version ruby_version.c)
 target_include_directories(ruby_version PRIVATE ${Ruby_INCLUDE_DIRS})
 target_link_libraries(ruby_version PRIVATE ${Ruby_LIBRARIES})
diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-result.txt b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-stderr.txt b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-stderr.txt
new file mode 100644
index 0000000..e9327a3
--- /dev/null
+++ b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-stderr.txt
@@ -0,0 +1,2 @@
+Unable to find executable:.*MyThirdPartyDependency/src(/[^/
+]+)?/third_party
diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-stdout.txt b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-stdout.txt
new file mode 100644
index 0000000..c2c1bc7
--- /dev/null
+++ b/Tests/RunCMake/ctest_labels_for_subprojects/CTestScriptVariableCommandLineWithInstrumentation-stdout.txt
@@ -0,0 +1,9 @@
+0% tests passed, 1 tests failed out of 1
++
+Subproject Time Summary:
+MyThirdPartyDependency += +[0-9.]+ sec\*proc \(1 test\)
++
+Label Time Summary:
+NotASubproject += +[0-9.]+ sec\*proc \(1 test\)
++
+Total Test time \(real\) = +[0-9.]+ sec
diff --git a/Tests/RunCMake/ctest_labels_for_subprojects/RunCMakeTest.cmake b/Tests/RunCMake/ctest_labels_for_subprojects/RunCMakeTest.cmake
index dbe7bf8..9b3b6f4 100644
--- a/Tests/RunCMake/ctest_labels_for_subprojects/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_labels_for_subprojects/RunCMakeTest.cmake
@@ -28,23 +28,37 @@
 # 2. Specify subprojects via a CTest script variable on the command line e.g.
 #    ctest -S test.cmake -DCTEST_LABELS_FOR_SUBPROJECTS:STRING="MySubproject"
 # Note: This test includes a failing build
-function(run_CTestScriptVariableCommandLine)
+# Note: Also use --instrumentation mode to ensure it doesn't interfere with label generation
+function(run_CTestScriptVariableCommandLine USE_INSTRUMENTATION)
   set(CTEST_EXTRA_CONFIG "set(CTEST_USE_LAUNCHERS 1)")
   set(CASE_TEST_PREFIX_CODE [[
 file(COPY "${CTEST_RUNCMAKE_SOURCE_DIRECTORY}/MyThirdPartyDependency"
   DESTINATION ${CTEST_SOURCE_DIRECTORY})
   ]])
-  set(CASE_CMAKELISTS_SUFFIX_CODE [[
-add_subdirectory(MyThirdPartyDependency)
-  ]])
-
-  run_ctest(CTestScriptVariableCommandLine "-DCTEST_LABELS_FOR_SUBPROJECTS:STRING=MyThirdPartyDependency")
+  if(USE_INSTRUMENTATION)
+    set(CASE_CMAKELISTS_SUFFIX_CODE [[
+  add_subdirectory(MyThirdPartyDependency)
+  set(CMAKE_EXPERIMENTAL_INSTRUMENTATION "a37d1069-1972-4901-b9c9-f194aaf2b6e0")
+  cmake_instrumentation(DATA_VERSION 1 API_VERSION 1)
+    ]])
+    set(RunCMake-check-file CTestScriptVariableCommandLine-check.cmake)
+    run_ctest(CTestScriptVariableCommandLineWithInstrumentation
+      "-DCTEST_LABELS_FOR_SUBPROJECTS:STRING=MyThirdPartyDependency")
+    unset(RunCMake-check-file)
+  else()
+    set(CASE_CMAKELISTS_SUFFIX_CODE [[
+  add_subdirectory(MyThirdPartyDependency)
+    ]])
+    run_ctest(CTestScriptVariableCommandLine
+      "-DCTEST_LABELS_FOR_SUBPROJECTS:STRING=MyThirdPartyDependency")
+  endif()
 
   unset(CTEST_EXTRA_CONFIG)
   unset(CASE_TEST_PREFIX_CODE)
   unset(CASE_CMAKELISTS_SUFFIX_CODE)
 endfunction()
-run_CTestScriptVariableCommandLine()
+run_CTestScriptVariableCommandLine(ON)
+run_CTestScriptVariableCommandLine(OFF)
 
 # 3. Set subprojects via a CTest module variable on the command line
 #    (will populate DartConfiguration.tcl)
diff --git a/Tests/RunCMake/find_package/MissingComponent-result.txt b/Tests/RunCMake/find_package/MissingComponent-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingComponent-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/MissingComponent-stderr.txt b/Tests/RunCMake/find_package/MissingComponent-stderr.txt
new file mode 100644
index 0000000..3386c30
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingComponent-stderr.txt
@@ -0,0 +1,11 @@
+CMake Error at MissingComponent.cmake:[0-9]+ \(find_package\):
+  Could not find a configuration file for package "ComponentTest" that is
+  compatible with requested version ""\.
+
+  The following configuration files were considered but not accepted:
+(
+    [^
+]*/Tests/RunCMake/find_package/cps/[Cc]omponent[Tt]est\.cps, version: unknown)+
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/MissingComponent.cmake b/Tests/RunCMake/find_package/MissingComponent.cmake
new file mode 100644
index 0000000..215b131
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingComponent.cmake
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.31)
+
+set(CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES "e82e467b-f997-4464-8ace-b00808fff261")
+
+# Protect tests from running inside the default install prefix.
+set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/NotDefaultPrefix")
+
+# Disable built-in search paths.
+set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
+set(CMAKE_FIND_USE_INSTALL_PREFIX OFF)
+
+set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+
+###############################################################################
+# Test requesting unavailable components from a package.
+find_package(ComponentTest REQUIRED COMPONENTS DoesNotExist)
diff --git a/Tests/RunCMake/find_package/MissingComponentDependency-result.txt b/Tests/RunCMake/find_package/MissingComponentDependency-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingComponentDependency-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/MissingComponentDependency-stderr.txt b/Tests/RunCMake/find_package/MissingComponentDependency-stderr.txt
new file mode 100644
index 0000000..939a094
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingComponentDependency-stderr.txt
@@ -0,0 +1,22 @@
+CMake Error in cps/componenttest-extra\.cps:
+  Could not find a package configuration file provided by "DoesNotExist" with
+  any of the following names:
+
+    DoesNotExist\.cps
+    doesnotexist\.cps
+    DoesNotExistConfig\.cmake
+    doesnotexist-config\.cmake
+
+  Add the installation prefix of "DoesNotExist" to CMAKE_PREFIX_PATH or set
+  "DoesNotExist_DIR" to a directory containing one of the above files\.  If
+  "DoesNotExist" provides a separate development package or SDK, be sure it
+  has been installed\.
+Call Stack \(most recent call first\):
+  cps/[Cc]omponent[Tt]est\.cps
+  MissingComponentDependency\.cmake:[0-9]+ \(find_package\)
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at MissingComponentDependency.cmake:[0-9]+ \(find_package\):
+  find_package could not find DoesNotExist, required by ComponentTest\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/MissingComponentDependency.cmake b/Tests/RunCMake/find_package/MissingComponentDependency.cmake
new file mode 100644
index 0000000..576debf
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingComponentDependency.cmake
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.31)
+
+set(CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES "e82e467b-f997-4464-8ace-b00808fff261")
+
+# Protect tests from running inside the default install prefix.
+set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/NotDefaultPrefix")
+
+# Disable built-in search paths.
+set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
+set(CMAKE_FIND_USE_INSTALL_PREFIX OFF)
+
+set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+
+###############################################################################
+# Test requesting components with missing dependencies from a package.
+find_package(ComponentTest REQUIRED COMPONENTS Incomplete)
diff --git a/Tests/RunCMake/find_package/MissingTransitiveComponent-result.txt b/Tests/RunCMake/find_package/MissingTransitiveComponent-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingTransitiveComponent-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/MissingTransitiveComponent-stderr.txt b/Tests/RunCMake/find_package/MissingTransitiveComponent-stderr.txt
new file mode 100644
index 0000000..fea5855
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingTransitiveComponent-stderr.txt
@@ -0,0 +1,17 @@
+CMake Error in cps/[Tt]ransitive[Mm]issing\.cps:
+  Could not find a configuration file for package "ComponentTest" that is
+  compatible with requested version ""\.
+
+  The following configuration files were considered but not accepted:
+(
+    [^
+]*/Tests/RunCMake/find_package/cps/[Cc]omponent[Tt]est\.cps, version: unknown)+
+
+Call Stack \(most recent call first\):
+  MissingTransitiveComponent\.cmake:[0-9]+ \(find_package\)
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at MissingTransitiveComponent\.cmake:[0-9]+ \(find_package\):
+  find_package could not find ComponentTest, required by TransitiveMissing\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/MissingTransitiveComponent.cmake b/Tests/RunCMake/find_package/MissingTransitiveComponent.cmake
new file mode 100644
index 0000000..d0b623b
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingTransitiveComponent.cmake
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.31)
+
+set(CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES "e82e467b-f997-4464-8ace-b00808fff261")
+
+# Protect tests from running inside the default install prefix.
+set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/NotDefaultPrefix")
+
+# Disable built-in search paths.
+set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
+set(CMAKE_FIND_USE_INSTALL_PREFIX OFF)
+
+set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+
+###############################################################################
+# Test depending on components of another package which are unavailable.
+find_package(TransitiveMissing REQUIRED)
diff --git a/Tests/RunCMake/find_package/MissingTransitiveComponentDependency-result.txt b/Tests/RunCMake/find_package/MissingTransitiveComponentDependency-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingTransitiveComponentDependency-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/find_package/MissingTransitiveComponentDependency-stderr.txt b/Tests/RunCMake/find_package/MissingTransitiveComponentDependency-stderr.txt
new file mode 100644
index 0000000..98814d4
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingTransitiveComponentDependency-stderr.txt
@@ -0,0 +1,24 @@
+CMake Error in cps/componenttest-extra\.cps:
+  Could not find a package configuration file provided by "DoesNotExist" with
+  any of the following names:
+
+    DoesNotExist\.cps
+    doesnotexist\.cps
+    DoesNotExistConfig\.cmake
+    doesnotexist-config\.cmake
+
+  Add the installation prefix of "DoesNotExist" to CMAKE_PREFIX_PATH or set
+  "DoesNotExist_DIR" to a directory containing one of the above files\.  If
+  "DoesNotExist" provides a separate development package or SDK, be sure it
+  has been installed\.
+Call Stack \(most recent call first\):
+  cps/[Cc]omponent[Tt]est\.cps
+  cps/[Tt]ransitive[Ii]ncomplete\.cps
+  MissingTransitiveComponentDependency\.cmake:[0-9]+ \(find_package\)
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at MissingTransitiveComponentDependency\.cmake:[0-9]+ \(find_package\):
+  find_package could not find ComponentTest, required by
+  TransitiveIncomplete\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/MissingTransitiveComponentDependency.cmake b/Tests/RunCMake/find_package/MissingTransitiveComponentDependency.cmake
new file mode 100644
index 0000000..df40186
--- /dev/null
+++ b/Tests/RunCMake/find_package/MissingTransitiveComponentDependency.cmake
@@ -0,0 +1,20 @@
+cmake_minimum_required(VERSION 3.31)
+
+set(CMAKE_EXPERIMENTAL_FIND_CPS_PACKAGES "e82e467b-f997-4464-8ace-b00808fff261")
+
+# Protect tests from running inside the default install prefix.
+set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/NotDefaultPrefix")
+
+# Disable built-in search paths.
+set(CMAKE_FIND_USE_PACKAGE_ROOT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH OFF)
+set(CMAKE_FIND_USE_CMAKE_SYSTEM_PATH OFF)
+set(CMAKE_FIND_USE_INSTALL_PREFIX OFF)
+
+set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR})
+
+###############################################################################
+# Test depending on components of another package which are missing
+# dependencies.
+find_package(TransitiveIncomplete REQUIRED)
diff --git a/Tests/RunCMake/find_package/MissingTransitiveDependency-stderr.txt b/Tests/RunCMake/find_package/MissingTransitiveDependency-stderr.txt
index ebb77a9..eaa3cd2 100644
--- a/Tests/RunCMake/find_package/MissingTransitiveDependency-stderr.txt
+++ b/Tests/RunCMake/find_package/MissingTransitiveDependency-stderr.txt
@@ -23,3 +23,8 @@
   cps/[Ii]ncomplete\.cps
   MissingTransitiveDependency\.cmake:[0-9]+ \(find_package\)
   CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at MissingTransitiveDependency\.cmake:[0-9]+ \(find_package\):
+  find_package could not find StillIncomplete, required by Incomplete\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index d5a7f8b..27b0c1a 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -23,6 +23,10 @@
 run_cmake(MissingConfigRequired)
 run_cmake(MissingConfigVersion)
 run_cmake(MissingTransitiveDependency)
+run_cmake(MissingComponent)
+run_cmake(MissingComponentDependency)
+run_cmake(MissingTransitiveComponent)
+run_cmake(MissingTransitiveComponentDependency)
 run_cmake(MixedModeOptions)
 run_cmake_with_options(ModuleModeDebugPkg --debug-find-pkg=Foo,Zot)
 run_cmake(PackageRoot)
diff --git a/Tests/RunCMake/find_package/cps/componenttest-extra.cps b/Tests/RunCMake/find_package/cps/componenttest-extra.cps
new file mode 100644
index 0000000..7cca03e
--- /dev/null
+++ b/Tests/RunCMake/find_package/cps/componenttest-extra.cps
@@ -0,0 +1,12 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "requires": {
+    "DoesNotExist": null
+  },
+  "components": {
+    "Incomplete": {
+      "type": "interface"
+    }
+  }
+}
diff --git a/Tests/RunCMake/find_package/cps/componenttest.cps b/Tests/RunCMake/find_package/cps/componenttest.cps
new file mode 100644
index 0000000..ef49af4
--- /dev/null
+++ b/Tests/RunCMake/find_package/cps/componenttest.cps
@@ -0,0 +1,6 @@
+{
+  "cps_version": "0.13",
+  "name": "ComponentTest",
+  "cps_path": "@prefix@/cps",
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package/cps/transitiveincomplete.cps b/Tests/RunCMake/find_package/cps/transitiveincomplete.cps
new file mode 100644
index 0000000..9d0f8c2
--- /dev/null
+++ b/Tests/RunCMake/find_package/cps/transitiveincomplete.cps
@@ -0,0 +1,11 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveIncomplete",
+  "cps_path": "@prefix@/cps",
+  "requires": {
+    "ComponentTest": {
+      "components": [ "Incomplete" ]
+    }
+  },
+  "components": {}
+}
diff --git a/Tests/RunCMake/find_package/cps/transitivemissing.cps b/Tests/RunCMake/find_package/cps/transitivemissing.cps
new file mode 100644
index 0000000..2a294aa
--- /dev/null
+++ b/Tests/RunCMake/find_package/cps/transitivemissing.cps
@@ -0,0 +1,11 @@
+{
+  "cps_version": "0.13",
+  "name": "TransitiveMissing",
+  "cps_path": "@prefix@/cps",
+  "requires": {
+    "ComponentTest": {
+      "components": [ "DoesNotExist" ]
+    }
+  },
+  "components": {}
+}
diff --git a/Utilities/Sphinx/static/cmake.css b/Utilities/Sphinx/static/cmake.css
index 909b58a..72b8060 100644
--- a/Utilities/Sphinx/static/cmake.css
+++ b/Utilities/Sphinx/static/cmake.css
@@ -1,6 +1,28 @@
 /* Import the Sphinx theme style.  */
 @import url("default.css");
 
+/* Navbar logo */
+li.rootlink > img {
+  vertical-align: middle;
+  margin-top: -5px;
+}
+
+/* Push the footer to the bottom of the page. */
+body {
+  display: flex;
+  flex-direction: column;
+  min-height: 100svh;
+}
+
+div.document {
+  flex-grow: 1;
+}
+
+div.bodywrapper,
+div.body {
+  height: 100%;
+}
+
 /* Wrap sidebar content even within words so that long
    document names do not escape sidebar borders.  */
 div.sphinxsidebarwrapper {
@@ -17,6 +39,12 @@
   background-color: #dfdfdf;
 }
 
+/* Un-justify some elements. */
+div.body table.docutils p,
+div.body nav.contents p {
+  text-align: left;
+}
+
 /* Apply <pre> style (from classic.css) to signature directive argument. */
 .signature .sig {
   padding: 5px;
@@ -73,7 +101,6 @@
   padding-right: 2px;
   padding-top: 5px;
   text-align: center;
-  width: 100%;
 }
 
 /* Revert style to the inherited (normal text) for `:guide:` links */
@@ -91,3 +118,212 @@
 table.docutils td {
   border-top: 1px solid #aaa;
 }
+
+/* Hide small screen specific items */
+#sidebar-toggle,
+#sidebar-check,
+#sidebar-overlay {
+  display: none;
+}
+
+/* Small screen overrides */
+@media screen and (max-width: 760px) {
+
+  /* Sticky header */
+  div.relbar1 {
+    position: sticky;
+    top: 0;
+    z-index: 10;
+  }
+
+  html {
+    scroll-padding-top: 56px;
+  }
+
+  /* Header and footer */
+  div.related {
+    line-height: 28px;
+  }
+
+  div.related ul {
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  div.related li.right {
+    display: none;
+  }
+
+  div.related li.rootlink {
+    display: block;
+    margin-bottom: -1px;
+  }
+
+  div.related span.reldelim1 {
+    display: none;
+  }
+
+  /* Popup sidebar */
+  div.sphinxsidebar {
+    background-color: #e4ece8;
+    margin-left: 0;
+    position: fixed;
+    top: 0;
+    right: 0;
+    width: 260px;
+    height: 100%;
+    overflow-y: scroll;
+    z-index: 30;
+    visibility: hidden;
+    transform: translateX(100%);
+    transition: transform 0.2s ease-out, visibility 0.2s;
+  }
+
+  #sidebar-check:checked ~ div.document div.sphinxsidebar {
+    visibility: visible;
+    transform: none;
+  }
+
+  div.sphinxsidebarwrapper {
+    padding-bottom: 10px;
+    display: flex;
+    flex-direction: column;
+  }
+
+  /* Put the search box first in the sidebar. */
+  #searchbox {
+    order: -1;
+    margin-bottom: 10px;
+  }
+
+  /* Popup sidebar overlay */
+  #sidebar-overlay {
+    display: block;
+    position: fixed;
+    top: 0;
+    width: 100%;
+    height: 200%;
+    background-color: #000;
+    z-index: 20;
+    visibility: hidden;
+    opacity: 0;
+    transition: opacity 0.2s ease-out, visibility 0.2s;
+  }
+
+  #sidebar-check:checked ~ #sidebar-overlay {
+    visibility: visible;
+    opacity: 0.5;
+  }
+
+  /* Popup sidebar button */
+  label.sidebar-toggle {
+    display: block;
+    float: right;
+    position: relative;
+    cursor: pointer;
+    width: 48px;
+    height: 56px;
+    background: no-repeat center/24px url("data:image/svg+xml,\
+      %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath\
+      d='M3,6H21 M3,12H21 M3,18H21' fill='none' stroke='white' stroke-width='2'\
+      stroke-linecap='square' /%3E%3C/svg%3E");
+  }
+
+  /* Force-wrap long words */
+  div.body span.pre {
+    white-space: pre-wrap;
+  }
+
+  /* Disable whole page horizontal scroll, enable selectively. */
+  div.body {
+    min-width: auto;
+    overflow-x: hidden;
+    overflow-wrap: break-word;
+  }
+
+  div.body h1 {
+    white-space: nowrap;
+    overflow-x: auto;
+  }
+
+  table.docutils {
+    display: block;
+    overflow-x: auto;
+  }
+
+  /* Adjust various margins */
+  div.bodywrapper {
+    margin: 0;
+  }
+
+  div.body {
+    padding: 0 12px 12px;
+  }
+
+  div.body h1,
+  div.body h2,
+  div.body h3,
+  div.body h4,
+  div.body h5,
+  div.body h6 {
+    margin: 20px -12px 10px;
+    padding: 3px 0px 3px 12px;
+  }
+
+  div.body h1 {
+    margin-top: 0;
+  }
+
+  div.body ul {
+    padding-left: 15px;
+  }
+
+  div.body dd {
+    margin-left: 10px;
+  }
+
+  div.sphinxsidebar h4 {
+    margin: 0;
+  }
+
+  div.sphinxsidebar ul {
+    margin: 5px 0 10px 10px;
+  }
+
+  div.sphinxsidebar ul ul {
+    margin: 0 0 0 10px;
+  }
+
+  div.sphinxsidebar li {
+    margin-top: 5px;
+  }
+
+  div.deprecated > *,
+  div.versionadded > *,
+  div.versionchanged > * {
+    padding-left: 10px;
+  }
+
+  div.deprecated > :first-child,
+  div.versionadded > :first-child,
+  div.versionchanged > :first-child {
+    text-indent: -10px;
+  }
+
+  a.headerlink {
+    display: none;
+  }
+
+  /* Make index single-column. */
+  table.indextable,
+  table.indextable tbody,
+  table.indextable tr,
+  table.indextable td {
+    display: block;
+  }
+
+  table.indextable td {
+    width: auto !important;
+  }
+}
diff --git a/Utilities/Sphinx/templates/layout.html b/Utilities/Sphinx/templates/layout.html
index 8fb7c42..b536b06 100644
--- a/Utilities/Sphinx/templates/layout.html
+++ b/Utilities/Sphinx/templates/layout.html
@@ -1,24 +1,24 @@
 {% extends "!layout.html" %}
 {% block rootrellink %}
   <li>
-    <img src="{{ pathto('_static/cmake-logo-16.png', 1) }}" alt=""
-         style="vertical-align: middle; margin-top: -2px" />
+    <label class="sidebar-toggle" for="sidebar-check"></label>
+  </li>
+  <li class="rootlink">
+    <img src="{{ pathto('_static/cmake-logo-16.png', 1) }}" width="16" height="16" alt=""/>
+  {%- if versionswitch is defined %}
+    <a href="https://cmake.org/">CMake</a> <span class="version_switch">{{ release }}</span>
+  {%- else %}
+    <a href="https://cmake.org/">CMake {{ release }}</a>
+  {%- endif %}
+    <span class="reldelim1">{{ reldelim1 }}</span>
   </li>
   <li>
-    <a href="https://cmake.org/">CMake</a>{{ reldelim1 }}
-  </li>
-  <li>
-    {%- if versionswitch is defined %}
-    <span class="version_switch">{{ release }}</span>
     <a href="{{ pathto(master_doc) }}">{% trans %}Documentation{% endtrans %}</a>{{ reldelim1 }}
-    {%- else %}
-    <a href="{{ pathto(master_doc) }}">{{ shorttitle|e }}</a>{{ reldelim1 }}
-    {%- endif %}
   </li>
 {% endblock %}
 
 {%- block relbar1 %}
-{{ super() }}
+{{ super()|replace('class="related"', 'class="related relbar1"') }}
 {%- if outdated is defined %}
     <div class="outdated">
       This documents an old version of CMake.
@@ -48,6 +48,14 @@
   <title>{{ title|striptags|e }} {{ "&mdash;"|safe }} {{ docstitle|e }}</title>
 {% endblock %}
 
+{%- block header %}
+{%- if render_sidebar %}
+    <input id="sidebar-check" type="checkbox" />
+    <label id="sidebar-overlay" for="sidebar-check"></label>
+{%- endif %}
+{{ super() }}
+{% endblock %}
+
 {%- block footer %}
 {{ super() }}
 {%- if googleanalytics is defined %}
@@ -62,4 +70,15 @@
 } catch(err) {}
 </script>
 {%- endif %}
+{%- if render_sidebar %}
+<script type="text/javascript">
+(function() {
+  "use strict";
+  const hide = () => document.getElementById("sidebar-check").checked = false;
+  addEventListener("keydown", e => (e.key === "Escape") && hide());
+  addEventListener("click", e => (e.target.tagName === "A") && hide());
+  addEventListener("hashchange", hide)
+})();
+</script>
+{%- endif %}
 {%- endblock %}
diff --git a/Utilities/Sphinx/templates/localtoc.html b/Utilities/Sphinx/templates/localtoc.html
new file mode 100644
index 0000000..0065fe7
--- /dev/null
+++ b/Utilities/Sphinx/templates/localtoc.html
@@ -0,0 +1,8 @@
+{# Sphinx sidebar template: local table of contents.
+   Overridden to remove the "#" link from the "Table of Contents". #}
+{%- if display_toc %}
+  <div>
+    <h3>{{ _('Table of Contents') }}</h3>
+    {{ toc }}
+  </div>
+{%- endif %}
diff --git a/Utilities/std/cmext/enum_set b/Utilities/std/cmext/enum_set
index f851d29..f1d615a 100644
--- a/Utilities/std/cmext/enum_set
+++ b/Utilities/std/cmext/enum_set
@@ -31,12 +31,6 @@
 // enum class Example : unsigned { A, B, C, D };
 // using ExampleSet = enum_set<Example, 4>;
 //
-// Another possibility is to add, at the end of the list, the definition
-// 'cm_count' with the value of the precedent definition:
-//
-//   enum class Example : unsigned { A, B, C, D, cm_count = D };
-//   using ExampleSet = enum_set<Example>;
-//
 // To facilitate the usage of the enum_set, operators '+' and '|' can be used
 // as alternate to the 'initializer_list':
 //
@@ -47,21 +41,6 @@
 
 namespace cm {
 
-namespace internals {
-template <typename Enum, typename U = void>
-struct enum_size
-{
-  static constexpr auto value =
-    std::numeric_limits<typename std::underlying_type<Enum>::type>::digits;
-};
-template <typename Enum>
-struct enum_size<Enum, cm::void_t<decltype(Enum::cm_count)>>
-{
-  static constexpr auto value =
-    static_cast<typename std::underlying_type<Enum>::type>(Enum::cm_count) + 1;
-};
-}
-
 template <typename EnumSet>
 class enum_set_iterator
 {
@@ -144,7 +123,9 @@
 };
 
 template <
-  typename Enum, std::size_t Size = internals::enum_size<Enum>::value,
+  typename Enum,
+  std::size_t Size =
+    std::numeric_limits<typename std::underlying_type<Enum>::type>::digits,
   typename cm::enable_if_t<
     cm::is_scoped_enum<Enum>::value &&
       std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
@@ -152,6 +133,8 @@
 class enum_set
 {
 public:
+  static constexpr std::size_t set_size = Size;
+
   using key_type = Enum;
   using value_type = Enum;
   using size_type = typename std::underlying_type<Enum>::type;
@@ -169,6 +152,14 @@
   constexpr enum_set() noexcept = default;
   enum_set(key_type e) { this->insert(e); }
   enum_set(enum_set const& other) noexcept { this->insert(other); }
+  template <typename E,
+            typename cm::enable_if_t<std::is_same<Enum, E>::value, int> = 0>
+  enum_set(enum_set<E> const& other) noexcept
+  {
+    static_assert(Size < enum_set<E>::set_size, "Incompatible sizes");
+
+    this->insert(other.cbegin(), other.cend());
+  }
   enum_set(std::initializer_list<value_type> list) { this->insert(list); }
 
   enum_set& operator=(key_type e)
@@ -446,12 +437,12 @@
   }
 
 private:
-  template <typename E>
-  friend inline bool operator==(enum_set<E> const& lhs,
-                                enum_set<E> const& rhs) noexcept;
+  template <typename E, std::size_t S>
+  friend inline bool operator==(enum_set<E, S> const& lhs,
+                                enum_set<E, S> const& rhs) noexcept;
 
-  template <typename E, typename Predicate>
-  friend inline void erase_if(enum_set<E>& set, Predicate pred);
+  template <typename E, std::size_t S, typename Predicate>
+  friend inline void erase_if(enum_set<E, S>& set, Predicate pred);
 
   friend class enum_set_iterator<enum_set>;
   friend class enum_set_iterator<enum_set const>;
@@ -462,99 +453,104 @@
 };
 
 // non-member functions for enum_set
-template <typename Enum>
-inline enum_set<Enum> operator+(enum_set<Enum> const& lhs, Enum rhs)
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs,
+                                      Enum rhs)
 {
-  return enum_set<Enum>{ lhs } += rhs;
+  return enum_set<Enum, Size>{ lhs } += rhs;
 }
-template <typename Enum>
-inline enum_set<Enum> operator+(enum_set<Enum> const& lhs,
-                                enum_set<Enum> const& rhs) noexcept
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs,
+                                      enum_set<Enum, Size> const& rhs) noexcept
 {
-  return enum_set<Enum>{ lhs } += rhs;
+  return enum_set<Enum, Size>{ lhs } += rhs;
 }
-template <typename Enum>
-inline enum_set<Enum> operator+(enum_set<Enum> const& lhs,
-                                std::initializer_list<Enum> const rhs)
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator+(enum_set<Enum, Size> const& lhs,
+                                      std::initializer_list<Enum> const rhs)
 {
-  return enum_set<Enum>{ lhs } += rhs;
+  return enum_set<Enum, Size>{ lhs } += rhs;
 }
 
-template <typename Enum>
-inline cm::enum_set<Enum> operator|(cm::enum_set<Enum> const& lhs, Enum rhs)
+template <typename Enum, std::size_t Size>
+inline cm::enum_set<Enum, Size> operator|(cm::enum_set<Enum, Size> const& lhs,
+                                          Enum rhs)
 {
-  return enum_set<Enum>{ lhs } |= rhs;
+  return enum_set<Enum, Size>{ lhs } |= rhs;
 }
-template <typename Enum>
-inline cm::enum_set<Enum> operator|(Enum lhs, cm::enum_set<Enum> const& rhs)
+template <typename Enum, std::size_t Size>
+inline cm::enum_set<Enum, Size> operator|(Enum lhs,
+                                          cm::enum_set<Enum, Size> const& rhs)
 {
-  return enum_set<Enum>{ lhs } |= rhs;
+  return enum_set<Enum, Size>{ lhs } |= rhs;
 }
-template <typename Enum>
-inline cm::enum_set<Enum> operator|(cm::enum_set<Enum> const& lhs,
-                                    cm::enum_set<Enum> const& rhs)
+template <typename Enum, std::size_t Size>
+inline cm::enum_set<Enum, Size> operator|(cm::enum_set<Enum, Size> const& lhs,
+                                          cm::enum_set<Enum, Size> const& rhs)
 {
-  return enum_set<Enum>{ lhs } |= rhs;
+  return enum_set<Enum, Size>{ lhs } |= rhs;
 }
 
-template <typename Enum>
-inline enum_set<Enum> operator-(enum_set<Enum> const& lhs, Enum rhs)
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs,
+                                      Enum rhs)
 {
-  return enum_set<Enum>{ lhs } -= rhs;
+  return enum_set<Enum, Size>{ lhs } -= rhs;
 }
-template <typename Enum>
-inline enum_set<Enum> operator-(enum_set<Enum> const& lhs,
-                                enum_set<Enum> const& rhs) noexcept
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs,
+                                      enum_set<Enum, Size> const& rhs) noexcept
 {
-  return enum_set<Enum>{ lhs } -= rhs;
+  return enum_set<Enum, Size>{ lhs } -= rhs;
 }
-template <typename Enum>
-inline enum_set<Enum> operator-(enum_set<Enum> const& lhs,
-                                std::initializer_list<Enum> const rhs)
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator-(enum_set<Enum, Size> const& lhs,
+                                      std::initializer_list<Enum> const rhs)
 {
-  return enum_set<Enum>{ lhs } -= rhs;
+  return enum_set<Enum, Size>{ lhs } -= rhs;
 }
 
-template <typename Enum>
-inline enum_set<Enum> operator^(enum_set<Enum> const& lhs, Enum rhs)
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs,
+                                      Enum rhs)
 {
-  return enum_set<Enum>{ lhs } ^= rhs;
+  return enum_set<Enum, Size>{ lhs } ^= rhs;
 }
-template <typename Enum>
-inline enum_set<Enum> operator^(enum_set<Enum> const& lhs,
-                                enum_set<Enum> const& rhs) noexcept
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs,
+                                      enum_set<Enum, Size> const& rhs) noexcept
 {
-  return enum_set<Enum>{ lhs } ^= rhs;
+  return enum_set<Enum, Size>{ lhs } ^= rhs;
 }
-template <typename Enum>
-inline enum_set<Enum> operator^(enum_set<Enum> const& lhs,
-                                std::initializer_list<Enum> const rhs)
+template <typename Enum, std::size_t Size>
+inline enum_set<Enum, Size> operator^(enum_set<Enum, Size> const& lhs,
+                                      std::initializer_list<Enum> const rhs)
 {
-  return enum_set<Enum>{ lhs } ^= rhs;
+  return enum_set<Enum, Size>{ lhs } ^= rhs;
 }
 
-template <typename Enum>
-inline bool operator==(enum_set<Enum> const& lhs,
-                       enum_set<Enum> const& rhs) noexcept
+template <typename Enum, std::size_t Size>
+inline bool operator==(enum_set<Enum, Size> const& lhs,
+                       enum_set<Enum, Size> const& rhs) noexcept
 {
   return lhs.Set == rhs.Set;
 }
 
-template <typename Enum>
-inline bool operator!=(enum_set<Enum> const& lhs,
-                       enum_set<Enum> const& rhs) noexcept
+template <typename Enum, std::size_t Size>
+inline bool operator!=(enum_set<Enum, Size> const& lhs,
+                       enum_set<Enum, Size> const& rhs) noexcept
 {
   return !(lhs == rhs);
 }
 
-template <typename Enum>
-inline void erase(enum_set<Enum>& set, Enum value)
+template <typename Enum, std::size_t Size>
+inline void erase(enum_set<Enum, Size>& set, Enum value)
 {
   set.erase(value);
 }
 
-template <typename Enum, typename Predicate>
-inline void erase_if(enum_set<Enum>& set, Predicate pred)
+template <typename Enum, std::size_t Size, typename Predicate>
+inline void erase_if(enum_set<Enum, Size>& set, Predicate pred)
 {
   for (std::size_t index = 0; index < set.Set.size(); ++index) {
     if (set.Set.test(index) && pred(static_cast<Enum>(index))) {
@@ -564,6 +560,12 @@
 }
 } // namespace cm
 
+//
+// WARNING: the following two functions rely on an enum_set without
+// explicit size.
+//
+// TODO: ensure compatibility with any enum_set definitions.
+//
 template <typename Enum,
           typename cm::enable_if_t<cm::is_scoped_enum<Enum>::value, int> = 0>
 inline cm::enum_set<Enum> operator+(Enum lhs, Enum rhs)