Merge branch 'release-3.27' into release-3.28
diff --git a/.gitignore b/.gitignore
index 3b9e52a..816dced 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,10 @@
 # Common build directories
 /build*/
 
+# CI jobs that run in symlinked trees produce these artifacts.
+/real_work/
+/work
+
 # MacOS Finder files.
 .DS_Store
 
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 297afda..3c1c9cc 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -18,6 +18,10 @@
     - test-ext
     - upload
 
+variables:
+    # Some jobs that place their artifacts in a different directory will override this.
+    CMAKE_CI_BUILD_DIR: build
+
 ################################################################################
 # Job declarations
 #
@@ -96,9 +100,9 @@
         - .linux_x86_64_tags
         - .run_automatically
 
-l:iwyu-debian10:
+l:iwyu-debian12:
     extends:
-        - .debian10_iwyu
+        - .debian12_iwyu
         - .cmake_build_linux
         - .linux_x86_64_tags
         - .run_automatically
@@ -132,7 +136,7 @@
 
 # Linux builds
 
-b:centos6-x86_64:
+b:centos7-x86_64:
     extends:
         - .linux_release_x86_64
         - .cmake_build_linux_release
@@ -140,7 +144,7 @@
         - .linux_x86_64_tags
         - .run_manually
     variables:
-        CMAKE_CI_ARTIFACTS_NAME: "artifacts-centos6-x86_64"
+        CMAKE_CI_ARTIFACTS_NAME: "artifacts-centos7-x86_64"
 
 b:centos7-aarch64:
     extends:
@@ -153,20 +157,20 @@
         CMAKE_CI_ARTIFACTS_NAME: "artifacts-centos7-aarch64"
         CMAKE_CI_NO_MR: "true"
 
-t:debian10-ninja:
+t:debian12-ninja:
     extends:
-        - .debian10_ninja
+        - .debian12_ninja
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .cmake_junit_artifacts
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
 
-t:debian10-aarch64-ninja:
+t:debian12-aarch64-ninja:
     extends:
-        - .debian10_aarch64_ninja
+        - .debian12_aarch64_ninja
         - .cmake_test_linux_release
         - .linux_aarch64_tags
         - .cmake_junit_artifacts
@@ -175,33 +179,65 @@
     variables:
         CMAKE_CI_NO_MR: "true"
 
-t:debian10-ninja-clang:
+t:debian12-ninja-clang:
     extends:
-        - .debian10_ninja_clang
+        - .debian12_ninja_clang
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:debian10-makefiles-clang:
+t:debian12-makefiles-clang:
     extends:
-        - .debian10_makefiles_clang
+        - .debian12_makefiles_clang
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
+t:debian12-ninja-multi-symlinked:
+    extends:
+        - .debian12_ninja_multi_symlinked
+        - .cmake_test_linux_release
+        - .linux_x86_64_tags
+        - .cmake_junit_artifacts
+        - .run_dependent
+        - .needs_centos7_x86_64
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+        CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
+
+t:debian12-hip-radeon:
+    extends:
+        - .debian12_hip_radeon
+        - .cmake_test_linux_release
+        - .linux_x86_64_tags_radeon
+        - .run_dependent
+        - .needs_centos7_x86_64
+    variables:
+        CMAKE_CI_NO_MR: "true"
+
+t:fedora38-hip-radeon:
+    extends:
+        - .fedora38_hip_radeon
+        - .cmake_test_linux_release
+        - .linux_x86_64_tags_radeon
+        - .run_dependent
+        - .needs_centos7_x86_64
+    variables:
+        CMAKE_CI_NO_MR: "true"
+
 t:fedora38-ninja-clang:
     extends:
         - .fedora38_ninja_clang
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -211,7 +247,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -221,7 +257,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -231,7 +267,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
 
 t:fedora38-makefiles-nospace:
     extends:
@@ -240,7 +276,7 @@
         - .linux_x86_64_tags
         - .cmake_junit_artifacts
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake-ci"
         CMAKE_CI_BUILD_NAME: fedora38_makefiles_nospace
@@ -252,7 +288,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_v3_tags_cuda
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -262,7 +298,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags_cuda
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_NO_MR: "true"
 
@@ -273,7 +309,7 @@
         - .linux_x86_64_tags_cuda
         - .cmake_junit_artifacts
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
 
 t:cuda10.2-clang:
     extends:
@@ -281,7 +317,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags_cuda
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_NO_MR: "true"
 
@@ -292,7 +328,7 @@
         - .linux_x86_64_tags_cuda
         - .cmake_junit_artifacts
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
 
 t:cuda11.6-clang:
     extends:
@@ -300,7 +336,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags_cuda
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_NO_MR: "true"
 
@@ -310,7 +346,27 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags_cuda
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
+    variables:
+        CMAKE_CI_NO_MR: "true"
+
+t:cuda11.8-minimal-splayed-ninja:
+    extends:
+        - .cuda11.8_splayed_nvidia
+        - .cmake_test_linux_release
+        - .linux_x86_64_tags_cuda
+        - .run_dependent
+        - .needs_centos7_x86_64
+    variables:
+        CMAKE_CI_NO_MR: "true"
+
+t:hip5.5-nvidia:
+    extends:
+        - .hip5.5_nvidia
+        - .cmake_test_linux_release
+        - .linux_x86_64_tags_cuda
+        - .run_dependent
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_NO_MR: "true"
 
@@ -320,7 +376,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags_radeon
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_NO_MR: "true"
 
@@ -330,7 +386,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -340,7 +396,18 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_dependent
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
+t:debian10-legacy:
+    extends:
+        - .debian10_legacy
+        - .cmake_test_linux_release
+        - .linux_x86_64_tags
+        - .cmake_junit_artifacts
+        - .run_dependent
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -354,27 +421,37 @@
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
 
-b:debian10-makefiles-inplace:
+b:fedora38-makefiles-symlinked:
     extends:
-        - .debian10_makefiles_inplace
+        - .fedora38_makefiles_symlinked
+        - .cmake_build_linux
+        - .cmake_build_artifacts
+        - .linux_x86_64_tags
+        - .run_manually
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
+b:debian12-makefiles-inplace:
+    extends:
+        - .debian12_makefiles_inplace
         - .cmake_build_linux_standalone
         - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-b:debian10-extdeps:
+b:debian12-extdeps:
     extends:
-        - .debian10_extdeps
+        - .debian12_extdeps
         - .cmake_build_linux_standalone
         - .linux_x86_64_tags
         - .run_manually
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-b:debian10-aarch64-extdeps:
+b:debian12-aarch64-extdeps:
     extends:
-        - .debian10_aarch64_extdeps
+        - .debian12_aarch64_extdeps
         - .cmake_build_linux_standalone
         - .linux_aarch64_tags
         - .run_manually
@@ -404,6 +481,20 @@
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
 
+t:fedora38-makefiles-symlinked:
+    extends:
+        - .fedora38_makefiles_symlinked
+        - .cmake_test_linux
+        - .linux_x86_64_tags_x11
+        - .cmake_test_artifacts
+        - .run_dependent
+    dependencies:
+        - b:fedora38-makefiles-symlinked
+    needs:
+        - b:fedora38-makefiles-symlinked
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 t:fedora38-ninja-multi:
     extends:
         - .fedora38_ninja_multi
@@ -826,6 +917,25 @@
     variables:
         CMAKE_CI_JOB_CONTINUOUS: "true"
 
+b:macos-arm64-ninja-symlinked:
+    extends:
+        - .macos_arm64_ninja_symlinked
+        - .cmake_build_macos
+        - .cmake_build_artifacts
+        - .macos_arm64_tags
+        - .run_manually
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
+b:macos-arm64-pch:
+    extends:
+        - .macos_arm64_pch
+        - .cmake_build_macos
+        - .macos_arm64_tags
+        - .run_manually
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 t:macos-x86_64-ninja:
     extends:
         - .macos_x86_64_ninja
@@ -855,6 +965,21 @@
         CMAKE_CI_JOB_CONTINUOUS: "true"
         CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
 
+t:macos-arm64-ninja-symlinked:
+    extends:
+        - .macos_arm64_ninja_symlinked
+        - .cmake_test_macos
+        - .cmake_test_artifacts
+        - .macos_arm64_tags
+        - .run_dependent
+    dependencies:
+        - b:macos-arm64-ninja-symlinked
+    needs:
+        - b:macos-arm64-ninja-symlinked
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+        CMAKE_CI_JOB_NIGHTLY_NINJA: "true"
+
 b:macos-x86_64-makefiles:
     extends:
         - .macos_x86_64_makefiles
@@ -914,6 +1039,20 @@
     needs:
         - t:macos-arm64-ninja
 
+t:macos-arm64-xcode-symlinked:
+    extends:
+        - .macos_arm64_xcode_symlinked
+        - .cmake_test_macos_external
+        - .macos_arm64_tags_ext
+        - .cmake_junit_artifacts
+        - .run_dependent
+    dependencies:
+        - t:macos-arm64-ninja
+    needs:
+        - t:macos-arm64-ninja
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 t:macos-x86_64-ninja-ub:
     extends:
         - .macos_x86_64_ninja_ub
@@ -998,6 +1137,15 @@
         - .windows_x86_64_tags_nonconcurrent_vs2022
         - .run_manually
 
+b:windows-vs2022-x64-pch:
+    extends:
+        - .windows_vs2022_x64_pch
+        - .cmake_build_windows
+        - .windows_x86_64_tags_nonconcurrent_vs2022
+        - .run_manually
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 t:windows-vs2022-x64-ninja:
     extends:
         - .windows_vs2022_x64_ninja
@@ -1108,7 +1256,7 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:windows-clang16.0-cl-ninja:
+t:windows-clang17.0-cl-ninja:
     extends:
         - .windows_clang_ninja
         - .cmake_test_windows_external
@@ -1120,10 +1268,10 @@
     needs:
         - t:windows-vs2022-x64-ninja
     variables:
-        CMAKE_CI_BUILD_NAME: windows_clang16.0_cl_ninja
+        CMAKE_CI_BUILD_NAME: windows_clang17.0_cl_ninja
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:windows-clang16.0-cl-nmake:
+t:windows-clang17.0-cl-nmake:
     extends:
         - .windows_clang_nmake
         - .cmake_test_windows_external
@@ -1135,10 +1283,10 @@
     needs:
         - t:windows-vs2022-x64-ninja
     variables:
-        CMAKE_CI_BUILD_NAME: windows_clang16.0_cl_nmake
+        CMAKE_CI_BUILD_NAME: windows_clang17.0_cl_nmake
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:windows-clang16.0-gnu-ninja:
+t:windows-clang17.0-gnu-ninja:
     extends:
         - .windows_clang_ninja
         - .cmake_test_windows_external
@@ -1150,10 +1298,10 @@
     needs:
         - t:windows-vs2022-x64-ninja
     variables:
-        CMAKE_CI_BUILD_NAME: windows_clang16.0_gnu_ninja
+        CMAKE_CI_BUILD_NAME: windows_clang17.0_gnu_ninja
         CMAKE_CI_JOB_NIGHTLY: "true"
 
-t:windows-clang16.0-gnu-nmake:
+t:windows-clang17.0-gnu-nmake:
     extends:
         - .windows_clang_nmake
         - .cmake_test_windows_external
@@ -1165,7 +1313,7 @@
     needs:
         - t:windows-vs2022-x64-ninja
     variables:
-        CMAKE_CI_BUILD_NAME: windows_clang16.0_gnu_nmake
+        CMAKE_CI_BUILD_NAME: windows_clang17.0_gnu_nmake
         CMAKE_CI_JOB_NIGHTLY: "true"
 
 t:windows-intel2021.9.0-ninja:
@@ -1254,6 +1402,20 @@
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
+t:windows-orangec6.73.1:
+    extends:
+        - .windows_orangec6.73.1
+        - .cmake_test_windows_external
+        - .windows_x86_64_tags_concurrent
+        - .cmake_junit_artifacts
+        - .run_dependent
+    dependencies:
+        - t:windows-vs2022-x64-ninja
+    needs:
+        - t:windows-vs2022-x64-ninja
+    variables:
+        CMAKE_CI_JOB_NIGHTLY: "true"
+
 # Windows arm64 jobs
 
 b:windows-arm64-vs2022-ninja:
diff --git a/.gitlab/.gitignore b/.gitlab/.gitignore
index 852dfa6..19796ef 100644
--- a/.gitlab/.gitignore
+++ b/.gitlab/.gitignore
@@ -12,6 +12,7 @@
 /ninja*
 /openmp
 /open-watcom*
+/orangec
 /python*
 /qt*
 /sccache*
diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml
index 6c4cc0d..d9943b8 100644
--- a/.gitlab/artifacts.yml
+++ b/.gitlab/artifacts.yml
@@ -5,63 +5,63 @@
         expire_in: 1d
         paths:
             # Test specifications.
-            - build/**/CTestTestfile.cmake
+            - ${CMAKE_CI_BUILD_DIR}/**/CTestTestfile.cmake
 
             # Allow CMake to find CMAKE_ROOT.
-            - build/CMakeFiles/CMakeSourceDir.txt
+            - ${CMAKE_CI_BUILD_DIR}/CMakeFiles/CMakeSourceDir.txt
 
             # Take the install tree.
-            - build/install/
+            - ${CMAKE_CI_BUILD_DIR}/install/
 
             # We need the main binaries.
-            - build/bin/
+            - ${CMAKE_CI_BUILD_DIR}/bin/
             # The cache is needed for the installation test.
-            - build/CMakeCache.txt
+            - ${CMAKE_CI_BUILD_DIR}/CMakeCache.txt
             # Test binaries. Eventually these might be better under
             # `Source/Tests` or the like.
-            - build/Tests/EnforceConfig.cmake
-            - build/Tests/CMakeBuildTest.cmake
-            - build/Tests/CMakeBuildDoubleProjectTest.cmake
-            - build/Tests/CMake*/runcompilecommands
-            - build/Tests/CMake*/runcompilecommands.exe
-            - build/Tests/CMake*/test*
-            - build/Tests/CMake*/PseudoMemcheck/valgrind
-            - build/Tests/CMake*/PseudoMemcheck/purify
-            - build/Tests/CMake*/PseudoMemcheck/memcheck_fail
-            - build/Tests/CMake*/PseudoMemcheck/BC
-            - build/Tests/CMake*/PseudoMemcheck/cuda-memcheck
-            - build/Tests/CMake*/PseudoMemcheck/valgrind.exe
-            - build/Tests/CMake*/PseudoMemcheck/purify.exe
-            - build/Tests/CMake*/PseudoMemcheck/memcheck_fail.exe
-            - build/Tests/CMake*/PseudoMemcheck/BC.exe
-            - build/Tests/CMake*/PseudoMemcheck/cuda-memcheck.exe
-            - build/Tests/CMake*/PseudoMemcheck/NoLog
-            - build/Tests/CMake*Lib/*LibTests
-            - build/Tests/CMake*Lib/*LibTests.exe
-            - build/Source/kwsys/cmsysTest*
-            - build/Source/kwsys/testConsoleBufChild.exe
-            - build/Utilities/cmcurl/curltest
-            - build/Utilities/cmcurl/curltest.exe
-            - build/Utilities/KWIML/test/kwiml_test
-            - build/Utilities/KWIML/test/kwiml_test.exe
-            - build/Source/kwsys/*cmsysTestDynload.*
-            - build/Source/kwsys/dynloaddir/cmsysTestDynloadImpl.dll
-            - build/Source/kwsys/dynloaddir/cmsysTestDynloadUse.dll
+            - ${CMAKE_CI_BUILD_DIR}/Tests/EnforceConfig.cmake
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeBuildTest.cmake
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeBuildDoubleProjectTest.cmake
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/runcompilecommands
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/runcompilecommands.exe
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/test*
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/valgrind
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/purify
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/memcheck_fail
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/BC
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/cuda-memcheck
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/valgrind.exe
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/purify.exe
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/memcheck_fail.exe
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/BC.exe
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/cuda-memcheck.exe
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*/PseudoMemcheck/NoLog
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*Lib/*LibTests
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMake*Lib/*LibTests.exe
+            - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/cmsysTest*
+            - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/testConsoleBufChild.exe
+            - ${CMAKE_CI_BUILD_DIR}/Utilities/cmcurl/curltest
+            - ${CMAKE_CI_BUILD_DIR}/Utilities/cmcurl/curltest.exe
+            - ${CMAKE_CI_BUILD_DIR}/Utilities/KWIML/test/kwiml_test
+            - ${CMAKE_CI_BUILD_DIR}/Utilities/KWIML/test/kwiml_test.exe
+            - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/*cmsysTestDynload.*
+            - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/dynloaddir/cmsysTestDynloadImpl.dll
+            - ${CMAKE_CI_BUILD_DIR}/Source/kwsys/dynloaddir/cmsysTestDynloadUse.dll
 
             # Test directories.
-            - build/Tests/CTest*
-            - build/Tests/Find*
-            - build/Tests/Qt*
-            - build/Tests/RunCMake/
-            - build/Tests/CMakeOnly/
-            - build/Tests/CMakeTests/
-            - build/Tests/CMakeGUI/
-            - build/Tests/FortranC/
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CTest*
+            - ${CMAKE_CI_BUILD_DIR}/Tests/Find*
+            - ${CMAKE_CI_BUILD_DIR}/Tests/Qt*
+            - ${CMAKE_CI_BUILD_DIR}/Tests/RunCMake/
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeOnly/
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeTests/
+            - ${CMAKE_CI_BUILD_DIR}/Tests/CMakeGUI/
+            - ${CMAKE_CI_BUILD_DIR}/Tests/FortranC/
 
             # CTest/CDash information.
-            - build/Testing/
-            - build/DartConfiguation.tcl
-            - build/CTestCustom.cmake
+            - ${CMAKE_CI_BUILD_DIR}/Testing/
+            - ${CMAKE_CI_BUILD_DIR}/DartConfiguation.tcl
+            - ${CMAKE_CI_BUILD_DIR}/CTestCustom.cmake
 
 .cmake_release_artifacts:
     artifacts:
@@ -71,17 +71,17 @@
         when: always
         paths:
             # Any packages made.
-            - build/cmake-*-linux-x86_64.*
-            - build/cmake-*-linux-aarch64.*
-            - build/cmake-*-macos*-universal.*
-            - build/cmake-*-windows-x86_64.*
-            - build/cmake-*-windows-i386.*
-            - build/cmake-*-windows-arm64.*
+            - ${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-*-windows-x86_64.*
+            - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-i386.*
+            - ${CMAKE_CI_BUILD_DIR}/cmake-*-windows-arm64.*
             # Any source packages made.
-            - build/cmake-*.tar.gz
-            - build/cmake-*.zip
+            - ${CMAKE_CI_BUILD_DIR}/cmake-*.tar.gz
+            - ${CMAKE_CI_BUILD_DIR}/cmake-*.zip
             # Any unsigned packages made.
-            - build/unsigned/cmake-*
+            - ${CMAKE_CI_BUILD_DIR}/unsigned/cmake-*
 
 .cmake_junit_artifacts:
     artifacts:
@@ -89,7 +89,7 @@
         when: always
         reports:
             junit:
-                - build/junit.xml
+                - ${CMAKE_CI_BUILD_DIR}/junit.xml
 
 .cmake_sphinx_artifacts:
     artifacts:
@@ -97,8 +97,8 @@
         when: always
         paths:
             # Take the sphinx logs.
-            - build/build-*.log
-            - build/linkcheck/output.*
+            - ${CMAKE_CI_BUILD_DIR}/build-*.log
+            - ${CMAKE_CI_BUILD_DIR}/linkcheck/output.*
 
 .cmake_test_artifacts:
     artifacts:
@@ -107,22 +107,22 @@
         when: always
         reports:
             junit:
-                - build/junit.xml
+                - ${CMAKE_CI_BUILD_DIR}/junit.xml
         paths:
             # Take the install tree.
-            - build/install/
+            - ${CMAKE_CI_BUILD_DIR}/install/
 
 .cmake_doc_artifacts:
     artifacts:
         expire_in: 1d
         paths:
             # Take the install tree.
-            - build/install-doc/
+            - ${CMAKE_CI_BUILD_DIR}/install-doc/
 
 .cmake_org_help_artifacts:
     artifacts:
         expire_in: 1d
         paths:
-            - build/html
+            - ${CMAKE_CI_BUILD_DIR}/html
         exclude:
-            - build/html/.buildinfo
+            - ${CMAKE_CI_BUILD_DIR}/html/.buildinfo
diff --git a/.gitlab/ci/clang.ps1 b/.gitlab/ci/clang.ps1
index 1fc8d8e..e455ebc 100755
--- a/.gitlab/ci/clang.ps1
+++ b/.gitlab/ci/clang.ps1
@@ -1,10 +1,10 @@
 $erroractionpreference = "stop"
 
-if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang16.0")) {
-    # LLVM/Clang 16.0
-    # https://github.com/llvm/llvm-project/releases/tag/llvmorg-16.0.0
-    $filename = "llvm-16.0.0-win-x86_64-1"
-    $sha256sum = "13F48356BA5892A82E8BB25EB283FDDAA8F23A0F209B6BF6525D2C5E1285B950"
+if ("$env:CMAKE_CI_BUILD_NAME".Contains("clang17.0")) {
+    # LLVM/Clang 17.0.1
+    # https://github.com/llvm/llvm-project/releases/tag/llvmorg-17.0.1
+    $filename = "llvm-17.0.1-win-x86_64-1"
+    $sha256sum = "803F5D7291219BE60D2EE69CE8882341F94A8707A214DED190614895B6996F55"
 } else {
     throw ('unknown CMAKE_CI_BUILD_NAME: ' + "$env:CMAKE_CI_BUILD_NAME")
 }
diff --git a/.gitlab/ci/cmake.ps1 b/.gitlab/ci/cmake.ps1
index 3efb67a..f6b5cc7 100755
--- a/.gitlab/ci/cmake.ps1
+++ b/.gitlab/ci/cmake.ps1
@@ -1,12 +1,12 @@
 $erroractionpreference = "stop"
 
-$version = "3.24.1"
+$version = "3.27.6"
 
 if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") {
-    $sha256sum = "C1B17431A16337D517F7BA78C7067B6F143A12686CB8087F3DD32F3FA45F5AAE"
+    $sha256sum = "F013A0CCA091AA953F9A60A99292EC7A20AE3F9CEB05CB5C08EBE164097C526F"
     $platform = "windows-x86_64"
 } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") {
-    $sha256sum = "D94683F3B0E63F6EF194C621194F6E26F3735EDA70750395E0F2BBEE4023FB95"
+    $sha256sum = "980B7E532D77BFB4E5814C76403242C503019F1C0699440220CF2D527C8FF350"
     $platform = "windows-arm64"
 } else {
     throw ('unknown PROCESSOR_ARCHITECTURE: ' + "$env:PROCESSOR_ARCHITECTURE")
diff --git a/.gitlab/ci/cmake.sh b/.gitlab/ci/cmake.sh
index 137da06..21da466b 100755
--- a/.gitlab/ci/cmake.sh
+++ b/.gitlab/ci/cmake.sh
@@ -2,22 +2,22 @@
 
 set -e
 
-readonly version="3.24.1"
+readonly version="3.27.6"
 
 case "$(uname -s)-$(uname -m)" in
     Linux-x86_64)
         shatool="sha256sum"
-        sha256sum="827bf068cfaa23a9fb95f990c9f8a7ed8f2caeb3af62b5c0a2fed7a8dd6dde3e"
+        sha256sum="26373a283daa8490d772dc8a179450cd6d391cb2a9db8d4242fe09e361efc42e"
         platform="linux-x86_64"
         ;;
     Linux-aarch64)
         shatool="sha256sum"
-        sha256sum="d50c40135df667ed659f8e4eb7cf7d53421250304f7b3e1a70af9cf3d0f2ab18"
+        sha256sum="811e5040ad7f3fb4924a875373d2a1a174a01400233a81a638a989157438a5e3"
         platform="linux-aarch64"
         ;;
     Darwin-*)
         shatool="shasum -a 256"
-        sha256sum="71bb8db69826d74c395a3c3bbf8b773dbe9f54a2c7331266ba70da303e9c97a1"
+        sha256sum="a66b497289ab8c769b601d93833448eaae985beb762993837a51a79916d12f23"
         platform="macos-universal"
         ;;
     *)
diff --git a/.gitlab/ci/configure_common.cmake b/.gitlab/ci/configure_common.cmake
index c207a74..f03c4b8 100644
--- a/.gitlab/ci/configure_common.cmake
+++ b/.gitlab/ci/configure_common.cmake
@@ -13,6 +13,10 @@
 set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install" CACHE PATH "")
 set(CMake_TEST_INSTALL "OFF" CACHE BOOL "")
 
+set(CTEST_TEST_CTEST ON CACHE BOOL "")
+set(CMAKE_RUN_LONG_TESTS ON CACHE BOOL "")
+set(CMAKE_TESTS_CDASH_SERVER "NOTFOUND" CACHE STRING "")
+
 if (NOT "$ENV{CMAKE_CI_BUILD_TYPE}" STREQUAL "")
   set(CMAKE_BUILD_TYPE "$ENV{CMAKE_CI_BUILD_TYPE}" CACHE STRING "")
 endif ()
diff --git a/.gitlab/ci/configure_cuda11.8_splayed_nvidia.cmake b/.gitlab/ci/configure_cuda11.8_splayed_nvidia.cmake
new file mode 100644
index 0000000..519699b
--- /dev/null
+++ b/.gitlab/ci/configure_cuda11.8_splayed_nvidia.cmake
@@ -0,0 +1,3 @@
+set(CMake_TEST_CUDA "NVIDIA" CACHE STRING "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake b/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
deleted file mode 100644
index dff0db1..0000000
--- a/.gitlab/ci/configure_debian10_aarch64_ninja.cmake
+++ /dev/null
@@ -1,97 +0,0 @@
-set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "")
-set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
-set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
-set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
-set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
-set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
-set(CMake_TEST_FindCups "ON" CACHE BOOL "")
-set(CMake_TEST_FindCURL "ON" CACHE BOOL "")
-set(CMake_TEST_FindDevIL "ON" CACHE BOOL "")
-set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "")
-set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "")
-set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "")
-set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "")
-set(CMake_TEST_FindFreetype "ON" CACHE BOOL "")
-set(CMake_TEST_FindGDAL "ON" CACHE BOOL "")
-set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
-set(CMake_TEST_FindGit "ON" CACHE BOOL "")
-set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
-set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
-set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
-set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
-set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
-set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "") # h5c++.mpich does not exist
-# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "") # h5c++.openmpi does not exist
-# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
-set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
-set(CMake_TEST_FindICU "ON" CACHE BOOL "")
-set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
-set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
-set(CMake_TEST_FindJNI "ON" CACHE BOOL "")
-set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
-set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
-set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
-set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibUV "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibXml2 "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI "ON" CACHE BOOL "")
-set(CMake_TEST_FindODBC "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP_Fortran "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenSP "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenSSL "ON" CACHE BOOL "")
-set(CMake_TEST_FindPatch "ON" CACHE BOOL "")
-set(CMake_TEST_FindPNG "ON" CACHE BOOL "")
-set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "")
-set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "")
-set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_NumPy "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_PyPy "ON" CACHE BOOL "")
-set(CMake_TEST_FindRuby "ON" CACHE BOOL "")
-set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
-set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
-set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
-set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
-set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
-set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
-set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
-set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "")
-set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
-set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
-set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "")
-set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
-set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
-set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
-set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
-
-include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian10_iwyu.cmake b/.gitlab/ci/configure_debian10_iwyu.cmake
deleted file mode 100644
index abe750d..0000000
--- a/.gitlab/ci/configure_debian10_iwyu.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-set(CMake_RUN_IWYU ON CACHE BOOL "")
-# Uncomment to diagnose IWYU problems as needed.
-#set(CMake_IWYU_VERBOSE ON CACHE BOOL "")
-set(IWYU_COMMAND "/usr/bin/include-what-you-use-6.0" CACHE FILEPATH "")
-
-include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_debian10_legacy.cmake b/.gitlab/ci/configure_debian10_legacy.cmake
new file mode 100644
index 0000000..11a5dc0
--- /dev/null
+++ b/.gitlab/ci/configure_debian10_legacy.cmake
@@ -0,0 +1,6 @@
+set(CMake_TEST_FindPython2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython2_IronPython "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython2_NumPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython2_PyPy "ON" CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian10_ninja.cmake b/.gitlab/ci/configure_debian10_ninja.cmake
deleted file mode 100644
index 211a2a7..0000000
--- a/.gitlab/ci/configure_debian10_ninja.cmake
+++ /dev/null
@@ -1,107 +0,0 @@
-set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "")
-set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "")
-if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
-  set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "")
-endif()
-
-set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
-set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
-set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
-set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
-set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
-set(CMake_TEST_FindCups "ON" CACHE BOOL "")
-set(CMake_TEST_FindCURL "ON" CACHE BOOL "")
-set(CMake_TEST_FindDevIL "ON" CACHE BOOL "")
-set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "")
-set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "")
-set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "")
-set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "")
-set(CMake_TEST_FindFreetype "ON" CACHE BOOL "")
-set(CMake_TEST_FindGDAL "ON" CACHE BOOL "")
-set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
-set(CMake_TEST_FindGit "ON" CACHE BOOL "")
-set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
-set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
-set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
-set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
-set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
-set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "") # h5c++.mpich does not exist
-# set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "") # h5c++.openmpi does not exist
-# set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
-set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
-set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
-set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
-set(CMake_TEST_FindICU "ON" CACHE BOOL "")
-set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
-set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
-set(CMake_TEST_FindJNI "ON" CACHE BOOL "")
-set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
-set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
-set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
-set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibUV "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibXml2 "ON" CACHE BOOL "")
-set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "")
-set(CMake_TEST_FindMPI "ON" CACHE BOOL "")
-set(CMake_TEST_FindODBC "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP_Fortran "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenSP "ON" CACHE BOOL "")
-set(CMake_TEST_FindOpenSSL "ON" CACHE BOOL "")
-set(CMake_TEST_FindPatch "ON" CACHE BOOL "")
-set(CMake_TEST_FindPNG "ON" CACHE BOOL "")
-set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "")
-set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "")
-set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_IronPython "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_NumPy "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_PyPy "ON" CACHE BOOL "")
-set(CMake_TEST_FindRuby "ON" CACHE BOOL "")
-set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "")
-set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
-set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
-set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
-set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
-set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
-set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
-set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
-set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "")
-set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
-set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
-set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "")
-set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
-set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
-set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
-set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
-
-if (NOT "$ENV{SWIFTC}" STREQUAL "")
-  set(CMAKE_Swift_COMPILER "$ENV{SWIFTC}" CACHE FILEPATH "")
-endif()
-
-include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian10_aarch64_extdeps.cmake b/.gitlab/ci/configure_debian12_aarch64_extdeps.cmake
similarity index 100%
rename from .gitlab/ci/configure_debian10_aarch64_extdeps.cmake
rename to .gitlab/ci/configure_debian12_aarch64_extdeps.cmake
diff --git a/.gitlab/ci/configure_debian12_aarch64_ninja.cmake b/.gitlab/ci/configure_debian12_aarch64_ninja.cmake
new file mode 100644
index 0000000..5b97e4f
--- /dev/null
+++ b/.gitlab/ci/configure_debian12_aarch64_ninja.cmake
@@ -0,0 +1,97 @@
+set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "")
+set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
+set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
+set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
+set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
+set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindCups "ON" CACHE BOOL "")
+set(CMake_TEST_FindCURL "ON" CACHE BOOL "")
+set(CMake_TEST_FindDevIL "ON" CACHE BOOL "")
+set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "")
+set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "")
+set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "")
+set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "")
+set(CMake_TEST_FindFreetype "ON" CACHE BOOL "")
+set(CMake_TEST_FindGDAL "ON" CACHE BOOL "")
+set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
+set(CMake_TEST_FindGit "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
+set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
+set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
+set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
+set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
+set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
+set(CMake_TEST_FindICU "ON" CACHE BOOL "")
+set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
+set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
+set(CMake_TEST_FindJNI "ON" CACHE BOOL "")
+set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
+set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
+set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
+set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibUV "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibXml2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI "ON" CACHE BOOL "")
+set(CMake_TEST_FindODBC "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenSP "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenSSL "ON" CACHE BOOL "")
+set(CMake_TEST_FindPatch "ON" CACHE BOOL "")
+set(CMake_TEST_FindPNG "ON" CACHE BOOL "")
+set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "")
+set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "")
+set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3 "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3_NumPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindRuby "ON" CACHE BOOL "")
+set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
+set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
+set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
+set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
+set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
+set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
+set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
+set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "")
+set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
+set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
+set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
+set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian10_extdeps.cmake b/.gitlab/ci/configure_debian12_extdeps.cmake
similarity index 100%
rename from .gitlab/ci/configure_debian10_extdeps.cmake
rename to .gitlab/ci/configure_debian12_extdeps.cmake
diff --git a/.gitlab/ci/configure_debian12_hip_radeon.cmake b/.gitlab/ci/configure_debian12_hip_radeon.cmake
new file mode 100644
index 0000000..c7d7004
--- /dev/null
+++ b/.gitlab/ci/configure_debian12_hip_radeon.cmake
@@ -0,0 +1,3 @@
+set(CMake_TEST_HIP "amd" CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian12_iwyu.cmake b/.gitlab/ci/configure_debian12_iwyu.cmake
new file mode 100644
index 0000000..37ccbf4
--- /dev/null
+++ b/.gitlab/ci/configure_debian12_iwyu.cmake
@@ -0,0 +1,7 @@
+set(CMake_RUN_IWYU ON CACHE BOOL "")
+set(CMake_IWYU_OPTIONS "-DCMAKE_IWYU_FORWARD_STD_HASH" CACHE STRING "")
+# Uncomment to diagnose IWYU problems as needed.
+#set(CMake_IWYU_VERBOSE ON CACHE BOOL "")
+set(IWYU_COMMAND "/usr/bin/include-what-you-use-15" CACHE FILEPATH "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_debian10_makefiles_clang.cmake b/.gitlab/ci/configure_debian12_makefiles_clang.cmake
similarity index 100%
rename from .gitlab/ci/configure_debian10_makefiles_clang.cmake
rename to .gitlab/ci/configure_debian12_makefiles_clang.cmake
diff --git a/.gitlab/ci/configure_debian10_makefiles_inplace.cmake b/.gitlab/ci/configure_debian12_makefiles_inplace.cmake
similarity index 100%
rename from .gitlab/ci/configure_debian10_makefiles_inplace.cmake
rename to .gitlab/ci/configure_debian12_makefiles_inplace.cmake
diff --git a/.gitlab/ci/configure_debian12_ninja.cmake b/.gitlab/ci/configure_debian12_ninja.cmake
new file mode 100644
index 0000000..89c1108
--- /dev/null
+++ b/.gitlab/ci/configure_debian12_ninja.cmake
@@ -0,0 +1,2 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_debian12_ninja_common.cmake")
+set(CMake_TEST_UseSWIG "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_debian10_ninja_clang.cmake b/.gitlab/ci/configure_debian12_ninja_clang.cmake
similarity index 100%
rename from .gitlab/ci/configure_debian10_ninja_clang.cmake
rename to .gitlab/ci/configure_debian12_ninja_clang.cmake
diff --git a/.gitlab/ci/configure_debian12_ninja_common.cmake b/.gitlab/ci/configure_debian12_ninja_common.cmake
new file mode 100644
index 0000000..d4d117b
--- /dev/null
+++ b/.gitlab/ci/configure_debian12_ninja_common.cmake
@@ -0,0 +1,109 @@
+set(CMake_TEST_CTestUpdate_BZR "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_CVS "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "")
+set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "")
+if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+  set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "")
+endif()
+
+set(CMake_TEST_FindALSA "ON" CACHE BOOL "")
+set(CMake_TEST_FindBLAS "All;static=1;Generic" CACHE STRING "")
+set(CMake_TEST_FindBoost "ON" CACHE BOOL "")
+set(CMake_TEST_FindBoost_Python "ON" CACHE BOOL "")
+set(CMake_TEST_FindBZip2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindCups "ON" CACHE BOOL "")
+set(CMake_TEST_FindCURL "ON" CACHE BOOL "")
+set(CMake_TEST_FindDevIL "ON" CACHE BOOL "")
+set(CMake_TEST_FindDoxygen_Dot "ON" CACHE BOOL "")
+set(CMake_TEST_FindDoxygen "ON" CACHE BOOL "")
+set(CMake_TEST_FindEXPAT "ON" CACHE BOOL "")
+set(CMake_TEST_FindFontconfig "ON" CACHE BOOL "")
+set(CMake_TEST_FindFreetype "ON" CACHE BOOL "")
+set(CMake_TEST_FindGDAL "ON" CACHE BOOL "")
+set(CMake_TEST_FindGIF "ON" CACHE BOOL "")
+set(CMake_TEST_FindGit "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLEW "ON" CACHE BOOL "")
+set(CMake_TEST_FindGLUT "ON" CACHE BOOL "")
+set(CMake_TEST_FindGnuTLS "ON" CACHE BOOL "")
+set(CMake_TEST_FindGSL "ON" CACHE BOOL "")
+set(CMake_TEST_FindGTest "ON" CACHE BOOL "")
+set(CMake_TEST_FindGTK2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5 "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER "/usr/bin/h5pcc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER "/usr/bin/h5c++.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER "/usr/bin/h5pfc.mpich" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_MPICH_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER "/usr/bin/h5pcc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_C_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER "/usr/bin/h5c++.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_CXX_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER "/usr/bin/h5pfc.openmpi" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_OpenMPI_Fortran_COMPILER_EXPLICIT "ON" CACHE BOOL "")
+set(CMake_TEST_FindHDF5_Serial_C_COMPILER "/usr/bin/h5cc" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_CXX_COMPILER "/usr/bin/h5c++" CACHE FILEPATH "")
+set(CMake_TEST_FindHDF5_Serial_Fortran_COMPILER "/usr/bin/h5fc" CACHE FILEPATH "")
+set(CMake_TEST_FindIconv "ON" CACHE BOOL "")
+set(CMake_TEST_FindICU "ON" CACHE BOOL "")
+set(CMake_TEST_FindImageMagick "ON" CACHE BOOL "")
+set(CMake_TEST_FindIntl "ON" CACHE BOOL "")
+set(CMake_TEST_FindJNI "ON" CACHE BOOL "")
+set(CMake_TEST_FindJPEG "ON" CACHE BOOL "")
+set(CMake_TEST_FindJsonCpp "ON" CACHE BOOL "")
+set(CMake_TEST_FindLAPACK "All;static=1;Generic" CACHE STRING "")
+set(CMake_TEST_FindLibArchive "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibinput "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibLZMA "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibUV "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibXml2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindLibXslt "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_C "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_FindMPI "ON" CACHE BOOL "")
+set(CMake_TEST_FindODBC "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenACC "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenAL "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenGL "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_C "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenMP "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenSP "ON" CACHE BOOL "")
+set(CMake_TEST_FindOpenSSL "ON" CACHE BOOL "")
+set(CMake_TEST_FindPatch "ON" CACHE BOOL "")
+set(CMake_TEST_FindPNG "ON" CACHE BOOL "")
+set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "")
+set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "")
+set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3 "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3_IronPython "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindRuby "ON" CACHE BOOL "")
+set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "")
+set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
+set(CMake_TEST_FindSQLite3 "ON" CACHE BOOL "")
+set(CMake_TEST_FindTIFF "ON" CACHE BOOL "")
+set(CMake_TEST_FindwxWidgets "ON" CACHE BOOL "")
+set(CMake_TEST_FindX11 "ON" CACHE BOOL "")
+set(CMake_TEST_FindXalanC "ON" CACHE BOOL "")
+set(CMake_TEST_FindXercesC "ON" CACHE BOOL "")
+set(CMake_TEST_Fortran_SUBMODULES "ON" CACHE BOOL "")
+set(CMake_TEST_IPO_WORKS_C "ON" CACHE BOOL "")
+set(CMake_TEST_IPO_WORKS_CXX "ON" CACHE BOOL "")
+set(CMake_TEST_IPO_WORKS_Fortran "ON" CACHE BOOL "")
+set(CMake_TEST_JQ "/usr/bin/jq" CACHE PATH "")
+set(CMake_TEST_Qt5 "ON" CACHE BOOL "")
+set(CMake_TEST_TLS_VERIFY_URL "https://gitlab.kitware.com" CACHE STRING "")
+
+if (NOT "$ENV{SWIFTC}" STREQUAL "")
+  set(CMAKE_Swift_COMPILER "$ENV{SWIFTC}" CACHE FILEPATH "")
+endif()
+
+if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+  set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "")
+endif()
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_debian12_ninja_multi_symlinked.cmake b/.gitlab/ci/configure_debian12_ninja_multi_symlinked.cmake
new file mode 100644
index 0000000..cf9202d
--- /dev/null
+++ b/.gitlab/ci/configure_debian12_ninja_multi_symlinked.cmake
@@ -0,0 +1,3 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_debian12_ninja_common.cmake")
+set(CMake_TEST_UseSWIG "OFF" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_fedora38_hip_radeon.cmake b/.gitlab/ci/configure_fedora38_hip_radeon.cmake
new file mode 100644
index 0000000..c7d7004
--- /dev/null
+++ b/.gitlab/ci/configure_fedora38_hip_radeon.cmake
@@ -0,0 +1,3 @@
+set(CMake_TEST_HIP "amd" CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_fedora38_makefiles.cmake b/.gitlab/ci/configure_fedora38_makefiles.cmake
index c2f9982..a3881cf 100644
--- a/.gitlab/ci/configure_fedora38_makefiles.cmake
+++ b/.gitlab/ci/configure_fedora38_makefiles.cmake
@@ -74,9 +74,12 @@
 set(CMake_TEST_FindPostgreSQL "ON" CACHE BOOL "")
 set(CMake_TEST_FindProtobuf "ON" CACHE BOOL "")
 set(CMake_TEST_FindProtobuf_gRPC "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_NumPy "ON" CACHE BOOL "")
-set(CMake_TEST_FindPython_PyPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython2 "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3 "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython2_NumPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3_NumPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython2_PyPy "ON" CACHE BOOL "")
+set(CMake_TEST_FindPython3_PyPy "ON" CACHE BOOL "")
 set(CMake_TEST_FindRuby "ON" CACHE BOOL "")
 set(CMake_TEST_FindRuby_RVM "ON" CACHE BOOL "")
 set(CMake_TEST_FindSDL "ON" CACHE BOOL "")
diff --git a/.gitlab/ci/configure_fedora38_makefiles_clang.cmake b/.gitlab/ci/configure_fedora38_makefiles_clang.cmake
index ff30ad9..3ac23be 100644
--- a/.gitlab/ci/configure_fedora38_makefiles_clang.cmake
+++ b/.gitlab/ci/configure_fedora38_makefiles_clang.cmake
@@ -1 +1,5 @@
+if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+  set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "")
+endif()
+
 include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora38_makefiles_symlinked.cmake b/.gitlab/ci/configure_fedora38_makefiles_symlinked.cmake
new file mode 100644
index 0000000..177bbcf
--- /dev/null
+++ b/.gitlab/ci/configure_fedora38_makefiles_symlinked.cmake
@@ -0,0 +1,2 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common.cmake")
diff --git a/.gitlab/ci/configure_fedora38_ninja_clang.cmake b/.gitlab/ci/configure_fedora38_ninja_clang.cmake
index 4454647..848c5b6 100644
--- a/.gitlab/ci/configure_fedora38_ninja_clang.cmake
+++ b/.gitlab/ci/configure_fedora38_ninja_clang.cmake
@@ -1,4 +1,4 @@
-set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "")
 set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common_clang.cmake")
diff --git a/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake b/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake
index 4454647..848c5b6 100644
--- a/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake
+++ b/.gitlab/ci/configure_fedora38_ninja_multi_clang.cmake
@@ -1,4 +1,4 @@
-set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "")
 set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_fedora38_common_clang.cmake")
diff --git a/.gitlab/ci/configure_hip5.5_nvidia.cmake b/.gitlab/ci/configure_hip5.5_nvidia.cmake
new file mode 100644
index 0000000..4b3511a
--- /dev/null
+++ b/.gitlab/ci/configure_hip5.5_nvidia.cmake
@@ -0,0 +1,3 @@
+set(CMake_TEST_HIP "nvidia" CACHE BOOL "")
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_hip5.5_radeon.cmake b/.gitlab/ci/configure_hip5.5_radeon.cmake
index 58036b0..c7d7004 100644
--- a/.gitlab/ci/configure_hip5.5_radeon.cmake
+++ b/.gitlab/ci/configure_hip5.5_radeon.cmake
@@ -1,3 +1,3 @@
-set(CMake_TEST_HIP "ON" CACHE BOOL "")
+set(CMake_TEST_HIP "amd" CACHE BOOL "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake
index e9121ae..f0ba9eb 100644
--- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake
+++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja.cmake
@@ -1,4 +1,3 @@
-set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi" CACHE STRING "")
-set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,bmionly" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake
index e9121ae..f0ba9eb 100644
--- a/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake
+++ b/.gitlab/ci/configure_linux_gcc_cxx_modules_ninja_multi.cmake
@@ -1,4 +1,3 @@
-set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi" CACHE STRING "")
-set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_gcc.cmake" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,bmionly" CACHE STRING "")
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_ninja_multi.cmake b/.gitlab/ci/configure_macos_arm64_ninja_multi.cmake
index b22285c..5b19c60 100644
--- a/.gitlab/ci/configure_macos_arm64_ninja_multi.cmake
+++ b/.gitlab/ci/configure_macos_arm64_ninja_multi.cmake
@@ -1,6 +1,11 @@
 if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
   set(CMake_TEST_ISPC "ON" CACHE STRING "")
+  set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "")
 endif()
 
+# FIXME: sccache sometimes fails with "Compiler killed by signal 9".
+# This job does not compile much anyway, so suppress it for now.
+set(configure_no_sccache 1)
+
 include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_ninja_symlinked.cmake b/.gitlab/ci/configure_macos_arm64_ninja_symlinked.cmake
new file mode 100644
index 0000000..63d0dc7
--- /dev/null
+++ b/.gitlab/ci/configure_macos_arm64_ninja_symlinked.cmake
@@ -0,0 +1,2 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_arm64_ninja.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_pch.cmake b/.gitlab/ci/configure_macos_arm64_pch.cmake
new file mode 100644
index 0000000..e2676ba
--- /dev/null
+++ b/.gitlab/ci/configure_macos_arm64_pch.cmake
@@ -0,0 +1,7 @@
+set(CMake_BUILD_PCH "ON" CACHE BOOL "")
+
+# sccache does not forward the PCH '-Xarch_arm64 "-include/..."' flag correctly.
+set(configure_no_sccache 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake")
diff --git a/.gitlab/ci/configure_macos_arm64_xcode_symlinked.cmake b/.gitlab/ci/configure_macos_arm64_xcode_symlinked.cmake
new file mode 100644
index 0000000..66b18e1
--- /dev/null
+++ b/.gitlab/ci/configure_macos_arm64_xcode_symlinked.cmake
@@ -0,0 +1,2 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_symlinked_common.cmake")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_macos_arm64_xcode.cmake")
diff --git a/.gitlab/ci/configure_mingw_osdn_io_common.cmake b/.gitlab/ci/configure_mingw_osdn_io_common.cmake
index 55dce1d..d316233 100644
--- a/.gitlab/ci/configure_mingw_osdn_io_common.cmake
+++ b/.gitlab/ci/configure_mingw_osdn_io_common.cmake
@@ -1,5 +1,8 @@
 set(CMake_TEST_Java OFF CACHE BOOL "")
 
+get_filename_component(mingw_dir "${CMAKE_CURRENT_LIST_DIR}/../mingw" ABSOLUTE)
+set(CMake_TEST_MSYSTEM_PREFIX "${mingw_dir}" CACHE STRING "")
+
 set(configure_no_sccache 1)
 
 include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_symlinked_common.cmake b/.gitlab/ci/configure_symlinked_common.cmake
new file mode 100644
index 0000000..a5e427e
--- /dev/null
+++ b/.gitlab/ci/configure_symlinked_common.cmake
@@ -0,0 +1,7 @@
+# Ensure that the symlink tree is set up correctly.
+if(NOT CMAKE_SOURCE_DIR STREQUAL "$ENV{CI_PROJECT_DIR}/work/cmake")
+  message(FATAL_ERROR "Expected value of CMAKE_SOURCE_DIR:\n  $ENV{CI_PROJECT_DIR}/work/cmake\nActual value:\n  ${CMAKE_SOURCE_DIR}")
+endif()
+if(NOT CMAKE_BINARY_DIR STREQUAL "$ENV{CI_PROJECT_DIR}/work/build")
+  message(FATAL_ERROR "Expected value of CMAKE_BINARY_DIR:\n  $ENV{CI_PROJECT_DIR}/work/build\nActual value:\n  ${CMAKE_BINARY_DIR}")
+endif()
diff --git a/.gitlab/ci/configure_windows_clang_ninja.cmake b/.gitlab/ci/configure_windows_clang_ninja.cmake
index d1f0f52..8a65eef 100644
--- a/.gitlab/ci/configure_windows_clang_ninja.cmake
+++ b/.gitlab/ci/configure_windows_clang_ninja.cmake
@@ -1,5 +1,5 @@
 if("$ENV{CMAKE_CI_BUILD_NAME}" MATCHES "(^|_)gnu(_|$)")
-  set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared" CACHE STRING "")
+  set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,export_bmi,install_bmi,shared,bmionly" CACHE STRING "")
   set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_clang.cmake" CACHE STRING "")
 endif()
 include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_clang_common.cmake")
diff --git a/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake b/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake
index 5f2a215..e378d62 100644
--- a/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake
+++ b/.gitlab/ci/configure_windows_msvc_cxx_modules_common.cmake
@@ -1,2 +1 @@
-set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,shared,export_bmi,install_bmi" CACHE STRING "")
-set(CMake_TEST_MODULE_COMPILATION_RULES "${CMAKE_CURRENT_LIST_DIR}/cxx_modules_rules_msvc.cmake" CACHE STRING "")
+set(CMake_TEST_MODULE_COMPILATION "named,compile_commands,collation,partitions,internal_partitions,shared,export_bmi,install_bmi,bmionly" CACHE STRING "")
diff --git a/.gitlab/ci/configure_windows_orangec6.73.1.cmake b/.gitlab/ci/configure_windows_orangec6.73.1.cmake
new file mode 100644
index 0000000..e667b94
--- /dev/null
+++ b/.gitlab/ci/configure_windows_orangec6.73.1.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_orangec_common.cmake")
diff --git a/.gitlab/ci/configure_windows_orangec_common.cmake b/.gitlab/ci/configure_windows_orangec_common.cmake
new file mode 100644
index 0000000..55dce1d
--- /dev/null
+++ b/.gitlab/ci/configure_windows_orangec_common.cmake
@@ -0,0 +1,5 @@
+set(CMake_TEST_Java OFF CACHE BOOL "")
+
+set(configure_no_sccache 1)
+
+include("${CMAKE_CURRENT_LIST_DIR}/configure_external_test.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2019_x64.cmake b/.gitlab/ci/configure_windows_vs2019_x64.cmake
index c7d41ea..b859525 100644
--- a/.gitlab/ci/configure_windows_vs2019_x64.cmake
+++ b/.gitlab/ci/configure_windows_vs2019_x64.cmake
@@ -1 +1,5 @@
+if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
+  set(CMAKE_TESTS_CDASH_SERVER "https://open.cdash.org" CACHE STRING "")
+endif()
+
 include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_vs_common.cmake")
diff --git a/.gitlab/ci/configure_windows_vs2022_x64_pch.cmake b/.gitlab/ci/configure_windows_vs2022_x64_pch.cmake
new file mode 100644
index 0000000..2a2eed7
--- /dev/null
+++ b/.gitlab/ci/configure_windows_vs2022_x64_pch.cmake
@@ -0,0 +1,2 @@
+set(CMake_BUILD_PCH "ON" CACHE BOOL "")
+include("${CMAKE_CURRENT_LIST_DIR}/configure_windows_common.cmake")
diff --git a/.gitlab/ci/cxx_modules_rules_clang.cmake b/.gitlab/ci/cxx_modules_rules_clang.cmake
index a8e1ff6..1268244 100644
--- a/.gitlab/ci/cxx_modules_rules_clang.cmake
+++ b/.gitlab/ci/cxx_modules_rules_clang.cmake
@@ -1,5 +1,3 @@
-set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
-
 # Default to C++ extensions being off. Clang's modules support have trouble
 # with extensions right now.
 set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/.gitlab/ci/cxx_modules_rules_gcc.cmake b/.gitlab/ci/cxx_modules_rules_gcc.cmake
deleted file mode 100644
index 3777506..0000000
--- a/.gitlab/ci/cxx_modules_rules_gcc.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
-
-string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
-  "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>"
-  " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
-  " -fmodules-ts -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT> -fdep-format=trtbd"
-  " -o <PREPROCESSED_SOURCE>")
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "-fmodules-ts -fmodule-mapper=<MODULE_MAP_FILE> -fdep-format=trtbd -x c++")
diff --git a/.gitlab/ci/cxx_modules_rules_msvc.cmake b/.gitlab/ci/cxx_modules_rules_msvc.cmake
deleted file mode 100644
index 2b09b0e..0000000
--- a/.gitlab/ci/cxx_modules_rules_msvc.cmake
+++ /dev/null
@@ -1 +0,0 @@
-set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
diff --git a/.gitlab/ci/docker/debian10-aarch64/Dockerfile b/.gitlab/ci/docker/debian10-aarch64/Dockerfile
deleted file mode 100644
index a0687e3..0000000
--- a/.gitlab/ci/docker/debian10-aarch64/Dockerfile
+++ /dev/null
@@ -1,26 +0,0 @@
-# syntax=docker/dockerfile:1
-
-ARG BASE_IMAGE=arm64v8/debian:10
-
-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=deps_packages.lst,target=/root/deps_packages.lst \
-    apt-get update \
- && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
-
-FROM ${BASE_IMAGE}
-LABEL maintainer="Brad King <brad.king@kitware.com>"
-
-RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \
-    --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \
-    --mount=type=bind,source=dpkg-exclude,target=/etc/dpkg/dpkg.cfg.d/exclude \
-    --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 \
-    sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst b/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst
deleted file mode 100644
index ca83323..0000000
--- a/.gitlab/ci/docker/debian10-aarch64/deps_packages.lst
+++ /dev/null
@@ -1,94 +0,0 @@
-# Install build requirements.
-libssl-dev
-
-# Install development tools.
-g++
-curl
-git
-
-# Install optional external build dependencies.
-libarchive-dev
-libbz2-dev
-libcurl4-gnutls-dev
-libexpat1-dev
-libjsoncpp-dev
-liblzma-dev
-libncurses-dev
-librhash-dev
-libuv1-dev
-libzstd-dev
-zlib1g-dev
-
-# Install iwyu runtime deps.
-clang-6.0
-libncurses6
-
-# Tools needed for the test suite.
-jq
-
-# Packages needed to test CTest.
-bzr bzr-xmloutput
-cvs
-subversion
-mercurial
-
-# Packages needed to test find modules.
-alsa-utils
-doxygen graphviz
-freeglut3-dev
-gnutls-dev
-libarchive-dev
-libblas-dev
-libboost-dev
-libboost-filesystem-dev
-libboost-program-options-dev
-libboost-python-dev
-libboost-thread-dev
-libbz2-dev
-libcups2-dev
-libcurl4-gnutls-dev
-libdevil-dev
-libfontconfig1-dev
-libfreetype6-dev
-libgdal-dev
-libgif-dev
-libgl1-mesa-dev
-libglew-dev
-libgmock-dev
-libgrpc++-dev libgrpc-dev
-libgsl-dev
-libgtest-dev
-libgtk2.0-dev
-libhdf5-dev
-libhdf5-mpich-dev
-libhdf5-openmpi-dev
-libicu-dev
-libinput-dev
-libjpeg-dev
-libjsoncpp-dev
-liblapack-dev
-liblzma-dev
-libmagick++-dev
-libopenal-dev
-libopenmpi-dev openmpi-bin
-libosp-dev
-libpng-dev
-libpq-dev postgresql-server-dev-11
-libprotobuf-dev libprotobuf-c-dev libprotoc-dev protobuf-compiler protobuf-compiler-grpc
-libsdl-dev
-libsqlite3-dev
-libtiff-dev
-libuv1-dev
-libwxgtk3.0-dev
-libx11-dev
-libxalan-c-dev
-libxerces-c-dev
-libxml2-dev libxml2-utils
-libxslt-dev xsltproc
-openjdk-11-jdk
-python2 python2-dev python-numpy pypy pypy-dev
-python3 python3-dev python3-numpy pypy3 pypy3-dev python3-venv
-qtbase5-dev qtbase5-dev-tools
-ruby ruby-dev
-swig
-unixodbc-dev
diff --git a/.gitlab/ci/docker/debian10-x86_64/Dockerfile b/.gitlab/ci/docker/debian10-x86_64/Dockerfile
new file mode 100644
index 0000000..c39b380
--- /dev/null
+++ b/.gitlab/ci/docker/debian10-x86_64/Dockerfile
@@ -0,0 +1,27 @@
+# syntax=docker/dockerfile:1
+
+ARG BASE_IMAGE=debian:10
+
+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=deps_packages.lst,target=/root/deps_packages.lst \
+    apt-get update \
+ && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
+
+
+FROM ${BASE_IMAGE}
+LABEL maintainer="Brad King <brad.king@kitware.com>"
+
+RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \
+    --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \
+    --mount=type=bind,source=dpkg-exclude,target=/etc/dpkg/dpkg.cfg.d/exclude \
+    --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 \
+    sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/debian10-x86_64/deps_packages.lst b/.gitlab/ci/docker/debian10-x86_64/deps_packages.lst
new file mode 100644
index 0000000..b415421
--- /dev/null
+++ b/.gitlab/ci/docker/debian10-x86_64/deps_packages.lst
@@ -0,0 +1,16 @@
+# Install build requirements.
+libssl-dev
+
+# Install development tools.
+g++
+curl
+git
+
+# Tools needed for the test suite.
+jq
+
+# Packages needed to test find modules.
+python2 python2-dev python-numpy pypy pypy-dev
+
+# CMake_TEST_FindPython2_IronPython
+libmono-system-windows-forms4.0-cil
diff --git a/.gitlab/ci/docker/debian10-aarch64/docker-clean b/.gitlab/ci/docker/debian10-x86_64/docker-clean
similarity index 100%
copy from .gitlab/ci/docker/debian10-aarch64/docker-clean
copy to .gitlab/ci/docker/debian10-x86_64/docker-clean
diff --git a/.gitlab/ci/docker/debian10-aarch64/dpkg-exclude b/.gitlab/ci/docker/debian10-x86_64/dpkg-exclude
similarity index 100%
copy from .gitlab/ci/docker/debian10-aarch64/dpkg-exclude
copy to .gitlab/ci/docker/debian10-x86_64/dpkg-exclude
diff --git a/.gitlab/ci/docker/debian10-x86_64/install_deps.sh b/.gitlab/ci/docker/debian10-x86_64/install_deps.sh
new file mode 100755
index 0000000..002f136
--- /dev/null
+++ b/.gitlab/ci/docker/debian10-x86_64/install_deps.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+set -e
+
+apt-get install -y $(grep '^[^#]\+$' /root/deps_packages.lst)
+
+curl -L -O https://github.com/IronLanguages/ironpython2/releases/download/ipy-2.7.12/ironpython_2.7.12.deb
+echo 'b7b90c82cf311dd3faf290ce3f274af5128b96db884a88dd643ce141bbf12fb9  ironpython_2.7.12.deb' > ironpython.sha256sum
+sha256sum --check ironpython.sha256sum
+dpkg -i ironpython_2.7.12.deb
+rm ironpython_2.7.12.deb ironpython.sha256sum
diff --git a/.gitlab/ci/docker/debian10/Dockerfile b/.gitlab/ci/docker/debian10/Dockerfile
deleted file mode 100644
index d866428..0000000
--- a/.gitlab/ci/docker/debian10/Dockerfile
+++ /dev/null
@@ -1,62 +0,0 @@
-# syntax=docker/dockerfile:1
-
-ARG BASE_IMAGE=debian:10
-
-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=deps_packages.lst,target=/root/deps_packages.lst \
-    --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \
-    --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \
-    apt-get update \
- && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
-
-
-FROM ${BASE_IMAGE} AS iwyu-build
-LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>"
-
-RUN --mount=type=bind,source=install_iwyu.sh,target=/root/install_iwyu.sh \
-    --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.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 \
-    sh /root/install_iwyu.sh
-
-
-FROM ${BASE_IMAGE} AS rvm-build
-LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>"
-
-RUN --mount=type=bind,source=install_rvm.sh,target=/root/install_rvm.sh \
-    --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.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 \
-    sh /root/install_rvm.sh
-
-
-FROM ${BASE_IMAGE}
-LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>"
-
-RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \
-    --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \
-    --mount=type=bind,source=dpkg-exclude,target=/etc/dpkg/dpkg.cfg.d/exclude \
-    --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 \
-    sh /root/install_deps.sh
-
-RUN --mount=type=bind,from=iwyu-build,source=/root,target=/root \
-    tar -C / -xf /root/iwyu.tar \
- && ln -s /usr/lib/llvm-6.0/bin/include-what-you-use /usr/bin/include-what-you-use-6.0
-
-RUN --mount=type=bind,from=rvm-build,source=/root,target=/root \
-    tar -C /usr/local -xf /root/rvm.tar
diff --git a/.gitlab/ci/docker/debian10/deps_packages.lst b/.gitlab/ci/docker/debian10/deps_packages.lst
deleted file mode 100644
index 0b79675..0000000
--- a/.gitlab/ci/docker/debian10/deps_packages.lst
+++ /dev/null
@@ -1,100 +0,0 @@
-# Install build requirements.
-libssl-dev
-
-# Install development tools.
-g++
-curl
-git
-
-# Install optional external build dependencies.
-libarchive-dev
-libbz2-dev
-libcurl4-gnutls-dev
-libexpat1-dev
-libjsoncpp-dev
-liblzma-dev
-libncurses-dev
-librhash-dev
-libuv1-dev
-libzstd-dev
-zlib1g-dev
-
-# Install iwyu runtime deps.
-clang-6.0
-libncurses6
-
-# Tools needed for the test suite.
-jq
-
-# Packages needed to test CTest.
-bzr bzr-xmloutput
-cvs
-subversion
-mercurial
-
-# Install swift runtime deps.
-libncurses5
-
-# Packages needed to test find modules.
-alsa-utils
-doxygen graphviz
-freeglut3-dev
-gnutls-dev
-libarchive-dev
-libblas-dev
-libboost-dev
-libboost-filesystem-dev
-libboost-program-options-dev
-libboost-python-dev
-libboost-thread-dev
-libbz2-dev
-libcups2-dev
-libcurl4-gnutls-dev
-libdevil-dev
-libfontconfig1-dev
-libfreetype6-dev
-libgdal-dev
-libgif-dev
-libgl1-mesa-dev
-libglew-dev
-libgmock-dev
-libgrpc++-dev libgrpc-dev
-libgsl-dev
-libgtest-dev
-libgtk2.0-dev
-libhdf5-dev
-libhdf5-mpich-dev
-libhdf5-openmpi-dev
-libicu-dev
-libinput-dev
-libjpeg-dev
-libjsoncpp-dev
-liblapack-dev
-liblzma-dev
-libmagick++-dev
-libopenal-dev
-libopenmpi-dev openmpi-bin
-libosp-dev
-libpng-dev
-libpq-dev postgresql-server-dev-11
-libprotobuf-dev libprotobuf-c-dev libprotoc-dev protobuf-compiler protobuf-compiler-grpc
-libsdl-dev
-libsqlite3-dev
-libtiff-dev
-libuv1-dev
-libwxgtk3.0-dev
-libx11-dev
-libxalan-c-dev
-libxerces-c-dev
-libxml2-dev libxml2-utils
-libxslt-dev xsltproc
-openjdk-11-jdk
-python2 python2-dev python-numpy pypy pypy-dev
-python3 python3-dev python3-numpy pypy3 pypy3-dev python3-venv
-qtbase5-dev qtbase5-dev-tools
-ruby ruby-dev
-swig
-unixodbc-dev
-
-# CMake_TEST_FindPython_IronPython
-libmono-system-windows-forms4.0-cil
diff --git a/.gitlab/ci/docker/debian10/docker-clean b/.gitlab/ci/docker/debian10/docker-clean
deleted file mode 100644
index e69de29..0000000
--- a/.gitlab/ci/docker/debian10/docker-clean
+++ /dev/null
diff --git a/.gitlab/ci/docker/debian10/dpkg-exclude b/.gitlab/ci/docker/debian10/dpkg-exclude
deleted file mode 100644
index 60b6565..0000000
--- a/.gitlab/ci/docker/debian10/dpkg-exclude
+++ /dev/null
@@ -1,21 +0,0 @@
-# Drop all man pages
-path-exclude=/usr/share/man/*
-
-# Drop all info pages
-path-exclude=/usr/share/info/*
-
-# Drop all README files except from the some packages
-path-exclude=/usr/**/*README*
-path-include=/usr/share/devscripts/templates/README.mk-build-deps
-path-include=/usr/share/equivs/template/debian/README.Debian.in
-
-# Drop all translations
-path-exclude=/usr/share/locale/*/LC_MESSAGES/*.mo
-
-# Drop all documentation ...
-path-exclude=/usr/share/doc/*
-path-exclude=/usr/share/doc-base/*
-path-exclude=/usr/share/gtk-doc/*
-
-# Per package excludes
-path-exclude=/usr/share/gnupg/help.*.txt
diff --git a/.gitlab/ci/docker/debian10/install_deps.sh b/.gitlab/ci/docker/debian10/install_deps.sh
deleted file mode 100755
index a00e322..0000000
--- a/.gitlab/ci/docker/debian10/install_deps.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-set -e
-
-apt-get install -y $(grep '^[^#]\+$' /root/deps_packages.lst)
-
-curl -L -O https://github.com/IronLanguages/ironpython2/releases/download/ipy-2.7.10/ironpython_2.7.10.deb
-echo 'e1aceec1d49ffa66e9059a52168a734999dcccc50164a60e2936649cae698f3e  ironpython_2.7.10.deb' > ironpython.sha256sum
-sha256sum --check ironpython.sha256sum
-dpkg -i ironpython_2.7.10.deb
-rm ironpython_2.7.10.deb ironpython.sha256sum
-
-# Perforce
-curl -L https://www.perforce.com/downloads/perforce/r21.2/bin.linux26x86_64/helix-core-server.tgz -o - \
-  | tar -C /usr/local/bin -xvzf - -- p4 p4d
diff --git a/.gitlab/ci/docker/debian10/install_iwyu.sh b/.gitlab/ci/docker/debian10/install_iwyu.sh
deleted file mode 100755
index 4814a71..0000000
--- a/.gitlab/ci/docker/debian10/install_iwyu.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/sh
-
-set -e
-
-# Install development tools.
-apt-get install -y $(grep '^[^#]\+$' /root/iwyu_packages.lst)
-
-cd /root
-git clone "https://github.com/include-what-you-use/include-what-you-use.git"
-cd include-what-you-use
-readonly llvm_version="$( clang-6.0 --version | head -n1 | cut -d' ' -f3 | cut -d. -f-2 )"
-git checkout "clang_$llvm_version"
-mkdir build
-cd build
-
-cmake -GNinja \
-    -DCMAKE_BUILD_TYPE=Release \
-    "-DCMAKE_INSTALL_PREFIX=/usr/lib/llvm-$llvm_version" \
-    "-DIWYU_LLVM_ROOT_PATH=/usr/lib/llvm-$llvm_version" \
-    ..
-ninja
-DESTDIR=/root/iwyu-destdir ninja install
-tar -C /root/iwyu-destdir -cf /root/iwyu.tar .
diff --git a/.gitlab/ci/docker/debian10/install_rvm.sh b/.gitlab/ci/docker/debian10/install_rvm.sh
deleted file mode 100755
index c6fff70..0000000
--- a/.gitlab/ci/docker/debian10/install_rvm.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/sh
-
-set -e
-
-apt-get install -y $(grep '^[^#]\+$' /root/rvm_packages.lst)
-
-gpg2 --keyserver hkps://keyserver.ubuntu.com \
-     --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \
-                 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
-
-curl -sSL https://get.rvm.io | bash -s stable
-
-# keep version in sync with `env_debian*_ninja.sh`
-/usr/local/rvm/bin/rvm install ruby-2.7.0
-
-for p in archives examples gem-cache log src; do
-    touch /usr/local/rvm/${p}/.tar_exclude
-done
-
-tar -C /usr/local --exclude-tag-under=.tar_exclude -cf /root/rvm.tar rvm
diff --git a/.gitlab/ci/docker/debian10/iwyu_packages.lst b/.gitlab/ci/docker/debian10/iwyu_packages.lst
deleted file mode 100644
index 9e291c9..0000000
--- a/.gitlab/ci/docker/debian10/iwyu_packages.lst
+++ /dev/null
@@ -1,9 +0,0 @@
-# Install development tools.
-clang-6.0
-libclang-6.0-dev
-llvm-6.0-dev
-libz-dev
-g++
-cmake
-ninja-build
-git
diff --git a/.gitlab/ci/docker/debian12-aarch64/Dockerfile b/.gitlab/ci/docker/debian12-aarch64/Dockerfile
new file mode 100644
index 0000000..58b5e0e
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-aarch64/Dockerfile
@@ -0,0 +1,26 @@
+# syntax=docker/dockerfile:1
+
+ARG BASE_IMAGE=arm64v8/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=deps_packages.lst,target=/root/deps_packages.lst \
+    apt-get update \
+ && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
+
+FROM ${BASE_IMAGE}
+LABEL maintainer="Brad King <brad.king@kitware.com>"
+
+RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \
+    --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \
+    --mount=type=bind,source=dpkg-exclude,target=/etc/dpkg/dpkg.cfg.d/exclude \
+    --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 \
+    sh /root/install_deps.sh
diff --git a/.gitlab/ci/docker/debian12-aarch64/deps_packages.lst b/.gitlab/ci/docker/debian12-aarch64/deps_packages.lst
new file mode 100644
index 0000000..c92c715
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-aarch64/deps_packages.lst
@@ -0,0 +1,93 @@
+# Install build requirements.
+libssl-dev
+
+# Install development tools.
+g++
+curl
+git
+
+# Install optional external build dependencies.
+libarchive-dev
+libbz2-dev
+libcurl4-gnutls-dev
+libexpat1-dev
+libjsoncpp-dev
+liblzma-dev
+libncurses-dev
+librhash-dev
+libuv1-dev
+libzstd-dev
+zlib1g-dev
+
+# Install iwyu runtime deps.
+clang-15
+libncurses6
+
+# Tools needed for the test suite.
+jq
+
+# Packages needed to test CTest.
+bzr
+cvs
+subversion
+mercurial
+
+# Packages needed to test find modules.
+alsa-utils
+doxygen graphviz
+freeglut3-dev
+gnutls-dev
+libarchive-dev
+libblas-dev
+libboost-dev
+libboost-filesystem-dev
+libboost-program-options-dev
+libboost-python-dev
+libboost-thread-dev
+libbz2-dev
+libcups2-dev
+libcurl4-gnutls-dev
+libdevil-dev
+libfontconfig1-dev
+libfreetype6-dev
+libgdal-dev
+libgif-dev
+libgl1-mesa-dev
+libglew-dev
+libgmock-dev
+libgrpc++-dev libgrpc-dev
+libgsl-dev
+libgtest-dev
+libgtk2.0-dev
+libhdf5-dev
+libhdf5-mpich-dev
+libhdf5-openmpi-dev
+libicu-dev
+libinput-dev
+libjpeg-dev
+libjsoncpp-dev
+liblapack-dev
+liblzma-dev
+libmagick++-dev
+libopenal-dev
+libopenmpi-dev openmpi-bin
+libosp-dev
+libpng-dev
+libpq-dev postgresql-server-dev-15
+libprotobuf-dev libprotobuf-c-dev libprotoc-dev protobuf-compiler protobuf-compiler-grpc
+libsdl1.2-dev
+libsqlite3-dev
+libtiff-dev
+libuv1-dev
+libwxgtk3.2-dev
+libx11-dev
+libxalan-c-dev
+libxerces-c-dev
+libxml2-dev libxml2-utils
+libxslt-dev xsltproc
+openjdk-17-jdk
+python3 python3-dev python3-numpy pypy3 pypy3-dev python3-venv
+qtbase5-dev qtbase5-dev-tools
+ruby ruby-dev
+swig
+unixodbc-dev
diff --git a/.gitlab/ci/docker/debian10-aarch64/docker-clean b/.gitlab/ci/docker/debian12-aarch64/docker-clean
similarity index 100%
rename from .gitlab/ci/docker/debian10-aarch64/docker-clean
rename to .gitlab/ci/docker/debian12-aarch64/docker-clean
diff --git a/.gitlab/ci/docker/debian10-aarch64/dpkg-exclude b/.gitlab/ci/docker/debian12-aarch64/dpkg-exclude
similarity index 100%
rename from .gitlab/ci/docker/debian10-aarch64/dpkg-exclude
rename to .gitlab/ci/docker/debian12-aarch64/dpkg-exclude
diff --git a/.gitlab/ci/docker/debian10-aarch64/install_deps.sh b/.gitlab/ci/docker/debian12-aarch64/install_deps.sh
similarity index 100%
rename from .gitlab/ci/docker/debian10-aarch64/install_deps.sh
rename to .gitlab/ci/docker/debian12-aarch64/install_deps.sh
diff --git a/.gitlab/ci/docker/debian12-x86_64/Dockerfile b/.gitlab/ci/docker/debian12-x86_64/Dockerfile
new file mode 100644
index 0000000..477e763
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-x86_64/Dockerfile
@@ -0,0 +1,62 @@
+# 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=deps_packages.lst,target=/root/deps_packages.lst \
+    --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.lst \
+    --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.lst \
+    apt-get update \
+ && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
+
+
+FROM ${BASE_IMAGE} AS iwyu-build
+LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>"
+
+RUN --mount=type=bind,source=install_iwyu.sh,target=/root/install_iwyu.sh \
+    --mount=type=bind,source=iwyu_packages.lst,target=/root/iwyu_packages.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 \
+    sh /root/install_iwyu.sh
+
+
+FROM ${BASE_IMAGE} AS rvm-build
+LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>"
+
+RUN --mount=type=bind,source=install_rvm.sh,target=/root/install_rvm.sh \
+    --mount=type=bind,source=rvm_packages.lst,target=/root/rvm_packages.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 \
+    sh /root/install_rvm.sh
+
+
+FROM ${BASE_IMAGE}
+LABEL maintainer="Ben Boeckel <ben.boeckel@kitware.com>"
+
+RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \
+    --mount=type=bind,source=deps_packages.lst,target=/root/deps_packages.lst \
+    --mount=type=bind,source=dpkg-exclude,target=/etc/dpkg/dpkg.cfg.d/exclude \
+    --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 \
+    sh /root/install_deps.sh
+
+RUN --mount=type=bind,from=iwyu-build,source=/root,target=/root \
+    tar -C / -xf /root/iwyu.tar \
+ && ln -s /usr/lib/llvm-15/bin/include-what-you-use /usr/bin/include-what-you-use-15
+
+RUN --mount=type=bind,from=rvm-build,source=/root,target=/root \
+    tar -C /usr/local -xf /root/rvm.tar
diff --git a/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst b/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst
new file mode 100644
index 0000000..a59f2c9
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-x86_64/deps_packages.lst
@@ -0,0 +1,102 @@
+# Install build requirements.
+libssl-dev
+
+# Install development tools.
+g++
+curl
+git
+
+# Install optional external build dependencies.
+libarchive-dev
+libbz2-dev
+libcurl4-gnutls-dev
+libexpat1-dev
+libjsoncpp-dev
+liblzma-dev
+libncurses-dev
+librhash-dev
+libuv1-dev
+libzstd-dev
+zlib1g-dev
+
+# Install iwyu runtime deps.
+clang-15
+libncurses6
+
+# Tools needed for the test suite.
+jq
+
+# Packages needed to test CTest.
+bzr
+cvs
+subversion
+mercurial
+
+# Install HIP language toolchain.
+hipcc
+
+# Install swift runtime deps.
+libncurses5
+
+# Packages needed to test find modules.
+alsa-utils
+doxygen graphviz
+freeglut3-dev
+gnutls-dev
+libarchive-dev
+libblas-dev
+libboost-dev
+libboost-filesystem-dev
+libboost-program-options-dev
+libboost-python-dev
+libboost-thread-dev
+libbz2-dev
+libcups2-dev
+libcurl4-gnutls-dev
+libdevil-dev
+libfontconfig1-dev
+libfreetype6-dev
+libgdal-dev
+libgif-dev
+libgl1-mesa-dev
+libglew-dev
+libgmock-dev
+libgrpc++-dev libgrpc-dev
+libgsl-dev
+libgtest-dev
+libgtk2.0-dev
+libhdf5-dev
+libhdf5-mpich-dev
+libhdf5-openmpi-dev
+libicu-dev
+libinput-dev
+libjpeg-dev
+libjsoncpp-dev
+liblapack-dev
+liblzma-dev
+libmagick++-dev
+libopenal-dev
+libopenmpi-dev openmpi-bin
+libosp-dev
+libpng-dev
+libpq-dev postgresql-server-dev-15
+libprotobuf-dev libprotobuf-c-dev libprotoc-dev protobuf-compiler protobuf-compiler-grpc
+libsdl1.2-dev
+libsqlite3-dev
+libtiff-dev
+libuv1-dev
+libwxgtk3.2-dev
+libx11-dev
+libxalan-c-dev
+libxerces-c-dev
+libxml2-dev libxml2-utils
+libxslt-dev xsltproc
+openjdk-17-jdk
+python3 python3-dev python3-numpy pypy3 pypy3-dev python3-venv
+qtbase5-dev qtbase5-dev-tools
+ruby ruby-dev
+swig
+unixodbc-dev
+
+# Packages needed to test ironpython.
+libmono-system-windows-forms4.0-cil
diff --git a/.gitlab/ci/docker/debian10-aarch64/docker-clean b/.gitlab/ci/docker/debian12-x86_64/docker-clean
similarity index 100%
copy from .gitlab/ci/docker/debian10-aarch64/docker-clean
copy to .gitlab/ci/docker/debian12-x86_64/docker-clean
diff --git a/.gitlab/ci/docker/debian10-aarch64/dpkg-exclude b/.gitlab/ci/docker/debian12-x86_64/dpkg-exclude
similarity index 100%
copy from .gitlab/ci/docker/debian10-aarch64/dpkg-exclude
copy to .gitlab/ci/docker/debian12-x86_64/dpkg-exclude
diff --git a/.gitlab/ci/docker/debian12-x86_64/install_deps.sh b/.gitlab/ci/docker/debian12-x86_64/install_deps.sh
new file mode 100755
index 0000000..7bfcacd
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-x86_64/install_deps.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+set -e
+
+apt-get install -y $(grep '^[^#]\+$' /root/deps_packages.lst)
+
+curl -L -O https://github.com/IronLanguages/ironpython3/releases/download/v3.4.0/ironpython_3.4.0.deb
+echo '7dcd10b7a0ec0342bd7e20eebb597a96bb15267eb797d59358a3b1cfaa3e1adc  ironpython_3.4.0.deb' > ironpython.sha256sum
+sha256sum --check ironpython.sha256sum
+dpkg -i ironpython_3.4.0.deb
+rm ironpython_3.4.0.deb ironpython.sha256sum
+
+# Perforce
+curl -L https://www.perforce.com/downloads/perforce/r21.2/bin.linux26x86_64/helix-core-server.tgz -o - \
+  | tar -C /usr/local/bin -xvzf - -- p4 p4d
diff --git a/.gitlab/ci/docker/debian12-x86_64/install_iwyu.sh b/.gitlab/ci/docker/debian12-x86_64/install_iwyu.sh
new file mode 100755
index 0000000..bbeceb8
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-x86_64/install_iwyu.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+set -e
+
+# Install development tools.
+apt-get install -y $(grep '^[^#]\+$' /root/iwyu_packages.lst)
+
+cd /root
+git clone "https://github.com/include-what-you-use/include-what-you-use.git"
+cd include-what-you-use
+readonly llvm_version="$( clang-15 --version | head -n1 | cut -d' ' -f4 | cut -d. -f-1 )"
+git checkout "clang_$llvm_version"
+mkdir build
+cd build
+
+cmake -GNinja \
+    -DCMAKE_BUILD_TYPE=Release \
+    "-DCMAKE_INSTALL_PREFIX=/usr/lib/llvm-$llvm_version" \
+    ..
+ninja
+DESTDIR=/root/iwyu-destdir ninja install
+tar -C /root/iwyu-destdir -cf /root/iwyu.tar .
diff --git a/.gitlab/ci/docker/debian12-x86_64/install_rvm.sh b/.gitlab/ci/docker/debian12-x86_64/install_rvm.sh
new file mode 100755
index 0000000..1ad42c4
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-x86_64/install_rvm.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -e
+
+apt-get install -y $(grep '^[^#]\+$' /root/rvm_packages.lst)
+
+gpg2 --keyserver hkps://keyserver.ubuntu.com \
+     --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 \
+                 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
+
+curl -sSL https://get.rvm.io | bash -s stable
+
+# keep version in sync with `env_debian*_ninja.sh`
+/usr/local/rvm/bin/rvm install ruby-3.2.2
+
+for p in archives examples gem-cache log src; do
+    touch /usr/local/rvm/${p}/.tar_exclude
+done
+
+tar -C /usr/local --exclude-tag-under=.tar_exclude -cf /root/rvm.tar rvm
diff --git a/.gitlab/ci/docker/debian12-x86_64/iwyu_packages.lst b/.gitlab/ci/docker/debian12-x86_64/iwyu_packages.lst
new file mode 100644
index 0000000..2dbddba
--- /dev/null
+++ b/.gitlab/ci/docker/debian12-x86_64/iwyu_packages.lst
@@ -0,0 +1,9 @@
+# Install development tools.
+clang-15
+libclang-15-dev
+llvm-15-dev
+libz-dev
+g++
+cmake
+ninja-build
+git
diff --git a/.gitlab/ci/docker/debian10/rvm_packages.lst b/.gitlab/ci/docker/debian12-x86_64/rvm_packages.lst
similarity index 100%
rename from .gitlab/ci/docker/debian10/rvm_packages.lst
rename to .gitlab/ci/docker/debian12-x86_64/rvm_packages.lst
diff --git a/.gitlab/ci/docker/fedora38/deps_packages.lst b/.gitlab/ci/docker/fedora38/deps_packages.lst
index c7c1385..da050d9 100644
--- a/.gitlab/ci/docker/fedora38/deps_packages.lst
+++ b/.gitlab/ci/docker/fedora38/deps_packages.lst
@@ -44,6 +44,13 @@
 jq
 which
 
+# Install HIP language toolchain.
+hsakmt-devel
+lld
+rocm-comgr-devel
+rocm-hip-devel
+rocm-runtime-devel
+
 # Packages needed to test CTest.
 breezy
 subversion
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile
index e0af0b9..d8e8238 100644
--- a/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile
+++ b/.gitlab/ci/docker/gcc_cxx_modules/Dockerfile
@@ -1,4 +1,4 @@
-FROM fedora:36
+FROM fedora:38
 MAINTAINER Ben Boeckel <ben.boeckel@kitware.com>
 
 # Install build dependencies for packages.
diff --git a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh
index 20ea35f..15cfe39 100755
--- a/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh
+++ b/.gitlab/ci/docker/gcc_cxx_modules/install_gcc.sh
@@ -2,8 +2,8 @@
 
 set -e
 
-readonly revision="p1689r5-cmake-ci-20220614" # 3075e510e3d29583f8886b95aff044c0474c84a5
-readonly tarball="https://github.com/mathstuf/gcc/archive/$revision.tar.gz"
+readonly revision="29862e21f6d656eca59284c927d0c4c0698eb99c" # master as of 21 Sep 2023
+readonly tarball="git://gcc.gnu.org/git/gcc.git"
 
 readonly workdir="$HOME/gcc"
 readonly srcdir="$workdir/gcc"
@@ -12,9 +12,8 @@
 
 mkdir -p "$workdir"
 cd "$workdir"
-curl -L "$tarball" > "gcc-$revision.tar.gz"
-tar xf "gcc-$revision.tar.gz"
-mv "gcc-$revision" "$srcdir"
+git clone "$tarball" "$srcdir"
+git -C "$srcdir" checkout "$revision"
 mkdir -p "$builddir"
 cd "$builddir"
 "$srcdir/configure" \
diff --git a/.gitlab/ci/docker/hip5.5/Dockerfile b/.gitlab/ci/docker/hip5.5/Dockerfile
index 2deb3c6..3a4aa53 100644
--- a/.gitlab/ci/docker/hip5.5/Dockerfile
+++ b/.gitlab/ci/docker/hip5.5/Dockerfile
@@ -2,7 +2,13 @@
 
 ARG BASE_IMAGE=rocm/dev-ubuntu-22.04:5.5
 
-FROM ${BASE_IMAGE} AS apt-cache
+FROM ${BASE_IMAGE} AS cuda-keyring
+ADD https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb /root/
+RUN --mount=type=tmpfs,target=/var/log \
+    dpkg -i /root/cuda-keyring_1.1-1_all.deb \
+ && rm /root/cuda-keyring_1.1-1_all.deb
+
+FROM cuda-keyring 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.
@@ -12,9 +18,12 @@
     apt-get update \
  && apt-get --download-only -y install $(grep -h '^[^#]\+$' /root/*.lst)
 
-FROM ${BASE_IMAGE}
+FROM cuda-keyring
 MAINTAINER Brad King <brad.king@kitware.com>
 
+ENV NVIDIA_DRIVER_CAPABILITIES=compute,utility
+ENV NVIDIA_REQUIRE_CUDA=cuda>=11.8
+ENV NVIDIA_VISIBLE_DEVICES=all
 ENV PATH="/opt/rocm/bin:$PATH"
 
 RUN --mount=type=bind,source=install_deps.sh,target=/root/install_deps.sh \
diff --git a/.gitlab/ci/docker/hip5.5/deps_packages.lst b/.gitlab/ci/docker/hip5.5/deps_packages.lst
index 9847925..3276055 100644
--- a/.gitlab/ci/docker/hip5.5/deps_packages.lst
+++ b/.gitlab/ci/docker/hip5.5/deps_packages.lst
@@ -2,3 +2,20 @@
 g++
 curl
 git
+
+# NVIDIA CUDA Compiler
+cuda-keyring
+cuda-nvcc-11-8
+cuda-profiler-api-11-8
+
+# NVIDIA CUDA Toolkit
+# These are not needed for HIP, but having them in
+# the environment allows us to run CUDA tests too.
+cuda-nvrtc-dev-11-8
+cuda-nvtx-11-8
+libcublas-dev-11-8
+libcufft-dev-11-8
+libcurand-dev-11-8
+libcusolver-dev-11-8
+libcusparse-dev-11-8
+libnpp-dev-11-8
diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake
index b02ceb0..28a3e27 100644
--- a/.gitlab/ci/download_qt.cmake
+++ b/.gitlab/ci/download_qt.cmake
@@ -15,11 +15,11 @@
   set(qt_url_root "https://cmake.org/files/dependencies")
   set(qt_url_path "")
   if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_x86_64_package")
-    list(APPEND qt_files "qt-5.12.1-win-x86_64-msvc_v142-1.zip")
-    set(qt_subdir "qt-5.12.1-win-x86_64-msvc_v142-1")
+    list(APPEND qt_files "qt-5.15.10-win-x86_64-msvc_v142-1.zip")
+    set(qt_subdir "qt-5.15.10-win-x86_64-msvc_v142-1")
   elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_i386_package")
-    list(APPEND qt_files "qt-5.12.1-win-i386-msvc_v142-1.zip")
-    set(qt_subdir "qt-5.12.1-win-i386-msvc_v142-1")
+    list(APPEND qt_files "qt-5.15.10-win-i386-msvc_v142-1.zip")
+    set(qt_subdir "qt-5.15.10-win-i386-msvc_v142-1")
   elseif ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows_arm64_package")
     list(APPEND qt_files "qt-6.3.0-win-arm64-msvc_v143-1.zip")
     set(qt_subdir "qt-6.3.0-win-arm64-msvc_v143-1")
diff --git a/.gitlab/ci/download_qt_hashes.cmake b/.gitlab/ci/download_qt_hashes.cmake
index 4c48a47..1b9cf14 100644
--- a/.gitlab/ci/download_qt_hashes.cmake
+++ b/.gitlab/ci/download_qt_hashes.cmake
@@ -13,8 +13,8 @@
 set("qt-5.9.9-macosx10.10-x86_64-arm64.tar.xz_hash" d4449771afa0bc6a809c14f1e6d939e7732494cf059503ae451e2bfe8fc60cc1)
 set("qt-5.15.2-macosx10.13-x86_64-arm64.tar.xz_hash" 7b9463a01c8beeee5bf8d01c70deff2d08561cd20aaf6f7a2f41cf8b68ce8a6b)
 
-set("qt-5.12.1-win-i386-msvc_v142-1.zip_hash" aa78711fdaa5d9b146bf7ddcf15983f9fbb3f995462f2d043f8cca74b40ddd11)
-set("qt-5.12.1-win-x86_64-msvc_v142-1.zip_hash" c2fc068b9dac40bb420e28e1ee15ce4f2ccfc866d767f3b99b6bb435b7c4f44b)
+set("qt-5.15.10-win-i386-msvc_v142-1.zip_hash" c158cebc054d3f4f09733772a8a04789e2884912d45782e8c0c5e6a0b2773e92)
+set("qt-5.15.10-win-x86_64-msvc_v142-1.zip_hash" d55c017aef359f6aa8c592b18ba13cc120c749417b55671548970690126cd139)
 
 set("qt-6.3.0-win-arm64-msvc_v143-1.zip_hash" f794c035fd4ff9f04468e1787a60d93d7496119c0060c3173a76d24a6b551b14)
 set("qt-6.3.0-win-i386-msvc_v143-1.zip_hash" 972bc707f78d11b44f360643ca4d0c898e761f7add43b96117d958c70d84a443)
diff --git a/.gitlab/ci/env.sh b/.gitlab/ci/env.sh
index 7634f5d..aa709a8 100644
--- a/.gitlab/ci/env.sh
+++ b/.gitlab/ci/env.sh
@@ -9,6 +9,16 @@
   rm -f "$log"
 }
 
+if test -n "$CMAKE_CI_IN_SYMLINK_TREE"; then
+  mkdir -p "$CI_PROJECT_DIR/real_work/work/build"
+  ln -s real_work/work "$CI_PROJECT_DIR/work"
+  git worktree prune
+  git worktree add "$CI_PROJECT_DIR/work/cmake" HEAD
+
+  # Assert that the hash matches.
+  test "$(git -C "$CI_PROJECT_DIR/work/cmake" rev-parse HEAD)" = "$(git -C "$CI_PROJECT_DIR" rev-parse HEAD)"
+fi
+
 if test -r ".gitlab/ci/env_${CMAKE_CONFIGURATION}.sh"; then
   source ".gitlab/ci/env_${CMAKE_CONFIGURATION}.sh"
 fi
diff --git a/.gitlab/ci/env_cuda11.8_splayed_nvidia.sh b/.gitlab/ci/env_cuda11.8_splayed_nvidia.sh
new file mode 100644
index 0000000..38e788d
--- /dev/null
+++ b/.gitlab/ci/env_cuda11.8_splayed_nvidia.sh
@@ -0,0 +1,36 @@
+#
+# Splay the libraries and includes to emulate conda where
+# things are split between the host and build prefix
+#
+# /usr/local/cuda/include/crt/ -> /tmp/cuda/include/crt
+# /usr/local/cuda/lib64/stubs/ -> /tmp/cuda/stubs/
+# /usr/local/cuda/lib64/libcudart* -> /tmp/cuda/libs/
+#
+# Also reduce to minimal subset of libraries by removing
+# static libraries to emulate a minimal cuda install
+mkdir -p /tmp/cuda/libs
+mkdir -p /tmp/cuda/stubs
+mkdir -p /tmp/cuda/include
+
+mv /usr/local/cuda/lib64/libcuda* /tmp/cuda/libs
+mv /usr/local/cuda/lib64/stubs/ /tmp/cuda/stubs/
+mv /usr/local/cuda/include/crt/ /tmp/cuda/include/
+
+# patch the nvcc.profile to handle the splayed layout
+# which allows verification
+mv /usr/local/cuda/bin/nvcc.profile /usr/local/cuda/bin/nvcc.profile.orig
+echo "
+TOP              = \$(_HERE_)/..
+
+NVVMIR_LIBRARY_DIR = \$(TOP)/\$(_NVVM_BRANCH_)/libdevice
+
+LD_LIBRARY_PATH += \$(TOP)/lib:
+PATH            += \$(TOP)/\$(_NVVM_BRANCH_)/bin:\$(_HERE_):
+
+INCLUDES        +=  \"-I\$(TOP)/\$(_TARGET_DIR_)/include\" \$(_SPACE_) \"-I/tmp/cuda/include\" \$(_SPACE_)
+
+LIBRARIES        =+ \$(_SPACE_) \"-L\$(TOP)/\$(_TARGET_DIR_)/lib\$(_TARGET_SIZE_)\" \"-L/tmp/cuda/stubs/\" \"-L/tmp/cuda/libs\"
+
+CUDAFE_FLAGS    +=
+PTXAS_FLAGS     +=
+" > /usr/local/cuda/bin/nvcc.profile
diff --git a/.gitlab/ci/env_debian10_makefiles_clang.sh b/.gitlab/ci/env_debian10_makefiles_clang.sh
deleted file mode 100644
index e0d5d61..0000000
--- a/.gitlab/ci/env_debian10_makefiles_clang.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-export CC=/usr/bin/clang-7
-export CXX=/usr/bin/clang++-7
diff --git a/.gitlab/ci/env_debian10_ninja.sh b/.gitlab/ci/env_debian10_ninja.sh
deleted file mode 100644
index ba8ad47..0000000
--- a/.gitlab/ci/env_debian10_ninja.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-export MY_RUBY_HOME="/usr/local/rvm/rubies/ruby-2.7.0"
-
-if test -z "$CI_MERGE_REQUEST_ID"; then
-  curl -L -O "https://download.swift.org/swift-5.7.1-release/ubuntu1804/swift-5.7.1-RELEASE/swift-5.7.1-RELEASE-ubuntu18.04.tar.gz"
-  echo '2b30f9efc969d9e96f0836d0871130dffb369822a3823ee6f3db44c29c1698e3  swift-5.7.1-RELEASE-ubuntu18.04.tar.gz' > swift.sha256sum
-  sha256sum --check swift.sha256sum
-  mkdir /opt/swift
-  tar xzf swift-5.7.1-RELEASE-ubuntu18.04.tar.gz -C /opt/swift --strip-components=2
-  rm swift-5.7.1-RELEASE-ubuntu18.04.tar.gz swift.sha256sum
-  export SWIFTC="/opt/swift/bin/swiftc"
-fi
diff --git a/.gitlab/ci/env_debian10_ninja_clang.sh b/.gitlab/ci/env_debian10_ninja_clang.sh
deleted file mode 100644
index e0d5d61..0000000
--- a/.gitlab/ci/env_debian10_ninja_clang.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-export CC=/usr/bin/clang-7
-export CXX=/usr/bin/clang++-7
diff --git a/.gitlab/ci/env_debian10_aarch64_extdeps.sh b/.gitlab/ci/env_debian12_aarch64_extdeps.sh
similarity index 100%
rename from .gitlab/ci/env_debian10_aarch64_extdeps.sh
rename to .gitlab/ci/env_debian12_aarch64_extdeps.sh
diff --git a/.gitlab/ci/env_debian10_extdeps.sh b/.gitlab/ci/env_debian12_extdeps.sh
similarity index 100%
rename from .gitlab/ci/env_debian10_extdeps.sh
rename to .gitlab/ci/env_debian12_extdeps.sh
diff --git a/.gitlab/ci/env_debian12_hip_radeon.sh b/.gitlab/ci/env_debian12_hip_radeon.sh
new file mode 100644
index 0000000..793c985
--- /dev/null
+++ b/.gitlab/ci/env_debian12_hip_radeon.sh
@@ -0,0 +1,9 @@
+export HIPCXX=/usr/bin/clang++-15
+export HIPFLAGS='--rocm-path=/usr --rocm-device-lib-path=/usr/lib/x86_64-linux-gnu/amdgcn/bitcode'
+
+# FIXME(debian): Clang is supposed to automatically parse a HIP version file.
+# The ROCm installer places it at '$prefix/bin/.hipVersion', but the package
+# on Debian moves it to '$prefix/share/hip/version'.  llvm-toolchain package
+# version 15.0.7-4 has 'debian/patches/amdgpu/usr-search-paths.patch' for this,
+# but Debian 12 currently provides version 15.0.6-4 without the patch.
+export HIPFLAGS="$HIPFLAGS --hip-version=5.2"
diff --git a/.gitlab/ci/env_debian12_makefiles_clang.sh b/.gitlab/ci/env_debian12_makefiles_clang.sh
new file mode 100644
index 0000000..eda7c1f
--- /dev/null
+++ b/.gitlab/ci/env_debian12_makefiles_clang.sh
@@ -0,0 +1,2 @@
+export CC=/usr/bin/clang-15
+export CXX=/usr/bin/clang++-15
diff --git a/.gitlab/ci/env_debian12_ninja.sh b/.gitlab/ci/env_debian12_ninja.sh
new file mode 100644
index 0000000..2b8ff2a
--- /dev/null
+++ b/.gitlab/ci/env_debian12_ninja.sh
@@ -0,0 +1,5 @@
+export MY_RUBY_HOME="/usr/local/rvm/rubies/ruby-3.2.2"
+
+if test -z "$CI_MERGE_REQUEST_ID"; then
+  source .gitlab/ci/swift-env.sh
+fi
diff --git a/.gitlab/ci/env_debian12_ninja_clang.sh b/.gitlab/ci/env_debian12_ninja_clang.sh
new file mode 100644
index 0000000..eda7c1f
--- /dev/null
+++ b/.gitlab/ci/env_debian12_ninja_clang.sh
@@ -0,0 +1,2 @@
+export CC=/usr/bin/clang-15
+export CXX=/usr/bin/clang++-15
diff --git a/.gitlab/ci/env_debian12_ninja_multi_symlinked.sh b/.gitlab/ci/env_debian12_ninja_multi_symlinked.sh
new file mode 100644
index 0000000..2b8ff2a
--- /dev/null
+++ b/.gitlab/ci/env_debian12_ninja_multi_symlinked.sh
@@ -0,0 +1,5 @@
+export MY_RUBY_HOME="/usr/local/rvm/rubies/ruby-3.2.2"
+
+if test -z "$CI_MERGE_REQUEST_ID"; then
+  source .gitlab/ci/swift-env.sh
+fi
diff --git a/.gitlab/ci/env_fedora38_hip_radeon.sh b/.gitlab/ci/env_fedora38_hip_radeon.sh
new file mode 100644
index 0000000..812ef5a
--- /dev/null
+++ b/.gitlab/ci/env_fedora38_hip_radeon.sh
@@ -0,0 +1 @@
+export HIPCXX=/usr/bin/clang++-16
diff --git a/.gitlab/ci/env_fedora38_makefiles_symlinked.cmake b/.gitlab/ci/env_fedora38_makefiles_symlinked.cmake
new file mode 100644
index 0000000..2accabf
--- /dev/null
+++ b/.gitlab/ci/env_fedora38_makefiles_symlinked.cmake
@@ -0,0 +1 @@
+include("${CMAKE_CURRENT_LIST_DIR}/env_fedora38_makefiles.cmake")
diff --git a/.gitlab/ci/env_fedora38_makefiles_symlinked.sh b/.gitlab/ci/env_fedora38_makefiles_symlinked.sh
new file mode 100644
index 0000000..452d3785
--- /dev/null
+++ b/.gitlab/ci/env_fedora38_makefiles_symlinked.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_fedora38_makefiles.sh
diff --git a/.gitlab/ci/env_hip5.5_nvidia.sh b/.gitlab/ci/env_hip5.5_nvidia.sh
new file mode 100644
index 0000000..67d1ef2
--- /dev/null
+++ b/.gitlab/ci/env_hip5.5_nvidia.sh
@@ -0,0 +1,4 @@
+export HIP_PLATFORM=nvidia
+export CUDA_PATH=/usr/local/cuda-11.8
+export PATH=/usr/local/cuda-11.8/bin:$PATH
+export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64
diff --git a/.gitlab/ci/env_macos_arm64_ninja_symlinked.sh b/.gitlab/ci/env_macos_arm64_ninja_symlinked.sh
new file mode 100644
index 0000000..b78cefc
--- /dev/null
+++ b/.gitlab/ci/env_macos_arm64_ninja_symlinked.sh
@@ -0,0 +1 @@
+. .gitlab/ci/env_macos_arm64_ninja.sh
diff --git a/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1 b/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1
index e2d573e..f6e1f0a 100755
--- a/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1
+++ b/.gitlab/ci/env_mingw_osdn_io_mingw_makefiles.ps1
@@ -1,3 +1 @@
-$pwdpath = $pwd.Path
-& "$pwsh" -File ".gitlab/ci/mingw.ps1"
-Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\mingw\bin;$env:PATH"
+. .gitlab/ci/mingw-env.ps1
diff --git a/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1 b/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1
index 6eccb72..f6e1f0a 100755
--- a/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1
+++ b/.gitlab/ci/env_mingw_osdn_io_msys_makefiles.ps1
@@ -1,5 +1 @@
-$pwdpath = $pwd.Path
-& "$pwsh" -File ".gitlab/ci/mingw.ps1"
-Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\mingw\msys\1.0\bin;$pwdpath\.gitlab\mingw\bin;$env:PATH"
-$env:MSYSTEM = 'MINGW32'
-$env:MAKE_MODE = 'unix'
+. .gitlab/ci/mingw-env.ps1
diff --git a/.gitlab/ci/env_windows_orangec6.73.1.ps1 b/.gitlab/ci/env_windows_orangec6.73.1.ps1
new file mode 100755
index 0000000..96e36a1
--- /dev/null
+++ b/.gitlab/ci/env_windows_orangec6.73.1.ps1
@@ -0,0 +1,2 @@
+. .gitlab/ci/ninja-env.ps1
+. .gitlab/ci/orangec-env.ps1
diff --git a/.gitlab/ci/env_windows_vs2022_x64.ps1 b/.gitlab/ci/env_windows_vs2022_x64.ps1
new file mode 100755
index 0000000..42aec11
--- /dev/null
+++ b/.gitlab/ci/env_windows_vs2022_x64.ps1
@@ -0,0 +1,4 @@
+if ("$env:CMAKE_CI_NIGHTLY" -eq "true") {
+  $cmake = "build\install\bin\cmake"
+  . ".gitlab/ci/qt-env.ps1"
+}
diff --git a/.gitlab/ci/gitlab_ci.cmake b/.gitlab/ci/gitlab_ci.cmake
index 080c93b..d0d4a59 100644
--- a/.gitlab/ci/gitlab_ci.cmake
+++ b/.gitlab/ci/gitlab_ci.cmake
@@ -5,8 +5,13 @@
 
 # Set up the source and build paths.
 set(CTEST_SOURCE_DIRECTORY "$ENV{CI_PROJECT_DIR}")
+if("$ENV{CMAKE_CI_IN_SYMLINK_TREE}")
+  set(CTEST_SOURCE_DIRECTORY "$ENV{CI_PROJECT_DIR}/work/cmake")
+endif()
 if("$ENV{CMAKE_CI_INPLACE}")
   set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}")
+elseif("$ENV{CMAKE_CI_IN_SYMLINK_TREE}")
+  set(CTEST_BINARY_DIRECTORY "$ENV{CI_PROJECT_DIR}/work/build")
 else()
   set(CTEST_BINARY_DIRECTORY "${CTEST_SOURCE_DIRECTORY}/build")
 endif()
diff --git a/.gitlab/ci/ispc.sh b/.gitlab/ci/ispc.sh
index 59ee200..c1e3793 100755
--- a/.gitlab/ci/ispc.sh
+++ b/.gitlab/ci/ispc.sh
@@ -12,12 +12,12 @@
         ;;
     Darwin-arm64)
         shatool="shasum -a 256"
-        sha256sum="62cee043a3a4dbff8c2f6d3885a7e573901bbc1325dd93d50f92904b7ea67fec"
+        sha256sum="c423a5a88d7a9a6ed667e41d025801c123fa0c5fd384d4ea138fa1fcf2bc24c9"
         platform="macOS.arm64"
         ;;
     Darwin-x86_64)
         shatool="shasum -a 256"
-        sha256sum="da0f11a048a316081a8ad8170d48b170b2ed7efc3b140fc88b8611238809c8e4"
+        sha256sum="e25222d2d6f4f8e3561556ac73f88721ceb5486439d6c2a566d37407ad9a5907"
         platform="macOS.x86_64"
         ;;
     *)
diff --git a/.gitlab/ci/mingw-env.ps1 b/.gitlab/ci/mingw-env.ps1
new file mode 100755
index 0000000..d68a7f7
--- /dev/null
+++ b/.gitlab/ci/mingw-env.ps1
@@ -0,0 +1,5 @@
+$pwdpath = $pwd.Path
+& "$pwsh" -File ".gitlab/ci/mingw.ps1"
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\mingw\bin;$pwdpath\.gitlab\mingw\msys\1.0\bin;$env:PATH"
+$env:MSYSTEM = 'MINGW32'
+$env:MAKE_MODE = 'unix'
diff --git a/.gitlab/ci/ninja-nightly.sh b/.gitlab/ci/ninja-nightly.sh
index b78b64e..739d1d5 100755
--- a/.gitlab/ci/ninja-nightly.sh
+++ b/.gitlab/ci/ninja-nightly.sh
@@ -5,7 +5,7 @@
 cd .gitlab
 
 git clone https://github.com/ninja-build/ninja.git ninja-src
-cmake -S ninja-src -B ninja-src/build -DCMAKE_BUILD_TYPE=Release
+cmake -S ninja-src -B ninja-src/build -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release
 cmake --build ninja-src/build --parallel --target ninja
 mv ninja-src/build/ninja .
 rm -rf ninja-src
diff --git a/.gitlab/ci/orangec-env.ps1 b/.gitlab/ci/orangec-env.ps1
new file mode 100755
index 0000000..3a5d232
--- /dev/null
+++ b/.gitlab/ci/orangec-env.ps1
@@ -0,0 +1,8 @@
+Invoke-Expression -Command .gitlab/ci/orangec.ps1
+$pwdpath = $pwd.Path
+Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\orangec\bin;$env:PATH"
+Set-Item -Force -Path "env:ORANGEC" -Value "$pwdpath\.gitlab\orangec"
+
+$env:CC  = "occ"
+$env:CXX = "occ"
+occ --version
diff --git a/.gitlab/ci/orangec.ps1 b/.gitlab/ci/orangec.ps1
new file mode 100755
index 0000000..2201e12
--- /dev/null
+++ b/.gitlab/ci/orangec.ps1
@@ -0,0 +1,24 @@
+$erroractionpreference = "stop"
+
+if ("$env:CMAKE_CONFIGURATION".Contains("orangec6.73.1")) {
+    # OrangeC 6.73.1
+    $archive = "ZippedBinaries6738.zip"
+    $release = "Orange-C-v6.73.1"
+    $sha256sum = "29BC506AB105B2BF1002129C37826B2153DF1C8D0F22B9A2C38ACA3FB72A5B89"
+} else {
+    throw ('unknown CMAKE_CONFIGURATION: ' + "$env:CMAKE_CONFIGURATION")
+}
+
+$outdir = $pwd.Path
+$outdir = "$outdir\.gitlab"
+$ProgressPreference = 'SilentlyContinue'
+Invoke-WebRequest -Uri "https://github.com/LADSoft/OrangeC/releases/download/$release/$archive" -OutFile "$outdir\$archive"
+$hash = Get-FileHash "$outdir\$archive" -Algorithm SHA256
+if ($hash.Hash -ne $sha256sum) {
+    exit 1
+}
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+[System.IO.Compression.ZipFile]::ExtractToDirectory("$outdir\$archive", "$outdir")
+# The archive contains directory 'orangec', placed at '$outdir\orangec'.
+Remove-Item "$outdir\$archive"
diff --git a/.gitlab/ci/qt-env.ps1 b/.gitlab/ci/qt-env.ps1
index 7eff55f..22b1099 100755
--- a/.gitlab/ci/qt-env.ps1
+++ b/.gitlab/ci/qt-env.ps1
@@ -1,6 +1,9 @@
+if ($cmake -eq $null) {
+    throw ('$cmake powershell variable not set ')
+}
 if ("$env:PROCESSOR_ARCHITECTURE" -eq "AMD64") {
     $pwdpath = $pwd.Path
-    cmake -P .gitlab/ci/download_qt.cmake
+    & $cmake -P .gitlab/ci/download_qt.cmake
     Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\qt\bin;$env:PATH"
     qmake -v
 } elseif ("$env:PROCESSOR_ARCHITECTURE" -eq "ARM64") {
diff --git a/.gitlab/ci/swift-env.sh b/.gitlab/ci/swift-env.sh
new file mode 100644
index 0000000..fa0c81e
--- /dev/null
+++ b/.gitlab/ci/swift-env.sh
@@ -0,0 +1,7 @@
+curl -L -O "https://download.swift.org/swift-5.7.1-release/ubuntu1804/swift-5.7.1-RELEASE/swift-5.7.1-RELEASE-ubuntu18.04.tar.gz"
+echo '2b30f9efc969d9e96f0836d0871130dffb369822a3823ee6f3db44c29c1698e3  swift-5.7.1-RELEASE-ubuntu18.04.tar.gz' > swift.sha256sum
+sha256sum --check swift.sha256sum
+mkdir /opt/swift
+tar xzf swift-5.7.1-RELEASE-ubuntu18.04.tar.gz -C /opt/swift --strip-components=2
+rm swift-5.7.1-RELEASE-ubuntu18.04.tar.gz swift.sha256sum
+export SWIFTC="/opt/swift/bin/swiftc"
diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml
index 451a8e2..8894057 100644
--- a/.gitlab/os-linux.yml
+++ b/.gitlab/os-linux.yml
@@ -11,11 +11,11 @@
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
 
 .linux_release_x86_64:
-    image: "kitware/cmake:build-linux-x86_64-deps-2020-04-02@sha256:77e9ab183f34680990db9da5945473e288f0d6556bce79ecc1589670d656e157"
+    image: "kitware/cmake:build-linux-x86_64-deps-2023-08-16@sha256:aa0ebdbd90a51cc83d31f393c5c48ec4599a28f7ccdc288558522c6265b24fae"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
-        LAUNCHER: "scl enable devtoolset-6 rh-python36 --"
+        LAUNCHER: "scl enable devtoolset-7 --"
         CMAKE_ARCH: x86_64
 
 .linux_release_aarch64:
@@ -30,11 +30,11 @@
     variables:
         BOOTSTRAP_ARGS: '-- "-DCMake_DOC_ARTIFACT_PREFIX=$CI_PROJECT_DIR/build/install-doc"'
 
-.needs_centos6_x86_64:
+.needs_centos7_x86_64:
     dependencies:
-        - b:centos6-x86_64
+        - b:centos7-x86_64
     needs:
-        - b:centos6-x86_64
+        - b:centos7-x86_64
 
 .needs_centos7_aarch64:
     dependencies:
@@ -44,23 +44,23 @@
 
 ### Debian
 
-.debian10:
-    image: "kitware/cmake:ci-debian10-x86_64-2023-03-29"
+.debian12:
+    image: "kitware/cmake:ci-debian12-x86_64-2023-07-27"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
         CMAKE_ARCH: x86_64
 
-.debian10_iwyu:
-    extends: .debian10
+.debian12_iwyu:
+    extends: .debian12
 
     variables:
-        CMAKE_CONFIGURATION: debian10_iwyu
+        CMAKE_CONFIGURATION: debian12_iwyu
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_CI_NO_INSTALL: 1
 
-.debian10_aarch64:
-    image: "kitware/cmake:ci-debian10-aarch64-2023-03-29"
+.debian12_aarch64:
+    image: "kitware/cmake:ci-debian12-aarch64-2023-07-27"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -69,7 +69,7 @@
 ### Fedora
 
 .fedora38:
-    image: "kitware/cmake:ci-fedora38-x86_64-2023-05-22"
+    image: "kitware/cmake:ci-fedora38-x86_64-2023-08-07"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes"
@@ -112,44 +112,54 @@
 
 #### Build and test
 
-.debian10_ninja:
-    extends: .debian10
+.debian12_ninja:
+    extends: .debian12
 
     variables:
-        CMAKE_CONFIGURATION: debian10_ninja
+        CMAKE_CONFIGURATION: debian12_ninja
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.debian10_aarch64_ninja:
-    extends: .debian10_aarch64
+.debian12_aarch64_ninja:
+    extends: .debian12_aarch64
 
     variables:
-        CMAKE_CONFIGURATION: debian10_aarch64_ninja
+        CMAKE_CONFIGURATION: debian12_aarch64_ninja
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.debian10_makefiles_inplace:
-    extends: .debian10
+.debian12_makefiles_inplace:
+    extends: .debian12
 
     variables:
-        CMAKE_CONFIGURATION: debian10_makefiles_inplace
+        CMAKE_CONFIGURATION: debian12_makefiles_inplace
         CMAKE_GENERATOR: "Unix Makefiles"
         CMAKE_CI_BOOTSTRAP: 1
         CMAKE_CI_INPLACE: 1
         CMAKE_CI_NO_INSTALL: 1
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.debian10_extdeps:
-    extends: .debian10
+.debian12_ninja_multi_symlinked:
+    extends: .debian12
 
     variables:
-        CMAKE_CONFIGURATION: debian10_extdeps
+        CMAKE_CONFIGURATION: debian12_ninja_multi_symlinked
+        CMAKE_GENERATOR: "Ninja Multi-Config"
+        CTEST_NO_WARNINGS_ALLOWED: 1
+        CMAKE_CI_IN_SYMLINK_TREE: 1
+        CMAKE_CI_BUILD_DIR: "real_work/work/build"
+
+.debian12_extdeps:
+    extends: .debian12
+
+    variables:
+        CMAKE_CONFIGURATION: debian12_extdeps
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
-.debian10_aarch64_extdeps:
-    extends: .debian10_aarch64
+.debian12_aarch64_extdeps:
+    extends: .debian12_aarch64
 
     variables:
-        CMAKE_CONFIGURATION: debian10_aarch64_extdeps
+        CMAKE_CONFIGURATION: debian12_aarch64_extdeps
         CMAKE_CI_BUILD_TYPE: Release
         CTEST_NO_WARNINGS_ALLOWED: 1
 
@@ -185,20 +195,30 @@
         CTEST_NO_WARNINGS_ALLOWED: 1
         CMAKE_GENERATOR: "Unix Makefiles"
 
+.fedora38_makefiles_symlinked:
+    extends: .fedora38
+
+    variables:
+        CMAKE_CONFIGURATION: fedora38_makefiles_symlinked
+        CTEST_NO_WARNINGS_ALLOWED: 1
+        CMAKE_GENERATOR: "Unix Makefiles"
+        CMAKE_CI_IN_SYMLINK_TREE: 1
+        CMAKE_CI_BUILD_DIR: "real_work/work/build"
+
 ### Clang Compiler
 
-.debian10_makefiles_clang:
-    extends: .debian10
+.debian12_makefiles_clang:
+    extends: .debian12
 
     variables:
-        CMAKE_CONFIGURATION: debian10_makefiles_clang
+        CMAKE_CONFIGURATION: debian12_makefiles_clang
         CMAKE_GENERATOR: "Unix Makefiles"
 
-.debian10_ninja_clang:
-    extends: .debian10
+.debian12_ninja_clang:
+    extends: .debian12
 
     variables:
-        CMAKE_CONFIGURATION: debian10_ninja_clang
+        CMAKE_CONFIGURATION: debian12_ninja_clang
 
 .fedora38_makefiles_clang:
     extends: .fedora38
@@ -342,10 +362,16 @@
         CMAKE_CONFIGURATION: cuda11.8_minimal_nvidia
         CTEST_NO_WARNINGS_ALLOWED: 1
 
+.cuda11.8_splayed_nvidia:
+    extends: .cuda11.8_minimal
+    variables:
+        CMAKE_CONFIGURATION: cuda11.8_splayed_nvidia
+        CTEST_NO_WARNINGS_ALLOWED: 1
+
 ### HIP builds
 
 .hip5.5:
-    image: "kitware/cmake:ci-hip5.5-x86_64-2023-06-01"
+    image: "kitware/cmake:ci-hip5.5-x86_64-2023-09-18"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -359,10 +385,31 @@
         CMAKE_CONFIGURATION: hip5.5_radeon
         CMAKE_GENERATOR: "Ninja Multi-Config"
 
+.debian12_hip_radeon:
+    extends: .debian12
+
+    variables:
+        CMAKE_CONFIGURATION: debian12_hip_radeon
+        CTEST_LABELS: "HIP"
+
+.fedora38_hip_radeon:
+    extends: .fedora38
+
+    variables:
+        CMAKE_CONFIGURATION: fedora38_hip_radeon
+        CTEST_LABELS: "HIP"
+
+.hip5.5_nvidia:
+    extends: .hip5.5
+
+    variables:
+        CMAKE_CONFIGURATION: hip5.5_nvidia
+        CTEST_LABELS: "HIP"
+
 ### C++ modules
 
 .gcc_cxx_modules_x86_64:
-    image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2022-06-21"
+    image: "kitware/cmake:ci-gcc_cxx_modules-x86_64-2023-09-21"
 
     variables:
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
@@ -383,6 +430,22 @@
         CMAKE_CONFIGURATION: linux_gcc_cxx_modules_ninja_multi
         CMAKE_GENERATOR: "Ninja Multi-Config"
 
+### Debian 10 legacy packages
+
+.debian10:
+    image: "kitware/cmake:ci-debian10-x86_64-2023-07-31"
+
+    variables:
+        GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci"
+        CMAKE_ARCH: x86_64
+
+.debian10_legacy:
+    extends: .debian10
+
+    variables:
+        CMAKE_CONFIGURATION: debian10_legacy
+        CTEST_LABELS: "Python2"
+
 ## Tags
 
 .linux_x86_64_tags:
@@ -588,7 +651,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_manually
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
@@ -598,7 +661,7 @@
         - .cmake_test_linux_release
         - .linux_x86_64_tags
         - .run_manually
-        - .needs_centos6_x86_64
+        - .needs_centos7_x86_64
     variables:
         CMAKE_CI_JOB_NIGHTLY: "true"
 
diff --git a/.gitlab/os-macos.yml b/.gitlab/os-macos.yml
index 09d7598..b3165fa 100644
--- a/.gitlab/os-macos.yml
+++ b/.gitlab/os-macos.yml
@@ -7,7 +7,7 @@
         GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci ext/$CI_CONCURRENT_ID"
         # TODO: Factor this out so that each job selects the Xcode version to
         # use so that different versions can be tested in a single pipeline.
-        DEVELOPER_DIR: "/Applications/Xcode-14.3.app/Contents/Developer"
+        DEVELOPER_DIR: "/Applications/Xcode-15.0.app/Contents/Developer"
         # Avoid conflicting with other projects running on the same machine.
         SCCACHE_SERVER_PORT: 4227
 
@@ -38,6 +38,21 @@
         CMAKE_CONFIGURATION: macos_arm64_ninja
         CTEST_NO_WARNINGS_ALLOWED: 1
 
+.macos_arm64_ninja_symlinked:
+    extends: .macos_build
+
+    variables:
+        CMAKE_CONFIGURATION: macos_arm64_ninja_symlinked
+        CTEST_NO_WARNINGS_ALLOWED: 1
+        CMAKE_CI_IN_SYMLINK_TREE: 1
+        CMAKE_CI_BUILD_DIR: "real_work/work/build"
+
+.macos_arm64_pch:
+    extends: .macos_arm64_ninja
+
+    variables:
+        CMAKE_CONFIGURATION: macos_arm64_pch
+
 .macos_x86_64_makefiles:
     extends: .macos_build
 
@@ -80,6 +95,16 @@
         CMAKE_GENERATOR: Xcode
         CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
 
+.macos_arm64_xcode_symlinked:
+    extends: .macos
+
+    variables:
+        CMAKE_CONFIGURATION: macos_arm64_xcode_symlinked
+        CMAKE_GENERATOR: Xcode
+        CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
+        CMAKE_CI_IN_SYMLINK_TREE: 1
+        CMAKE_CI_BUILD_DIR: "real_work/work/build"
+
 .macos_arm64_xcode_ub:
     extends: .macos
 
@@ -110,7 +135,7 @@
         - cmake # Since this is a bare runner, pin to a project.
         - macos-x86_64
         - shell
-        - xcode-14.3
+        - xcode-15.0
         - nonconcurrent
 
 .macos_x86_64_tags_ext:
@@ -118,7 +143,7 @@
         - cmake # Since this is a bare runner, pin to a project.
         - macos-x86_64
         - shell
-        - xcode-14.3
+        - xcode-15.0
         - concurrent
 
 .macos_arm64_tags:
@@ -126,7 +151,7 @@
         - cmake # Since this is a bare runner, pin to a project.
         - macos-arm64
         - shell
-        - xcode-14.3
+        - xcode-15.0
         - nonconcurrent
 
 .macos_arm64_tags_ext:
@@ -134,7 +159,7 @@
         - cmake # Since this is a bare runner, pin to a project.
         - macos-arm64
         - shell
-        - xcode-14.3
+        - xcode-15.0
         - concurrent
 
 .macos_arm64_tags_package:
@@ -142,7 +167,7 @@
         - cmake # Since this is a bare runner, pin to a project.
         - macos-arm64
         - shell
-        - xcode-14.3
+        - xcode-15.0
         - nonconcurrent
         - finder
 
diff --git a/.gitlab/os-windows.yml b/.gitlab/os-windows.yml
index 026f2f4..c449ab8 100644
--- a/.gitlab/os-windows.yml
+++ b/.gitlab/os-windows.yml
@@ -55,6 +55,13 @@
         VCVARSPLATFORM: "arm64"
         VCVARSVERSION: "14.36.32532"
 
+.windows_vs2022_x64_pch:
+    extends:
+        - .windows_vs2022_x64_ninja
+
+    variables:
+        CMAKE_CONFIGURATION: windows_vs2022_x64_pch
+
 .windows_vs2022_x64_ninja:
     extends:
         - .windows_build_ninja
@@ -254,6 +261,20 @@
     variables:
         CMAKE_CONFIGURATION: windows_openwatcom1.9
 
+.windows_orangec:
+    extends: .windows
+
+    variables:
+        CMAKE_GENERATOR: "Ninja"
+        CMAKE_CI_BUILD_TYPE: Release
+        CMAKE_CI_NIGHTLY_IGNORE_DEPS: "true"
+
+.windows_orangec6.73.1:
+    extends: .windows_orangec
+
+    variables:
+        CMAKE_CONFIGURATION: windows_orangec6.73.1
+
 .windows_arm64_vs2022:
     extends: .windows
 
@@ -359,6 +380,7 @@
     - Set-Item -Force -Path "env:WIX" -Value "$pwdpath\.gitlab\wix"
     - (& "$pwsh" -File ".gitlab/ci/cmake.ps1")
     - Set-Item -Force -Path "env:PATH" -Value "$pwdpath\.gitlab\cmake\bin;$env:PATH"
+    - $cmake = "cmake"
     - . .gitlab/ci/ninja-env.ps1
     - (& "$env:WIX\bin\light.exe" -help) | Select -First 1
     - cmake --version
diff --git a/Auxiliary/vim/cmake.vim.in b/Auxiliary/vim/cmake.vim.in
index 6edc040..39d7193 100644
--- a/Auxiliary/vim/cmake.vim.in
+++ b/Auxiliary/vim/cmake.vim.in
@@ -22,20 +22,20 @@
 syn region cmakeBracketArgument start="\[\z(=\?\|=[0-9]*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
 
 syn region cmakeComment start="#" end="$" contains=cmakeTodo,@Spell
-syn region cmakeBracketComment start="#\[\z(=\?\|=[0-9]*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
+syn region cmakeBracketComment start="\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
 
 syn match cmakeEscaped /\(\\\\\|\\"\|\\n\|\\t\)/ contained
 syn region cmakeRegistry start="\[" end="]" contained oneline contains=cmakeTodo,cmakeEscaped
 
 syn region cmakeGeneratorExpression start="$<" end=">" contained oneline contains=cmakeVariableValue,cmakeProperty,cmakeGeneratorExpressions,cmakeTodo
 
-syn region cmakeString start='"' end='"' contained contains=cmakeTodo,cmakeVariableValue,cmakeEscaped
+syn region cmakeString start='"' end='"' contained contains=cmakeTodo,cmakeVariableValue,cmakeEscaped,@Spell
 
 syn region cmakeVariableValue start="${" end="}" contained oneline contains=cmakeVariable,cmakeTodo,cmakeVariableValue
 
 syn region cmakeEnvironment start="$ENV{" end="}" contained oneline contains=cmakeTodo
 
-syn region cmakeArguments start="(" end=")" contains=ALLBUT,cmakeGeneratorExpressions,cmakeCommand,cmakeCommandConditional,cmakeCommandRepeat,cmakeCommandDeprecated,cmakeCommandManuallyAdded,cmakeArguments,cmakeTodo
+syn region cmakeArguments start="(" end=")" contains=ALLBUT,cmakeGeneratorExpressions,cmakeCommand,cmakeCommandConditional,cmakeCommandRepeat,cmakeCommandDeprecated,cmakeCommandManuallyAdded,cmakeArguments,cmakeTodo,@Spell
 
 syn case match
 
diff --git a/Auxiliary/vim/extract-upper-case.pl b/Auxiliary/vim/extract-upper-case.pl
index 1179199..7f40b74 100755
--- a/Auxiliary/vim/extract-upper-case.pl
+++ b/Auxiliary/vim/extract-upper-case.pl
@@ -16,10 +16,10 @@
 my %keywords; # command => keyword-list
 
 # find cmake/Modules/ | sed -rn 's/.*CMakeDetermine(.+)Compiler.cmake/\1/p' | sort
-my @languages = qw(ASM ASM_MASM ASM_NASM C CSharp CUDA CXX Fortran Java RC Swift);
+my @languages = qw(ASM ASM_MASM ASM_NASM C CSharp CUDA CXX Fortran Java RC Swift HIP);
 
 # unwanted upper-cases
-my %unwanted = map { $_ => 1 } qw(VS CXX IDE NOTFOUND NO_ DFOO DBAR NEW);
+my %unwanted = map { $_ => 1 } qw(VS CXX IDE NOTFOUND NO_ DFOO DBAR NEW GNU);
 	# cannot remove ALL - exists for add_custom_command
 
 # control-statements
@@ -30,7 +30,7 @@
 my %deprecated = map { $_ => 1 } qw(build_name exec_program export_library_dependencies install_files install_programs install_targets link_libraries make_directory output_required_files remove subdir_depends subdirs use_mangled_mesa utility_source variable_requires write_file);
 
 # add some (popular) modules
-push @modules, "ExternalProject";
+push @modules, "ExternalProject", "FetchContent";
 
 # variables
 open(CMAKE, "$cmake --help-variable-list|") or die "could not run cmake";
diff --git a/Auxiliary/vim/syntax/cmake.vim b/Auxiliary/vim/syntax/cmake.vim
index 83f13d8..4bbdc65 100644
--- a/Auxiliary/vim/syntax/cmake.vim
+++ b/Auxiliary/vim/syntax/cmake.vim
@@ -1,13 +1,13 @@
 " Vim syntax file
 " Program:      CMake - Cross-Platform Makefile Generator
-" Version:      cmake version 3.19.20201028-gdab947f
+" Version:      cmake version 3.27.20230713-gdc88dd5
 " Language:     CMake
 " Author:       Andy Cedilnik <andy.cedilnik@kitware.com>,
 "               Nicholas Hutchinson <nshutchinson@gmail.com>,
 "               Patrick Boettcher <patrick.boettcher@posteo.de>
 " Maintainer:   Dimitri Merejkowsky <d.merej@gmail.com>
 " Former Maintainer: Karthik Krishnan <karthik.krishnan@kitware.com>
-" Last Change:  2020 oct. 28
+" Last Change:  2023 Jul 13
 "
 " License:      The CMake license applies to this file. See
 "               https://cmake.org/licensing
@@ -22,20 +22,20 @@
 syn region cmakeBracketArgument start="\[\z(=\?\|=[0-9]*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
 
 syn region cmakeComment start="#" end="$" contains=cmakeTodo,@Spell
-syn region cmakeBracketComment start="#\[\z(=\?\|=[0-9]*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
+syn region cmakeBracketComment start="\[\z(=*\)\[" end="\]\z1\]" contains=cmakeTodo,@Spell
 
 syn match cmakeEscaped /\(\\\\\|\\"\|\\n\|\\t\)/ contained
 syn region cmakeRegistry start="\[" end="]" contained oneline contains=cmakeTodo,cmakeEscaped
 
 syn region cmakeGeneratorExpression start="$<" end=">" contained oneline contains=cmakeVariableValue,cmakeProperty,cmakeGeneratorExpressions,cmakeTodo
 
-syn region cmakeString start='"' end='"' contained contains=cmakeTodo,cmakeVariableValue,cmakeEscaped
+syn region cmakeString start='"' end='"' contained contains=cmakeTodo,cmakeVariableValue,cmakeEscaped,@Spell
 
 syn region cmakeVariableValue start="${" end="}" contained oneline contains=cmakeVariable,cmakeTodo,cmakeVariableValue
 
 syn region cmakeEnvironment start="$ENV{" end="}" contained oneline contains=cmakeTodo
 
-syn region cmakeArguments start="(" end=")" contains=ALLBUT,cmakeGeneratorExpressions,cmakeCommand,cmakeCommandConditional,cmakeCommandRepeat,cmakeCommandDeprecated,cmakeCommandManuallyAdded,cmakeArguments,cmakeTodo
+syn region cmakeArguments start="(" end=")" contains=ALLBUT,cmakeGeneratorExpressions,cmakeCommand,cmakeCommandConditional,cmakeCommandRepeat,cmakeCommandDeprecated,cmakeCommandManuallyAdded,cmakeArguments,cmakeTodo,@Spell
 
 syn case match
 
@@ -76,6 +76,7 @@
             \ AUTOGEN_USE_SYSTEM_INCLUDE
             \ AUTOGEN_TARGETS_FOLDER
             \ AUTOGEN_TARGET_DEPENDS
+            \ AUTOGEN_USE_SYSTEM_INCLUDE
             \ AUTOMOC
             \ AUTOMOC_COMPILER_PREDEFINES
             \ AUTOMOC_DEPEND_FILTERS
@@ -120,6 +121,7 @@
             \ COMPILE_OPTIONS
             \ COMPILE_PDB_NAME
             \ COMPILE_PDB_OUTPUT_DIRECTORY
+            \ COMPILE_WARNING_AS_ERROR
             \ COST
             \ CPACK_DESKTOP_SHORTCUTS
             \ CPACK_NEVER_OVERWRITE
@@ -140,6 +142,10 @@
             \ CUDA_STANDARD
             \ CUDA_STANDARD_REQUIRED
             \ CXX_EXTENSIONS
+            \ CXX_MODULE_DIRS
+            \ CXX_MODULE_SET
+            \ CXX_MODULE_SETS
+            \ CXX_SCAN_FOR_MODULES
             \ CXX_STANDARD
             \ CXX_STANDARD_REQUIRED
             \ C_EXTENSIONS
@@ -156,6 +162,7 @@
             \ DISABLED
             \ DISABLED_FEATURES
             \ DISABLE_PRECOMPILE_HEADERS
+            \ DLL_NAME_WITH_SOVERSION
             \ DOTNET_SDK
             \ DOTNET_TARGET_FRAMEWORK
             \ DOTNET_TARGET_FRAMEWORK_VERSION
@@ -168,7 +175,9 @@
             \ ENVIRONMENT_MODIFICATION
             \ EXCLUDE_FROM_ALL
             \ EXCLUDE_FROM_DEFAULT_BUILD
+            \ EXPORT_COMPILE_COMMANDS
             \ EXPORT_NAME
+            \ EXPORT_NO_SYSTEM
             \ EXPORT_PROPERTIES
             \ EXTERNAL_OBJECT
             \ EchoString
@@ -183,6 +192,7 @@
             \ FOLDER
             \ FRAMEWORK
             \ FRAMEWORK_VERSION
+            \ Fortran_BUILDING_INSTRINSIC_MODULES
             \ Fortran_FORMAT
             \ Fortran_MODULE_DIRECTORY
             \ Fortran_PREPROCESS
@@ -195,7 +205,10 @@
             \ GLOBAL_DEPENDS_NO_CYCLES
             \ GNUtoMS
             \ HAS_CXX
+            \ HEADER_DIRS
             \ HEADER_FILE_ONLY
+            \ HEADER_SET
+            \ HEADER_SETS
             \ HELPSTRING
             \ HIP_ARCHITECTURES
             \ HIP_EXTENSIONS
@@ -214,8 +227,10 @@
             \ IMPORTED_LINK_INTERFACE_MULTIPLICITY
             \ IMPORTED_LOCATION
             \ IMPORTED_NO_SONAME
+            \ IMPORTED_NO_SYSTEM
             \ IMPORTED_OBJECTS
             \ IMPORTED_SONAME
+            \ IMPORTED_TARGETS
             \ IMPORT_PREFIX
             \ IMPORT_SUFFIX
             \ INCLUDE_DIRECTORIES
@@ -224,15 +239,21 @@
             \ INSTALL_REMOVE_ENVIRONMENT_RPATH
             \ INSTALL_RPATH
             \ INSTALL_RPATH_USE_LINK_PATH
+            \ INTERFACE_AUTOMOC_MACRO_NAMES
             \ INTERFACE_AUTOUIC_OPTIONS
             \ INTERFACE_AUTOMOC_MACRO_NAMES
             \ INTERFACE_COMPILE_DEFINITIONS
             \ INTERFACE_COMPILE_FEATURES
             \ INTERFACE_COMPILE_OPTIONS
+            \ INTERFACE_CXX_MODULE_SETS
+            \ INTERFACE_HEADER_SETS
+            \ INTERFACE_HEADER_SETS_TO_VERIFY
             \ INTERFACE_INCLUDE_DIRECTORIES
             \ INTERFACE_LINK_DEPENDS
             \ INTERFACE_LINK_DIRECTORIES
             \ INTERFACE_LINK_LIBRARIES
+            \ INTERFACE_LINK_LIBRARIES_DIRECT
+            \ INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE
             \ INTERFACE_LINK_OPTIONS
             \ INTERFACE_POSITION_INDEPENDENT_CODE
             \ INTERFACE_PRECOMPILE_HEADERS
@@ -242,6 +263,7 @@
             \ IN_TRY_COMPILE
             \ IOS_INSTALL_COMBINED
             \ ISPC_HEADER_DIRECTORY
+            \ ISPC_HEADER_SUFFIX
             \ ISPC_INSTRUCTION_SETS
             \ JOB_POOLS
             \ JOB_POOL_COMPILE
@@ -260,6 +282,8 @@
             \ LINK_INTERFACE_LIBRARIES
             \ LINK_INTERFACE_MULTIPLICITY
             \ LINK_LIBRARIES
+            \ LINK_LIBRARIES_ONLY_TARGETS
+            \ LINK_LIBRARY_OVERRIDE
             \ LINK_OPTIONS
             \ LINK_SEARCH_END_STATIC
             \ LINK_SEARCH_START_STATIC
@@ -277,6 +301,7 @@
             \ MANUALLY_ADDED_DEPENDENCIES
             \ MEASUREMENT
             \ MODIFIED
+            \ MSVC_DEBUG_INFORMATION_FORMAT
             \ MSVC_RUNTIME_LIBRARY
             \ NAME
             \ NO_SONAME
@@ -343,6 +368,7 @@
             \ SUBDIRECTORIES
             \ SUFFIX
             \ SYMBOLIC
+            \ SYSTEM
             \ Swift_DEPENDENCIES_FILE
             \ Swift_DIAGNOSTICS_FILE
             \ Swift_LANGUAGE_VERSION
@@ -356,16 +382,20 @@
             \ TEST_INCLUDE_FILES
             \ TIMEOUT
             \ TIMEOUT_AFTER_MATCH
+            \ TIMEOUT_SIGNAL_GRACE_PERIOD
+            \ TIMEOUT_SIGNAL_NAME
             \ TYPE
             \ UNITY_BUILD
             \ UNITY_BUILD_BATCH_SIZE
             \ UNITY_BUILD_CODE_AFTER_INCLUDE
             \ UNITY_BUILD_CODE_BEFORE_INCLUDE
             \ UNITY_BUILD_MODE
+            \ UNITY_BUILD_UNIQUE_ID
             \ UNITY_GROUP
             \ USE_FOLDERS
             \ VALUE
             \ VARIABLES
+            \ VERIFY_INTERFACE_HEADER_SETS
             \ VERSION
             \ VISIBILITY_INLINES_HIDDEN
             \ VS_CONFIGURATION_TYPE
@@ -422,11 +452,14 @@
             \ VS_WINRT_EXTENSIONS
             \ VS_WINRT_REFERENCES
             \ VS_XAML_TYPE
+            \ WATCOM_RUNTIME_LIBRARY
             \ WILL_FAIL
             \ WIN32_EXECUTABLE
             \ WINDOWS_EXPORT_ALL_SYMBOLS
             \ WORKING_DIRECTORY
             \ WRAP_EXCLUDE
+            \ XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY
+            \ XCODE_EMBED_FRAMEWORKS_REMOVE_HEADERS_ON_COPY
             \ XCODE_EMIT_EFFECTIVE_PLATFORM_NAME
             \ XCODE_EXPLICIT_FILE_TYPE
             \ XCODE_FILE_ATTRIBUTES
@@ -443,9 +476,13 @@
             \ XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
             \ XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
             \ XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
+            \ XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+            \ XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE
+            \ XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
             \ XCODE_SCHEME_ENVIRONMENT
             \ XCODE_SCHEME_EXECUTABLE
             \ XCODE_SCHEME_GUARD_MALLOC
+            \ XCODE_SCHEME_LAUNCH_CONFIGURATION
             \ XCODE_SCHEME_LAUNCH_MODE
             \ XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP
             \ XCODE_SCHEME_MALLOC_GUARD_EDGES
@@ -460,16 +497,19 @@
             \ XCODE_SCHEME_LAUNCH_CONFIGURATION
             \ XCODE_SCHEME_WORKING_DIRECTORY
             \ XCODE_SCHEME_ZOMBIE_OBJECTS
+            \ XCODE_XCCONFIG
             \ XCTEST
 
 syn keyword cmakeVariable contained
             \ ANDROID
             \ APPLE
             \ BORLAND
+            \ BSD
             \ BUILD_SHARED_LIBS
             \ CACHE
             \ CMAKE_ABSOLUTE_DESTINATION_FILES
             \ CMAKE_ADD_CUSTOM_COMMAND_DEPENDS_EXPLICIT_ONLY
+            \ CMAKE_ADSP_ROOT
             \ CMAKE_AIX_EXPORT_ALL_SYMBOLS
             \ CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS
             \ CMAKE_ANDROID_API
@@ -479,6 +519,7 @@
             \ CMAKE_ANDROID_ARM_MODE
             \ CMAKE_ANDROID_ARM_NEON
             \ CMAKE_ANDROID_ASSETS_DIRECTORIES
+            \ CMAKE_ANDROID_EXCEPTIONS
             \ CMAKE_ANDROID_GUI
             \ CMAKE_ANDROID_JAR_DEPENDENCIES
             \ CMAKE_ANDROID_JAR_DIRECTORIES
@@ -489,14 +530,17 @@
             \ CMAKE_ANDROID_NDK_DEPRECATED_HEADERS
             \ CMAKE_ANDROID_NDK_TOOLCHAIN_HOST_TAG
             \ CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION
+            \ CMAKE_ANDROID_NDK_VERSION
             \ CMAKE_ANDROID_PROCESS_MAX
             \ CMAKE_ANDROID_PROGUARD
             \ CMAKE_ANDROID_PROGUARD_CONFIG_PATH
+            \ CMAKE_ANDROID_RTTI
             \ CMAKE_ANDROID_SECURE_PROPS_PATH
             \ CMAKE_ANDROID_SKIP_ANT_STEP
             \ CMAKE_ANDROID_STANDALONE_TOOLCHAIN
             \ CMAKE_ANDROID_STL_TYPE
             \ CMAKE_APPBUNDLE_PATH
+            \ CMAKE_APPLE_SILICON_PROCESSOR
             \ CMAKE_AR
             \ CMAKE_ARCHIVE_OUTPUT_DIRECTORY
             \ CMAKE_ARGC
@@ -508,12 +552,15 @@
             \ CMAKE_ASM_ARCHIVE_APPEND
             \ CMAKE_ASM_ARCHIVE_CREATE
             \ CMAKE_ASM_ARCHIVE_FINISH
+            \ CMAKE_ASM_BYTE_ORDER
             \ CMAKE_ASM_CLANG_TIDY
+            \ CMAKE_ASM_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_ASM_COMPILER
             \ CMAKE_ASM_COMPILER_ABI
             \ CMAKE_ASM_COMPILER_AR
             \ CMAKE_ASM_COMPILER_ARCHITECTURE_ID
             \ CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_ASM_COMPILER_FRONTEND_VARIANT
             \ CMAKE_ASM_COMPILER_ID
             \ CMAKE_ASM_COMPILER_LAUNCHER
             \ CMAKE_ASM_COMPILER_LOADED
@@ -528,6 +575,8 @@
             \ CMAKE_ASM_CREATE_SHARED_LIBRARY
             \ CMAKE_ASM_CREATE_SHARED_MODULE
             \ CMAKE_ASM_CREATE_STATIC_LIBRARY
+            \ CMAKE_ASM_EXTENSIONS
+            \ CMAKE_ASM_EXTENSIONS_DEFAULT
             \ CMAKE_ASM_FLAGS
             \ CMAKE_ASM_FLAGS_DEBUG
             \ CMAKE_ASM_FLAGS_DEBUG_INIT
@@ -546,6 +595,7 @@
             \ CMAKE_ASM_INCLUDE_WHAT_YOU_USE
             \ CMAKE_ASM_INIT
             \ CMAKE_ASM_LIBRARY_ARCHITECTURE
+            \ CMAKE_ASM_LINKER_LAUNCHER
             \ CMAKE_ASM_LINKER_PREFERENCE
             \ CMAKE_ASM_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_ASM_LINKER_WRAPPER_FLAG
@@ -554,6 +604,7 @@
             \ CMAKE_ASM_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_ASM_LINK_LIBRARY_FLAG
             \ CMAKE_ASM_LINK_LIBRARY_SUFFIX
+            \ CMAKE_ASM_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_ASM_MASM
             \ CMAKE_ASM_MASM_ANDROID_TOOLCHAIN_MACHINE
             \ CMAKE_ASM_MASM_ANDROID_TOOLCHAIN_PREFIX
@@ -561,12 +612,15 @@
             \ CMAKE_ASM_MASM_ARCHIVE_APPEND
             \ CMAKE_ASM_MASM_ARCHIVE_CREATE
             \ CMAKE_ASM_MASM_ARCHIVE_FINISH
+            \ CMAKE_ASM_MASM_BYTE_ORDER
             \ CMAKE_ASM_MASM_CLANG_TIDY
+            \ CMAKE_ASM_MASM_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_ASM_MASM_COMPILER
             \ CMAKE_ASM_MASM_COMPILER_ABI
             \ CMAKE_ASM_MASM_COMPILER_AR
             \ CMAKE_ASM_MASM_COMPILER_ARCHITECTURE_ID
             \ CMAKE_ASM_MASM_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_ASM_MASM_COMPILER_FRONTEND_VARIANT
             \ CMAKE_ASM_MASM_COMPILER_ID
             \ CMAKE_ASM_MASM_COMPILER_LAUNCHER
             \ CMAKE_ASM_MASM_COMPILER_LOADED
@@ -581,6 +635,8 @@
             \ CMAKE_ASM_MASM_CREATE_SHARED_LIBRARY
             \ CMAKE_ASM_MASM_CREATE_SHARED_MODULE
             \ CMAKE_ASM_MASM_CREATE_STATIC_LIBRARY
+            \ CMAKE_ASM_MASM_EXTENSIONS
+            \ CMAKE_ASM_MASM_EXTENSIONS_DEFAULT
             \ CMAKE_ASM_MASM_FLAGS
             \ CMAKE_ASM_MASM_FLAGS_DEBUG
             \ CMAKE_ASM_MASM_FLAGS_DEBUG_INIT
@@ -599,6 +655,7 @@
             \ CMAKE_ASM_MASM_INCLUDE_WHAT_YOU_USE
             \ CMAKE_ASM_MASM_INIT
             \ CMAKE_ASM_MASM_LIBRARY_ARCHITECTURE
+            \ CMAKE_ASM_MASM_LINKER_LAUNCHER
             \ CMAKE_ASM_MASM_LINKER_PREFERENCE
             \ CMAKE_ASM_MASM_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_ASM_MASM_LINKER_WRAPPER_FLAG
@@ -607,14 +664,19 @@
             \ CMAKE_ASM_MASM_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_ASM_MASM_LINK_LIBRARY_FLAG
             \ CMAKE_ASM_MASM_LINK_LIBRARY_SUFFIX
+            \ CMAKE_ASM_MASM_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_ASM_MASM_OUTPUT_EXTENSION
             \ CMAKE_ASM_MASM_PLATFORM_ID
             \ CMAKE_ASM_MASM_SIMULATE_ID
             \ CMAKE_ASM_MASM_SIMULATE_VERSION
             \ CMAKE_ASM_MASM_SIZEOF_DATA_PTR
             \ CMAKE_ASM_MASM_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_ASM_MASM_STANDARD
+            \ CMAKE_ASM_MASM_STANDARD_DEFAULT
             \ CMAKE_ASM_MASM_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_ASM_MASM_STANDARD_LIBRARIES
+            \ CMAKE_ASM_MASM_STANDARD_REQUIRED
+            \ CMAKE_ASM_MASM_SUPPORTED
             \ CMAKE_ASM_MASM_VISIBILITY_PRESET
             \ CMAKE_ASM_NASM
             \ CMAKE_ASM_NASM_ANDROID_TOOLCHAIN_MACHINE
@@ -623,12 +685,15 @@
             \ CMAKE_ASM_NASM_ARCHIVE_APPEND
             \ CMAKE_ASM_NASM_ARCHIVE_CREATE
             \ CMAKE_ASM_NASM_ARCHIVE_FINISH
+            \ CMAKE_ASM_NASM_BYTE_ORDER
             \ CMAKE_ASM_NASM_CLANG_TIDY
+            \ CMAKE_ASM_NASM_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_ASM_NASM_COMPILER
             \ CMAKE_ASM_NASM_COMPILER_ABI
             \ CMAKE_ASM_NASM_COMPILER_AR
             \ CMAKE_ASM_NASM_COMPILER_ARCHITECTURE_ID
             \ CMAKE_ASM_NASM_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_ASM_NASM_COMPILER_FRONTEND_VARIANT
             \ CMAKE_ASM_NASM_COMPILER_ID
             \ CMAKE_ASM_NASM_COMPILER_LAUNCHER
             \ CMAKE_ASM_NASM_COMPILER_LOADED
@@ -643,6 +708,8 @@
             \ CMAKE_ASM_NASM_CREATE_SHARED_LIBRARY
             \ CMAKE_ASM_NASM_CREATE_SHARED_MODULE
             \ CMAKE_ASM_NASM_CREATE_STATIC_LIBRARY
+            \ CMAKE_ASM_NASM_EXTENSIONS
+            \ CMAKE_ASM_NASM_EXTENSIONS_DEFAULT
             \ CMAKE_ASM_NASM_FLAGS
             \ CMAKE_ASM_NASM_FLAGS_DEBUG
             \ CMAKE_ASM_NASM_FLAGS_DEBUG_INIT
@@ -661,6 +728,7 @@
             \ CMAKE_ASM_NASM_INCLUDE_WHAT_YOU_USE
             \ CMAKE_ASM_NASM_INIT
             \ CMAKE_ASM_NASM_LIBRARY_ARCHITECTURE
+            \ CMAKE_ASM_NASM_LINKER_LAUNCHER
             \ CMAKE_ASM_NASM_LINKER_PREFERENCE
             \ CMAKE_ASM_NASM_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_ASM_NASM_LINKER_WRAPPER_FLAG
@@ -669,14 +737,19 @@
             \ CMAKE_ASM_NASM_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_ASM_NASM_LINK_LIBRARY_FLAG
             \ CMAKE_ASM_NASM_LINK_LIBRARY_SUFFIX
+            \ CMAKE_ASM_NASM_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_ASM_NASM_OUTPUT_EXTENSION
             \ CMAKE_ASM_NASM_PLATFORM_ID
             \ CMAKE_ASM_NASM_SIMULATE_ID
             \ CMAKE_ASM_NASM_SIMULATE_VERSION
             \ CMAKE_ASM_NASM_SIZEOF_DATA_PTR
             \ CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_ASM_NASM_STANDARD
+            \ CMAKE_ASM_NASM_STANDARD_DEFAULT
             \ CMAKE_ASM_NASM_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_ASM_NASM_STANDARD_LIBRARIES
+            \ CMAKE_ASM_NASM_STANDARD_REQUIRED
+            \ CMAKE_ASM_NASM_SUPPORTED
             \ CMAKE_ASM_NASM_VISIBILITY_PRESET
             \ CMAKE_ASM_OUTPUT_EXTENSION
             \ CMAKE_ASM_PLATFORM_ID
@@ -684,8 +757,12 @@
             \ CMAKE_ASM_SIMULATE_VERSION
             \ CMAKE_ASM_SIZEOF_DATA_PTR
             \ CMAKE_ASM_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_ASM_STANDARD
+            \ CMAKE_ASM_STANDARD_DEFAULT
             \ CMAKE_ASM_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_ASM_STANDARD_LIBRARIES
+            \ CMAKE_ASM_STANDARD_REQUIRED
+            \ CMAKE_ASM_SUPPORTED
             \ CMAKE_ASM_VISIBILITY_PRESET
             \ CMAKE_AUTOGEN_ORIGIN_DEPENDS
             \ CMAKE_AUTOGEN_PARALLEL
@@ -694,15 +771,18 @@
             \ CMAKE_AUTOMOC
             \ CMAKE_AUTOMOC_COMPILER_PREDEFINES
             \ CMAKE_AUTOMOC_DEPEND_FILTERS
+            \ CMAKE_AUTOMOC_EXECUTABLE
             \ CMAKE_AUTOMOC_MACRO_NAMES
             \ CMAKE_AUTOMOC_MOC_OPTIONS
             \ CMAKE_AUTOMOC_PATH_PREFIX
             \ CMAKE_AUTOMOC_RELAXED_MODE
             \ CMAKE_AUTOMOC_EXECUTABLE
             \ CMAKE_AUTORCC
+            \ CMAKE_AUTORCC_EXECUTABLE
             \ CMAKE_AUTORCC_OPTIONS
             \ CMAKE_AUTORCC_EXECUTABLE
             \ CMAKE_AUTOUIC
+            \ CMAKE_AUTOUIC_EXECUTABLE
             \ CMAKE_AUTOUIC_OPTIONS
             \ CMAKE_AUTOUIC_SEARCH_PATHS
             \ CMAKE_AUTOUIC_EXECUTABLE
@@ -733,6 +813,7 @@
             \ CMAKE_COMPILER_IS_GNUCXX
             \ CMAKE_COMPILER_IS_GNUG77
             \ CMAKE_COMPILE_PDB_OUTPUT_DIRECTORY
+            \ CMAKE_COMPILE_WARNING_AS_ERROR
             \ CMAKE_CONFIGURATION_TYPES
             \ CMAKE_CPACK_COMMAND
             \ CMAKE_CROSSCOMPILING
@@ -745,12 +826,15 @@
             \ CMAKE_CSharp_ARCHIVE_APPEND
             \ CMAKE_CSharp_ARCHIVE_CREATE
             \ CMAKE_CSharp_ARCHIVE_FINISH
+            \ CMAKE_CSharp_BYTE_ORDER
             \ CMAKE_CSharp_CLANG_TIDY
+            \ CMAKE_CSharp_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_CSharp_COMPILER
             \ CMAKE_CSharp_COMPILER_ABI
             \ CMAKE_CSharp_COMPILER_AR
             \ CMAKE_CSharp_COMPILER_ARCHITECTURE_ID
             \ CMAKE_CSharp_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_CSharp_COMPILER_FRONTEND_VARIANT
             \ CMAKE_CSharp_COMPILER_ID
             \ CMAKE_CSharp_COMPILER_LAUNCHER
             \ CMAKE_CSharp_COMPILER_LOADED
@@ -765,6 +849,8 @@
             \ CMAKE_CSharp_CREATE_SHARED_LIBRARY
             \ CMAKE_CSharp_CREATE_SHARED_MODULE
             \ CMAKE_CSharp_CREATE_STATIC_LIBRARY
+            \ CMAKE_CSharp_EXTENSIONS
+            \ CMAKE_CSharp_EXTENSIONS_DEFAULT
             \ CMAKE_CSharp_FLAGS
             \ CMAKE_CSharp_FLAGS_DEBUG
             \ CMAKE_CSharp_FLAGS_DEBUG_INIT
@@ -783,6 +869,7 @@
             \ CMAKE_CSharp_INCLUDE_WHAT_YOU_USE
             \ CMAKE_CSharp_INIT
             \ CMAKE_CSharp_LIBRARY_ARCHITECTURE
+            \ CMAKE_CSharp_LINKER_LAUNCHER
             \ CMAKE_CSharp_LINKER_PREFERENCE
             \ CMAKE_CSharp_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_CSharp_LINKER_WRAPPER_FLAG
@@ -791,14 +878,19 @@
             \ CMAKE_CSharp_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_CSharp_LINK_LIBRARY_FLAG
             \ CMAKE_CSharp_LINK_LIBRARY_SUFFIX
+            \ CMAKE_CSharp_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_CSharp_OUTPUT_EXTENSION
             \ CMAKE_CSharp_PLATFORM_ID
             \ CMAKE_CSharp_SIMULATE_ID
             \ CMAKE_CSharp_SIMULATE_VERSION
             \ CMAKE_CSharp_SIZEOF_DATA_PTR
             \ CMAKE_CSharp_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_CSharp_STANDARD
+            \ CMAKE_CSharp_STANDARD_DEFAULT
             \ CMAKE_CSharp_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_CSharp_STANDARD_LIBRARIES
+            \ CMAKE_CSharp_STANDARD_REQUIRED
+            \ CMAKE_CSharp_SUPPORTED
             \ CMAKE_CSharp_VISIBILITY_PRESET
             \ CMAKE_CTEST_ARGUMENTS
             \ CMAKE_CTEST_COMMAND
@@ -810,12 +902,15 @@
             \ CMAKE_CUDA_ARCHIVE_APPEND
             \ CMAKE_CUDA_ARCHIVE_CREATE
             \ CMAKE_CUDA_ARCHIVE_FINISH
+            \ CMAKE_CUDA_BYTE_ORDER
             \ CMAKE_CUDA_CLANG_TIDY
+            \ CMAKE_CUDA_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_CUDA_COMPILER
             \ CMAKE_CUDA_COMPILER_ABI
             \ CMAKE_CUDA_COMPILER_AR
             \ CMAKE_CUDA_COMPILER_ARCHITECTURE_ID
             \ CMAKE_CUDA_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_CUDA_COMPILER_FRONTEND_VARIANT
             \ CMAKE_CUDA_COMPILER_ID
             \ CMAKE_CUDA_COMPILER_LAUNCHER
             \ CMAKE_CUDA_COMPILER_LOADED
@@ -832,6 +927,7 @@
             \ CMAKE_CUDA_CREATE_SHARED_MODULE
             \ CMAKE_CUDA_CREATE_STATIC_LIBRARY
             \ CMAKE_CUDA_EXTENSIONS
+            \ CMAKE_CUDA_EXTENSIONS_DEFAULT
             \ CMAKE_CUDA_FLAGS
             \ CMAKE_CUDA_FLAGS_DEBUG
             \ CMAKE_CUDA_FLAGS_DEBUG_INIT
@@ -851,6 +947,7 @@
             \ CMAKE_CUDA_INCLUDE_WHAT_YOU_USE
             \ CMAKE_CUDA_INIT
             \ CMAKE_CUDA_LIBRARY_ARCHITECTURE
+            \ CMAKE_CUDA_LINKER_LAUNCHER
             \ CMAKE_CUDA_LINKER_PREFERENCE
             \ CMAKE_CUDA_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_CUDA_LINKER_WRAPPER_FLAG
@@ -859,6 +956,7 @@
             \ CMAKE_CUDA_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_CUDA_LINK_LIBRARY_FLAG
             \ CMAKE_CUDA_LINK_LIBRARY_SUFFIX
+            \ CMAKE_CUDA_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_CUDA_OUTPUT_EXTENSION
             \ CMAKE_CUDA_PLATFORM_ID
             \ CMAKE_CUDA_RESOLVE_DEVICE_SYMBOLS
@@ -869,9 +967,11 @@
             \ CMAKE_CUDA_SIZEOF_DATA_PTR
             \ CMAKE_CUDA_SOURCE_FILE_EXTENSIONS
             \ CMAKE_CUDA_STANDARD
+            \ CMAKE_CUDA_STANDARD_DEFAULT
             \ CMAKE_CUDA_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_CUDA_STANDARD_LIBRARIES
             \ CMAKE_CUDA_STANDARD_REQUIRED
+            \ CMAKE_CUDA_SUPPORTED
             \ CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES
             \ CMAKE_CUDA_VISIBILITY_PRESET
             \ CMAKE_CURRENT_BINARY_DIR
@@ -890,12 +990,15 @@
             \ CMAKE_CXX_ARCHIVE_APPEND
             \ CMAKE_CXX_ARCHIVE_CREATE
             \ CMAKE_CXX_ARCHIVE_FINISH
+            \ CMAKE_CXX_BYTE_ORDER
             \ CMAKE_CXX_CLANG_TIDY
+            \ CMAKE_CXX_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_CXX_COMPILER
             \ CMAKE_CXX_COMPILER_ABI
             \ CMAKE_CXX_COMPILER_AR
             \ CMAKE_CXX_COMPILER_ARCHITECTURE_ID
             \ CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_CXX_COMPILER_FRONTEND_VARIANT
             \ CMAKE_CXX_COMPILER_ID
             \ CMAKE_CXX_COMPILER_LAUNCHER
             \ CMAKE_CXX_COMPILER_LOADED
@@ -912,6 +1015,7 @@
             \ CMAKE_CXX_CREATE_SHARED_MODULE
             \ CMAKE_CXX_CREATE_STATIC_LIBRARY
             \ CMAKE_CXX_EXTENSIONS
+            \ CMAKE_CXX_EXTENSIONS_DEFAULT
             \ CMAKE_CXX_FLAGS
             \ CMAKE_CXX_FLAGS_DEBUG
             \ CMAKE_CXX_FLAGS_DEBUG_INIT
@@ -939,16 +1043,20 @@
             \ CMAKE_CXX_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_CXX_LINK_LIBRARY_FLAG
             \ CMAKE_CXX_LINK_LIBRARY_SUFFIX
+            \ CMAKE_CXX_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_CXX_OUTPUT_EXTENSION
             \ CMAKE_CXX_PLATFORM_ID
+            \ CMAKE_CXX_SCAN_FOR_MODULES
             \ CMAKE_CXX_SIMULATE_ID
             \ CMAKE_CXX_SIMULATE_VERSION
             \ CMAKE_CXX_SIZEOF_DATA_PTR
             \ CMAKE_CXX_SOURCE_FILE_EXTENSIONS
             \ CMAKE_CXX_STANDARD
+            \ CMAKE_CXX_STANDARD_DEFAULT
             \ CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_CXX_STANDARD_LIBRARIES
             \ CMAKE_CXX_STANDARD_REQUIRED
+            \ CMAKE_CXX_SUPPORTED
             \ CMAKE_CXX_VISIBILITY_PRESET
             \ CMAKE_C_ANDROID_TOOLCHAIN_MACHINE
             \ CMAKE_C_ANDROID_TOOLCHAIN_PREFIX
@@ -956,12 +1064,15 @@
             \ CMAKE_C_ARCHIVE_APPEND
             \ CMAKE_C_ARCHIVE_CREATE
             \ CMAKE_C_ARCHIVE_FINISH
+            \ CMAKE_C_BYTE_ORDER
             \ CMAKE_C_CLANG_TIDY
+            \ CMAKE_C_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_C_COMPILER
             \ CMAKE_C_COMPILER_ABI
             \ CMAKE_C_COMPILER_AR
             \ CMAKE_C_COMPILER_ARCHITECTURE_ID
             \ CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_C_COMPILER_FRONTEND_VARIANT
             \ CMAKE_C_COMPILER_ID
             \ CMAKE_C_COMPILER_LAUNCHER
             \ CMAKE_C_COMPILER_LOADED
@@ -978,6 +1089,7 @@
             \ CMAKE_C_CREATE_SHARED_MODULE
             \ CMAKE_C_CREATE_STATIC_LIBRARY
             \ CMAKE_C_EXTENSIONS
+            \ CMAKE_C_EXTENSIONS_DEFAULT
             \ CMAKE_C_FLAGS
             \ CMAKE_C_FLAGS_DEBUG
             \ CMAKE_C_FLAGS_DEBUG_INIT
@@ -1005,6 +1117,7 @@
             \ CMAKE_C_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_C_LINK_LIBRARY_FLAG
             \ CMAKE_C_LINK_LIBRARY_SUFFIX
+            \ CMAKE_C_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_C_OUTPUT_EXTENSION
             \ CMAKE_C_PLATFORM_ID
             \ CMAKE_C_SIMULATE_ID
@@ -1012,17 +1125,21 @@
             \ CMAKE_C_SIZEOF_DATA_PTR
             \ CMAKE_C_SOURCE_FILE_EXTENSIONS
             \ CMAKE_C_STANDARD
+            \ CMAKE_C_STANDARD_DEFAULT
             \ CMAKE_C_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_C_STANDARD_LIBRARIES
             \ CMAKE_C_STANDARD_REQUIRED
+            \ CMAKE_C_SUPPORTED
             \ CMAKE_C_VISIBILITY_PRESET
             \ CMAKE_DEBUG_POSTFIX
             \ CMAKE_DEBUG_TARGET_PROPERTIES
             \ CMAKE_DEFAULT_BUILD_TYPE
             \ CMAKE_DEFAULT_CONFIGS
             \ CMAKE_DEPENDS_IN_PROJECT_ONLY
+            \ CMAKE_DEPENDS_USE_COMPILER
             \ CMAKE_DIRECTORY_LABELS
             \ CMAKE_DISABLE_PRECOMPILE_HEADERS
+            \ CMAKE_DLL_NAME_WITH_SOVERSION
             \ CMAKE_DL_LIBS
             \ CMAKE_DOTNET_SDK
             \ CMAKE_DOTNET_TARGET_FRAMEWORK
@@ -1036,7 +1153,20 @@
             \ CMAKE_ENABLE_EXPORTS
             \ CMAKE_ERROR_DEPRECATED
             \ CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
+            \ CMAKE_EXECUTABLE_ENABLE_EXPORTS
             \ CMAKE_EXECUTABLE_SUFFIX
+            \ CMAKE_EXECUTABLE_SUFFIX_ASM
+            \ CMAKE_EXECUTABLE_SUFFIX_ASM_MASM
+            \ CMAKE_EXECUTABLE_SUFFIX_ASM_NASM
+            \ CMAKE_EXECUTABLE_SUFFIX_C
+            \ CMAKE_EXECUTABLE_SUFFIX_CSharp
+            \ CMAKE_EXECUTABLE_SUFFIX_CUDA
+            \ CMAKE_EXECUTABLE_SUFFIX_CXX
+            \ CMAKE_EXECUTABLE_SUFFIX_Fortran
+            \ CMAKE_EXECUTABLE_SUFFIX_HIP
+            \ CMAKE_EXECUTABLE_SUFFIX_Java
+            \ CMAKE_EXECUTABLE_SUFFIX_RC
+            \ CMAKE_EXECUTABLE_SUFFIX_Swift
             \ CMAKE_EXECUTE_PROCESS_COMMAND_ECHO
             \ CMAKE_EXE_LINKER_FLAGS
             \ CMAKE_EXE_LINKER_FLAGS_INIT
@@ -1056,9 +1186,11 @@
             \ CMAKE_FIND_PACKAGE_NO_PACKAGE_REGISTRY
             \ CMAKE_FIND_PACKAGE_NO_SYSTEM_PACKAGE_REGISTRY
             \ CMAKE_FIND_PACKAGE_PREFER_CONFIG
+            \ CMAKE_FIND_PACKAGE_REDIRECTS_DIR
             \ CMAKE_FIND_PACKAGE_RESOLVE_SYMLINKS
             \ CMAKE_FIND_PACKAGE_SORT_DIRECTION
             \ CMAKE_FIND_PACKAGE_SORT_ORDER
+            \ CMAKE_FIND_PACKAGE_TARGETS_GLOBAL
             \ CMAKE_FIND_PACKAGE_WARN_NO_MODULE
             \ CMAKE_FIND_ROOT_PATH
             \ CMAKE_FIND_ROOT_PATH_MODE_INCLUDE
@@ -1069,6 +1201,7 @@
             \ CMAKE_FIND_USE_INSTALL_PREFIX
             \ CMAKE_FIND_USE_CMAKE_PATH
             \ CMAKE_FIND_USE_CMAKE_SYSTEM_PATH
+            \ CMAKE_FIND_USE_INSTALL_PREFIX
             \ CMAKE_FIND_USE_PACKAGE_REGISTRY
             \ CMAKE_FIND_USE_PACKAGE_ROOT_PATH
             \ CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH
@@ -1083,12 +1216,15 @@
             \ CMAKE_Fortran_ARCHIVE_APPEND
             \ CMAKE_Fortran_ARCHIVE_CREATE
             \ CMAKE_Fortran_ARCHIVE_FINISH
+            \ CMAKE_Fortran_BYTE_ORDER
             \ CMAKE_Fortran_CLANG_TIDY
+            \ CMAKE_Fortran_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_Fortran_COMPILER
             \ CMAKE_Fortran_COMPILER_ABI
             \ CMAKE_Fortran_COMPILER_AR
             \ CMAKE_Fortran_COMPILER_ARCHITECTURE_ID
             \ CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_Fortran_COMPILER_FRONTEND_VARIANT
             \ CMAKE_Fortran_COMPILER_ID
             \ CMAKE_Fortran_COMPILER_LAUNCHER
             \ CMAKE_Fortran_COMPILER_LOADED
@@ -1103,6 +1239,8 @@
             \ CMAKE_Fortran_CREATE_SHARED_LIBRARY
             \ CMAKE_Fortran_CREATE_SHARED_MODULE
             \ CMAKE_Fortran_CREATE_STATIC_LIBRARY
+            \ CMAKE_Fortran_EXTENSIONS
+            \ CMAKE_Fortran_EXTENSIONS_DEFAULT
             \ CMAKE_Fortran_FLAGS
             \ CMAKE_Fortran_FLAGS_DEBUG
             \ CMAKE_Fortran_FLAGS_DEBUG_INIT
@@ -1122,6 +1260,7 @@
             \ CMAKE_Fortran_INCLUDE_WHAT_YOU_USE
             \ CMAKE_Fortran_INIT
             \ CMAKE_Fortran_LIBRARY_ARCHITECTURE
+            \ CMAKE_Fortran_LINKER_LAUNCHER
             \ CMAKE_Fortran_LINKER_PREFERENCE
             \ CMAKE_Fortran_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_Fortran_LINKER_WRAPPER_FLAG
@@ -1130,6 +1269,7 @@
             \ CMAKE_Fortran_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_Fortran_LINK_LIBRARY_FLAG
             \ CMAKE_Fortran_LINK_LIBRARY_SUFFIX
+            \ CMAKE_Fortran_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_Fortran_MODDIR_DEFAULT
             \ CMAKE_Fortran_MODDIR_FLAG
             \ CMAKE_Fortran_MODOUT_FLAG
@@ -1141,8 +1281,12 @@
             \ CMAKE_Fortran_SIMULATE_VERSION
             \ CMAKE_Fortran_SIZEOF_DATA_PTR
             \ CMAKE_Fortran_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_Fortran_STANDARD
+            \ CMAKE_Fortran_STANDARD_DEFAULT
             \ CMAKE_Fortran_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_Fortran_STANDARD_LIBRARIES
+            \ CMAKE_Fortran_STANDARD_REQUIRED
+            \ CMAKE_Fortran_SUPPORTED
             \ CMAKE_Fortran_VISIBILITY_PRESET
             \ CMAKE_GENERATOR
             \ CMAKE_GENERATOR_INSTANCE
@@ -1162,12 +1306,15 @@
             \ CMAKE_HIP_ARCHIVE_APPEND
             \ CMAKE_HIP_ARCHIVE_CREATE
             \ CMAKE_HIP_ARCHIVE_FINISH
+            \ CMAKE_HIP_BYTE_ORDER
             \ CMAKE_HIP_CLANG_TIDY
+            \ CMAKE_HIP_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_HIP_COMPILER
             \ CMAKE_HIP_COMPILER_ABI
             \ CMAKE_HIP_COMPILER_AR
             \ CMAKE_HIP_COMPILER_ARCHITECTURE_ID
             \ CMAKE_HIP_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_HIP_COMPILER_FRONTEND_VARIANT
             \ CMAKE_HIP_COMPILER_ID
             \ CMAKE_HIP_COMPILER_LAUNCHER
             \ CMAKE_HIP_COMPILER_LOADED
@@ -1184,6 +1331,7 @@
             \ CMAKE_HIP_CREATE_SHARED_MODULE
             \ CMAKE_HIP_CREATE_STATIC_LIBRARY
             \ CMAKE_HIP_EXTENSIONS
+            \ CMAKE_HIP_EXTENSIONS_DEFAULT
             \ CMAKE_HIP_FLAGS
             \ CMAKE_HIP_FLAGS_DEBUG
             \ CMAKE_HIP_FLAGS_DEBUG_INIT
@@ -1211,6 +1359,7 @@
             \ CMAKE_HIP_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_HIP_LINK_LIBRARY_FLAG
             \ CMAKE_HIP_LINK_LIBRARY_SUFFIX
+            \ CMAKE_HIP_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_HIP_OUTPUT_EXTENSION
             \ CMAKE_HIP_PLATFORM_ID
             \ CMAKE_HIP_SIMULATE_ID
@@ -1218,12 +1367,16 @@
             \ CMAKE_HIP_SIZEOF_DATA_PTR
             \ CMAKE_HIP_SOURCE_FILE_EXTENSIONS
             \ CMAKE_HIP_STANDARD
+            \ CMAKE_HIP_STANDARD_DEFAULT
             \ CMAKE_HIP_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_HIP_STANDARD_LIBRARIES
             \ CMAKE_HIP_STANDARD_REQUIRED
+            \ CMAKE_HIP_SUPPORTED
             \ CMAKE_HIP_VISIBILITY_PRESET
             \ CMAKE_HOME_DIRECTORY
             \ CMAKE_HOST_APPLE
+            \ CMAKE_HOST_BSD
+            \ CMAKE_HOST_LINUX
             \ CMAKE_HOST_SOLARIS
             \ CMAKE_HOST_SYSTEM
             \ CMAKE_HOST_SYSTEM_NAME
@@ -1232,6 +1385,7 @@
             \ CMAKE_HOST_UNIX
             \ CMAKE_HOST_WIN32
             \ CMAKE_IGNORE_PATH
+            \ CMAKE_IGNORE_PREFIX_PATH
             \ CMAKE_IMPORT_LIBRARY_PREFIX
             \ CMAKE_IMPORT_LIBRARY_SUFFIX
             \ CMAKE_INCLUDE_CURRENT_DIR
@@ -1252,6 +1406,7 @@
             \ CMAKE_INTERPROCEDURAL_OPTIMIZATION
             \ CMAKE_IOS_INSTALL_COMBINED
             \ CMAKE_ISPC_HEADER_DIRECTORY
+            \ CMAKE_ISPC_HEADER_SUFFIX
             \ CMAKE_ISPC_INSTRUCTION_SETS
             \ CMAKE_JOB_POOLS
             \ CMAKE_JOB_POOL_COMPILE
@@ -1264,12 +1419,15 @@
             \ CMAKE_Java_ARCHIVE_APPEND
             \ CMAKE_Java_ARCHIVE_CREATE
             \ CMAKE_Java_ARCHIVE_FINISH
+            \ CMAKE_Java_BYTE_ORDER
             \ CMAKE_Java_CLANG_TIDY
+            \ CMAKE_Java_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_Java_COMPILER
             \ CMAKE_Java_COMPILER_ABI
             \ CMAKE_Java_COMPILER_AR
             \ CMAKE_Java_COMPILER_ARCHITECTURE_ID
             \ CMAKE_Java_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_Java_COMPILER_FRONTEND_VARIANT
             \ CMAKE_Java_COMPILER_ID
             \ CMAKE_Java_COMPILER_LAUNCHER
             \ CMAKE_Java_COMPILER_LOADED
@@ -1284,6 +1442,8 @@
             \ CMAKE_Java_CREATE_SHARED_LIBRARY
             \ CMAKE_Java_CREATE_SHARED_MODULE
             \ CMAKE_Java_CREATE_STATIC_LIBRARY
+            \ CMAKE_Java_EXTENSIONS
+            \ CMAKE_Java_EXTENSIONS_DEFAULT
             \ CMAKE_Java_FLAGS
             \ CMAKE_Java_FLAGS_DEBUG
             \ CMAKE_Java_FLAGS_DEBUG_INIT
@@ -1302,6 +1462,7 @@
             \ CMAKE_Java_INCLUDE_WHAT_YOU_USE
             \ CMAKE_Java_INIT
             \ CMAKE_Java_LIBRARY_ARCHITECTURE
+            \ CMAKE_Java_LINKER_LAUNCHER
             \ CMAKE_Java_LINKER_PREFERENCE
             \ CMAKE_Java_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_Java_LINKER_WRAPPER_FLAG
@@ -1310,15 +1471,22 @@
             \ CMAKE_Java_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_Java_LINK_LIBRARY_FLAG
             \ CMAKE_Java_LINK_LIBRARY_SUFFIX
+            \ CMAKE_Java_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_Java_OUTPUT_EXTENSION
             \ CMAKE_Java_PLATFORM_ID
             \ CMAKE_Java_SIMULATE_ID
             \ CMAKE_Java_SIMULATE_VERSION
             \ CMAKE_Java_SIZEOF_DATA_PTR
             \ CMAKE_Java_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_Java_STANDARD
+            \ CMAKE_Java_STANDARD_DEFAULT
             \ CMAKE_Java_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_Java_STANDARD_LIBRARIES
+            \ CMAKE_Java_STANDARD_REQUIRED
+            \ CMAKE_Java_SUPPORTED
             \ CMAKE_Java_VISIBILITY_PRESET
+            \ CMAKE_KATE_FILES_MODE
+            \ CMAKE_KATE_MAKE_ARGUMENTS
             \ CMAKE_LIBRARY_ARCHITECTURE
             \ CMAKE_LIBRARY_ARCHITECTURE_REGEX
             \ CMAKE_LIBRARY_OUTPUT_DIRECTORY
@@ -1326,14 +1494,17 @@
             \ CMAKE_LIBRARY_PATH_FLAG
             \ CMAKE_LINK_DEF_FILE_FLAG
             \ CMAKE_LINK_DEPENDS_NO_SHARED
+            \ CMAKE_LINK_DEPENDS_USE_LINKER
             \ CMAKE_LINK_DIRECTORIES_BEFORE
             \ CMAKE_LINK_INTERFACE_LIBRARIES
+            \ CMAKE_LINK_LIBRARIES_ONLY_TARGETS
             \ CMAKE_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_LINK_LIBRARY_FLAG
             \ CMAKE_LINK_LIBRARY_SUFFIX
             \ CMAKE_LINK_SEARCH_END_STATIC
             \ CMAKE_LINK_SEARCH_START_STATIC
             \ CMAKE_LINK_WHAT_YOU_USE
+            \ CMAKE_LINK_WHAT_YOU_USE_CHECK
             \ CMAKE_MACOSX_BUNDLE
             \ CMAKE_MACOSX_RPATH
             \ CMAKE_MAJOR_VERSION
@@ -1351,6 +1522,7 @@
             \ CMAKE_MODULE_LINKER_FLAGS_INIT
             \ CMAKE_MODULE_PATH
             \ CMAKE_MSVCIDE_RUN_PATH
+            \ CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
             \ CMAKE_MSVC_RUNTIME_LIBRARY
             \ CMAKE_NETRC
             \ CMAKE_NETRC_FILE
@@ -1378,6 +1550,7 @@
             \ CMAKE_PCH_INSTANTIATE_TEMPLATES
             \ CMAKE_PCH_WARN_INVALID
             \ CMAKE_PDB_OUTPUT_DIRECTORY
+            \ CMAKE_PLATFORM_NO_VERSIONED_SONAME
             \ CMAKE_POSITION_INDEPENDENT_CODE
             \ CMAKE_PREFIX_PATH
             \ CMAKE_PROGRAM_PATH
@@ -1386,6 +1559,7 @@
             \ CMAKE_PROJECT_INCLUDE
             \ CMAKE_PROJECT_INCLUDE_BEFORE
             \ CMAKE_PROJECT_NAME
+            \ CMAKE_PROJECT_TOP_LEVEL_INCLUDES
             \ CMAKE_PROJECT_VERSION
             \ CMAKE_PROJECT_VERSION_MAJOR
             \ CMAKE_PROJECT_VERSION_MINOR
@@ -1399,12 +1573,15 @@
             \ CMAKE_RC_ARCHIVE_APPEND
             \ CMAKE_RC_ARCHIVE_CREATE
             \ CMAKE_RC_ARCHIVE_FINISH
+            \ CMAKE_RC_BYTE_ORDER
             \ CMAKE_RC_CLANG_TIDY
+            \ CMAKE_RC_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_RC_COMPILER
             \ CMAKE_RC_COMPILER_ABI
             \ CMAKE_RC_COMPILER_AR
             \ CMAKE_RC_COMPILER_ARCHITECTURE_ID
             \ CMAKE_RC_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_RC_COMPILER_FRONTEND_VARIANT
             \ CMAKE_RC_COMPILER_ID
             \ CMAKE_RC_COMPILER_LAUNCHER
             \ CMAKE_RC_COMPILER_LOADED
@@ -1419,6 +1596,8 @@
             \ CMAKE_RC_CREATE_SHARED_LIBRARY
             \ CMAKE_RC_CREATE_SHARED_MODULE
             \ CMAKE_RC_CREATE_STATIC_LIBRARY
+            \ CMAKE_RC_EXTENSIONS
+            \ CMAKE_RC_EXTENSIONS_DEFAULT
             \ CMAKE_RC_FLAGS
             \ CMAKE_RC_FLAGS_DEBUG
             \ CMAKE_RC_FLAGS_DEBUG_INIT
@@ -1437,6 +1616,7 @@
             \ CMAKE_RC_INCLUDE_WHAT_YOU_USE
             \ CMAKE_RC_INIT
             \ CMAKE_RC_LIBRARY_ARCHITECTURE
+            \ CMAKE_RC_LINKER_LAUNCHER
             \ CMAKE_RC_LINKER_PREFERENCE
             \ CMAKE_RC_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_RC_LINKER_WRAPPER_FLAG
@@ -1445,19 +1625,25 @@
             \ CMAKE_RC_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_RC_LINK_LIBRARY_FLAG
             \ CMAKE_RC_LINK_LIBRARY_SUFFIX
+            \ CMAKE_RC_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_RC_OUTPUT_EXTENSION
             \ CMAKE_RC_PLATFORM_ID
             \ CMAKE_RC_SIMULATE_ID
             \ CMAKE_RC_SIMULATE_VERSION
             \ CMAKE_RC_SIZEOF_DATA_PTR
             \ CMAKE_RC_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_RC_STANDARD
+            \ CMAKE_RC_STANDARD_DEFAULT
             \ CMAKE_RC_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_RC_STANDARD_LIBRARIES
+            \ CMAKE_RC_STANDARD_REQUIRED
+            \ CMAKE_RC_SUPPORTED
             \ CMAKE_RC_VISIBILITY_PRESET
             \ CMAKE_ROOT
             \ CMAKE_RULE_MESSAGES
             \ CMAKE_RUNTIME_OUTPUT_DIRECTORY
             \ CMAKE_SCRIPT_MODE_FILE
+            \ CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS
             \ CMAKE_SHARED_LIBRARY_PREFIX
             \ CMAKE_SHARED_LIBRARY_SUFFIX
             \ CMAKE_SHARED_LINKER_FLAGS
@@ -1486,6 +1672,7 @@
             \ CMAKE_SYSTEM_APPBUNDLE_PATH
             \ CMAKE_SYSTEM_FRAMEWORK_PATH
             \ CMAKE_SYSTEM_IGNORE_PATH
+            \ CMAKE_SYSTEM_IGNORE_PREFIX_PATH
             \ CMAKE_SYSTEM_INCLUDE_PATH
             \ CMAKE_SYSTEM_LIBRARY_PATH
             \ CMAKE_SYSTEM_NAME
@@ -1500,12 +1687,15 @@
             \ CMAKE_Swift_ARCHIVE_APPEND
             \ CMAKE_Swift_ARCHIVE_CREATE
             \ CMAKE_Swift_ARCHIVE_FINISH
+            \ CMAKE_Swift_BYTE_ORDER
             \ CMAKE_Swift_CLANG_TIDY
+            \ CMAKE_Swift_CLANG_TIDY_EXPORT_FIXES_DIR
             \ CMAKE_Swift_COMPILER
             \ CMAKE_Swift_COMPILER_ABI
             \ CMAKE_Swift_COMPILER_AR
             \ CMAKE_Swift_COMPILER_ARCHITECTURE_ID
             \ CMAKE_Swift_COMPILER_EXTERNAL_TOOLCHAIN
+            \ CMAKE_Swift_COMPILER_FRONTEND_VARIANT
             \ CMAKE_Swift_COMPILER_ID
             \ CMAKE_Swift_COMPILER_LAUNCHER
             \ CMAKE_Swift_COMPILER_LOADED
@@ -1520,6 +1710,8 @@
             \ CMAKE_Swift_CREATE_SHARED_LIBRARY
             \ CMAKE_Swift_CREATE_SHARED_MODULE
             \ CMAKE_Swift_CREATE_STATIC_LIBRARY
+            \ CMAKE_Swift_EXTENSIONS
+            \ CMAKE_Swift_EXTENSIONS_DEFAULT
             \ CMAKE_Swift_FLAGS
             \ CMAKE_Swift_FLAGS_DEBUG
             \ CMAKE_Swift_FLAGS_DEBUG_INIT
@@ -1539,6 +1731,7 @@
             \ CMAKE_Swift_INIT
             \ CMAKE_Swift_LANGUAGE_VERSION
             \ CMAKE_Swift_LIBRARY_ARCHITECTURE
+            \ CMAKE_Swift_LINKER_LAUNCHER
             \ CMAKE_Swift_LINKER_PREFERENCE
             \ CMAKE_Swift_LINKER_PREFERENCE_PROPAGATES
             \ CMAKE_Swift_LINKER_WRAPPER_FLAG
@@ -1547,6 +1740,7 @@
             \ CMAKE_Swift_LINK_LIBRARY_FILE_FLAG
             \ CMAKE_Swift_LINK_LIBRARY_FLAG
             \ CMAKE_Swift_LINK_LIBRARY_SUFFIX
+            \ CMAKE_Swift_LINK_WHAT_YOU_USE_FLAG
             \ CMAKE_Swift_MODULE_DIRECTORY
             \ CMAKE_Swift_NUM_THREADS
             \ CMAKE_Swift_OUTPUT_EXTENSION
@@ -1555,16 +1749,25 @@
             \ CMAKE_Swift_SIMULATE_VERSION
             \ CMAKE_Swift_SIZEOF_DATA_PTR
             \ CMAKE_Swift_SOURCE_FILE_EXTENSIONS
+            \ CMAKE_Swift_STANDARD
+            \ CMAKE_Swift_STANDARD_DEFAULT
             \ CMAKE_Swift_STANDARD_INCLUDE_DIRECTORIES
             \ CMAKE_Swift_STANDARD_LIBRARIES
+            \ CMAKE_Swift_STANDARD_REQUIRED
+            \ CMAKE_Swift_SUPPORTED
             \ CMAKE_Swift_VISIBILITY_PRESET
+            \ CMAKE_TASKING_TOOLSET
+            \ CMAKE_TLS_CAINFO
+            \ CMAKE_TLS_VERIFY
             \ CMAKE_TOOLCHAIN_FILE
             \ CMAKE_TRY_COMPILE_CONFIGURATION
+            \ CMAKE_TRY_COMPILE_NO_PLATFORM_VARIABLES
             \ CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
             \ CMAKE_TRY_COMPILE_TARGET_TYPE
             \ CMAKE_TWEAK_VERSION
             \ CMAKE_UNITY_BUILD
             \ CMAKE_UNITY_BUILD_BATCH_SIZE
+            \ CMAKE_UNITY_BUILD_UNIQUE_ID
             \ CMAKE_USER_MAKE_RULES_OVERRIDE
             \ CMAKE_USER_MAKE_RULES_OVERRIDE_ASM
             \ CMAKE_USER_MAKE_RULES_OVERRIDE_ASM_MASM
@@ -1580,8 +1783,13 @@
             \ CMAKE_USER_MAKE_RULES_OVERRIDE_Swift
             \ CMAKE_USE_RELATIVE_PATHS
             \ CMAKE_VERBOSE_MAKEFILE
+            \ CMAKE_VERIFY_INTERFACE_HEADER_SETS
             \ CMAKE_VERSION
             \ CMAKE_VISIBILITY_INLINES_HIDDEN
+            \ CMAKE_VS_DEBUGGER_COMMAND
+            \ CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS
+            \ CMAKE_VS_DEBUGGER_ENVIRONMENT
+            \ CMAKE_VS_DEBUGGER_WORKING_DIRECTORY
             \ CMAKE_VS_DEVENV_COMMAND
             \ CMAKE_VS_GLOBALS
             \ CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD
@@ -1589,6 +1797,8 @@
             \ CMAKE_VS_INTEL_Fortran_PROJECT_VERSION
             \ CMAKE_VS_JUST_MY_CODE_DEBUGGING
             \ CMAKE_VS_MSBUILD_COMMAND
+            \ CMAKE_VS_NO_COMPILE_BATCHING
+            \ CMAKE_VS_NUGET_PACKAGE_RESTORE
             \ CMAKE_VS_NsightTegra_VERSION
             \ CMAKE_VS_PLATFORM_NAME
             \ CMAKE_VS_PLATFORM_NAME_DEFAULT
@@ -1604,11 +1814,17 @@
             \ CMAKE_VS_SDK_LIBRARY_WINRT_DIRECTORIES
             \ CMAKE_VS_SDK_REFERENCE_DIRECTORIES
             \ CMAKE_VS_SDK_SOURCE_DIRECTORIES
+            \ CMAKE_VS_TARGET_FRAMEWORK_IDENTIFIER
+            \ CMAKE_VS_TARGET_FRAMEWORK_TARGETS_VERSION
+            \ CMAKE_VS_TARGET_FRAMEWORK_VERSION
+            \ CMAKE_VS_VERSION_BUILD_NUMBER
+            \ CMAKE_VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION
             \ CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION
             \ CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION_MAXIMUM
             \ CMAKE_VS_WINRT_BY_DEFAULT
             \ CMAKE_WARN_DEPRECATED
             \ CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION
+            \ CMAKE_WATCOM_RUNTIME_LIBRARY
             \ CMAKE_WIN32_EXECUTABLE
             \ CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS
             \ CMAKE_XCODE_BUILD_SYSTEM
@@ -1623,8 +1839,12 @@
             \ CMAKE_XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER
             \ CMAKE_XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS
             \ CMAKE_XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE
+            \ CMAKE_XCODE_SCHEME_ENABLE_GPU_API_VALIDATION
+            \ CMAKE_XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE
+            \ CMAKE_XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION
             \ CMAKE_XCODE_SCHEME_ENVIRONMENT
             \ CMAKE_XCODE_SCHEME_GUARD_MALLOC
+            \ CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION
             \ CMAKE_XCODE_SCHEME_LAUNCH_MODE
             \ CMAKE_XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP
             \ CMAKE_XCODE_SCHEME_MALLOC_GUARD_EDGES
@@ -1639,8 +1859,10 @@
             \ CMAKE_XCODE_SCHEME_LAUNCH_CONFIGURATION
             \ CMAKE_XCODE_SCHEME_WORKING_DIRECTORY
             \ CMAKE_XCODE_SCHEME_ZOMBIE_OBJECTS
+            \ CMAKE_XCODE_XCCONFIG
             \ CPACK_ABSOLUTE_DESTINATION_FILES
             \ CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY
+            \ CPACK_CUSTOM_INSTALL_VARIABLES
             \ CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION
             \ CPACK_INCLUDE_TOPLEVEL_DIRECTORY
             \ CPACK_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
@@ -1675,6 +1897,7 @@
             \ CTEST_CUSTOM_PRE_MEMCHECK
             \ CTEST_CUSTOM_PRE_TEST
             \ CTEST_CUSTOM_TESTS_IGNORE
+            \ CTEST_CUSTOM_TEST_OUTPUT_TRUNCATION
             \ CTEST_CUSTOM_WARNING_EXCEPTION
             \ CTEST_CUSTOM_WARNING_MATCH
             \ CTEST_CVS_CHECKOUT
@@ -1707,6 +1930,7 @@
             \ CTEST_RESOURCE_SPEC_FILE
             \ CTEST_RUN_CURRENT_SCRIPT
             \ CTEST_SCP_COMMAND
+            \ CTEST_SCRIPT_DIRECTORY
             \ CTEST_SITE
             \ CTEST_SOURCE_DIRECTORY
             \ CTEST_SUBMIT_INACTIVITY_TIMEOUT
@@ -2005,6 +2229,7 @@
             \ GHSMULTI
             \ IOS
             \ LIBRARY_OUTPUT_PATH
+            \ LINUX
             \ MINGW
             \ MSVC
             \ MSVC10
@@ -2023,6 +2248,7 @@
             \ PROJECT_BINARY_DIR
             \ PROJECT_DESCRIPTION
             \ PROJECT_HOMEPAGE_URL
+            \ PROJECT_IS_TOP_LEVEL
             \ PROJECT_NAME
             \ PROJECT_SOURCE_DIR
             \ PROJECT_VERSION
@@ -2040,6 +2266,7 @@
 
 syn keyword cmakeModule contained
             \ ExternalProject
+            \ FetchContent
 
 syn keyword cmakeKWExternalProject contained
             \ AWS
@@ -2053,10 +2280,10 @@
             \ CMAKE_CACHE_ARGS
             \ CMAKE_CACHE_DEFAULT_ARGS
             \ CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY
-            \ CMAKE_TLS_CAINFO
-            \ CMAKE_TLS_VERIFY
+            \ CMAKE_INSTALL_MODE
             \ COMMENT
             \ CONFIGURE_COMMAND
+            \ CONFIGURE_HANDLED_BY_BUILD
             \ CVS
             \ CVSROOT
             \ CVS_MODULE
@@ -2068,6 +2295,7 @@
             \ DOWNLOADED_FILE
             \ DOWNLOAD_COMMAND
             \ DOWNLOAD_DIR
+            \ DOWNLOAD_EXTRACT_TIMESTAMP
             \ DOWNLOAD_NAME
             \ DOWNLOAD_NO_EXTRACT
             \ DOWNLOAD_NO_PROGRESS
@@ -2159,24 +2387,90 @@
             \ USES_TERMINAL_UPDATE
             \ WORKING_DIRECTORY
 
+syn keyword cmakeKWFetchContent contained
+            \ ALWAYS
+            \ BINARY_DIR
+            \ BUILD_COMMAND
+            \ BYPASS_PROVIDER
+            \ CMAKE_PROJECT_
+            \ CONFIGURE_COMMAND
+            \ COPY
+            \ CORRECT
+            \ DCMAKE_TOOLCHAIN_FILE
+            \ DESTINATION
+            \ DOWNLOAD_NO_EXTRACT
+            \ EXISTS
+            \ FETCHCONTENT_BASE_DIR
+            \ FETCHCONTENT_FULLY_DISCONNECTED
+            \ FETCHCONTENT_MAKEAVAILABLE_SERIAL
+            \ FETCHCONTENT_QUIET
+            \ FETCHCONTENT_SOURCE_DIR_
+            \ FETCHCONTENT_TRY_FIND_PACKAGE_MODE
+            \ FETCHCONTENT_UPDATES_DISCONNECTED
+            \ FETCHCONTENT_UPDATES_DISCONNECTED_
+            \ FIND_PACKAGE_ARGS
+            \ GIT_REPOSITORY
+            \ GIT_TAG
+            \ GLOBAL
+            \ GTEST_BOTH_LIBRARIES
+            \ GTEST_LIBRARIES
+            \ GTEST_MAIN_LIBRARIES
+            \ INSTALL_COMMAND
+            \ INTERNAL
+            \ NAME
+            \ NAMES
+            \ NEVER
+            \ NOTE
+            \ OFF
+            \ OPTIONAL
+            \ OPT_IN
+            \ OVERRIDE_FIND_PACKAGE
+            \ PACKAGE_VERSION_COMPATIBLE
+            \ PACKAGE_VERSION_EXACT
+            \ QUIET
+            \ SOURCE_SUBDIR
+            \ STREQUAL
+            \ SUBBUILD_DIR
+            \ SVN_REPOSITORY
+            \ SVN_REVISION
+            \ SYSTEM
+            \ TARGET
+            \ TEST_COMMAND
+            \ TRUE
+            \ URL
+            \ URL_HASH
+            \ VERIFY_INTERFACE_HEADER_SETS
+            \ WRITE
+            \ WRONG
+            \ _BINARY_DIR
+            \ _INCLUDE
+            \ _POPULATED
+            \ _SOURCE_DIR
+
 syn keyword cmakeKWadd_compile_definitions contained
             \ COMPILE_DEFINITIONS
             \ VAR
 
 syn keyword cmakeKWadd_compile_options contained
+            \ CMAKE_
+            \ COMPILE_LANGUAGE
             \ COMPILE_OPTIONS
+            \ CONFIG
             \ SHELL
             \ UNIX_COMMAND
-            \ WX
+            \ _FLAGS
+            \ _FLAGS_
 
 syn keyword cmakeKWadd_custom_command contained
             \ APPEND
             \ ARGS
+            \ BNF
             \ BYPRODUCTS
             \ CC
             \ COMMAND
             \ COMMAND_EXPAND_LISTS
             \ COMMENT
+            \ CONFIG
             \ CROSSCOMPILING_EMULATOR
             \ DEPENDS
             \ DEPENDS_EXPLICIT_ONLY
@@ -2188,6 +2482,7 @@
             \ JOB_POOLS
             \ JOIN
             \ MAIN_DEPENDENCY
+            \ MODULE
             \ NOT
             \ OUTPUT
             \ PATH
@@ -2263,17 +2558,21 @@
             \ FRAMEWORK
             \ GLOBAL
             \ HEADER_FILE_ONLY
+            \ HEADER_SETS
             \ IMPORTED
             \ IMPORTED_
             \ IMPORTED_IMPLIB
             \ IMPORTED_IMPLIB_
             \ IMPORTED_LOCATION
             \ IMPORTED_LOCATION_
+            \ IMPORTED_NO_SONAME
             \ IMPORTED_OBJECTS
             \ IMPORTED_OBJECTS_
+            \ IMPORTED_SONAME
             \ INTERFACE
             \ INTERFACE_
             \ INTERFACE_SOURCES
+            \ LC_ID_DYLIB
             \ LIBRARY_OUTPUT_DIRECTORY
             \ MODULE
             \ OBJECT
@@ -2283,22 +2582,25 @@
             \ POST_BUILD
             \ PRE_BUILD
             \ PRE_LINK
-            \ PRIVATE
-            \ PUBLIC
+            \ PRIVATE_HEADER
+            \ PUBLIC_HEADER
             \ RUNTIME_OUTPUT_DIRECTORY
             \ SHARED
+            \ SONAME
             \ SOURCES
             \ STATIC
+            \ TARGETS
             \ TARGET_OBJECTS
+            \ TARGET_RUNTIME_DLLS
             \ UNKNOWN
 
 syn keyword cmakeKWadd_link_options contained
             \ CMAKE_
+            \ CONFIG
             \ CUDA_RESOLVE_DEVICE_SYMBOLS
             \ CUDA_SEPARABLE_COMPILATION
             \ DEVICE_LINK
             \ GCC
-            \ GNU
             \ HOST_LINK
             \ LANG
             \ LINKER
@@ -2306,11 +2608,14 @@
             \ SHELL
             \ STATIC_LIBRARY_OPTIONS
             \ UNIX_COMMAND
+            \ _FLAGS
+            \ _FLAGS_
             \ _LINKER_WRAPPER_FLAG
             \ _LINKER_WRAPPER_FLAG_SEP
 
 syn keyword cmakeKWadd_subdirectory contained
             \ EXCLUDE_FROM_ALL
+            \ SYSTEM
 
 syn keyword cmakeKWadd_test contained
             \ BUILD_TESTING
@@ -2324,15 +2629,64 @@
             \ SKIP_REGULAR_EXPRESSION
             \ TARGET_FILE
             \ WILL_FAIL
+            \ WILL_FALL
             \ WORKING_DIRECTORY
 
+syn keyword cmakeKWblock contained
+            \ PARENT_SCOPE
+            \ POLICIES
+            \ PROPAGATE
+            \ PUSH
+            \ SCOPE_FOR
+            \ TRUE
+            \ VARIABLES
+
 syn keyword cmakeKWbuild_command contained
             \ CONFIGURATION
+            \ PARALLEL_LEVEL
             \ TARGET
 
+syn keyword cmakeKWcmake_file_api contained
+            \ API
+            \ API_VERSION
+            \ BUILD_DIR
+            \ CMAKEFILES
+            \ CODEMODEL
+            \ COMMAND
+            \ CONFIG
+            \ QUERY
+            \ TOOLCHAINS
+
 syn keyword cmakeKWcmake_host_system_information contained
+            \ APPEND
             \ AVAILABLE_PHYSICAL_MEMORY
             \ AVAILABLE_VIRTUAL_MEMORY
+            \ BOTH
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_CONTENT
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_ID
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_NAME
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_PRETTY_NAME
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_RESULT_VERSION_ID
+            \ CMAKE_GET_OS_RELEASE_FALLBACK_SCRIPTS
+            \ DISTRIB_INFO
+            \ DISTRIB_PRETTY_NAME
+            \ DISTRO
+            \ DISTRO_BUG_REPORT_URL
+            \ DISTRO_HOME_URL
+            \ DISTRO_ID
+            \ DISTRO_ID_LIKE
+            \ DISTRO_NAME
+            \ DISTRO_PRETTY_NAME
+            \ DISTRO_PRIVACY_POLICY_URL
+            \ DISTRO_SUPPORT_URL
+            \ DISTRO_UBUNTU_CODENAME
+            \ DISTRO_VERSION
+            \ DISTRO_VERSION_CODENAME
+            \ DISTRO_VERSION_ID
+            \ ERROR_VARIABLE
+            \ EXISTS
             \ FQDN
             \ HAS_FPU
             \ HAS_MMX
@@ -2341,38 +2695,101 @@
             \ HAS_SSE
             \ HAS_SSE_FP
             \ HAS_SSE_MMX
+            \ HKCC
+            \ HKCR
+            \ HKCU
+            \ HKEY_CLASSES_ROOT
+            \ HKEY_CURRENT_CONFIG
+            \ HKEY_CURRENT_USER
+            \ HKEY_LOCAL_MACHINE
+            \ HKEY_USERS
+            \ HKLM
+            \ HKU
             \ HOSTNAME
             \ ID
+            \ LIMIT_COUNT
+            \ LISTS
+            \ LTS
+            \ MATCHES
+            \ NNN
+            \ NOT
             \ NUMBER_OF_LOGICAL_CORES
             \ NUMBER_OF_PHYSICAL_CORES
             \ OS_NAME
             \ OS_PLATFORM
             \ OS_RELEASE
             \ OS_VERSION
+            \ PRETTY_NAME
             \ PROCESSOR_DESCRIPTION
             \ PROCESSOR_NAME
             \ PROCESSOR_SERIAL_NUMBER
             \ QUERY
+            \ REG_DWORD
+            \ REG_EXPAND_SZ
+            \ REG_MULTI_SZ
+            \ REG_QWORD
+            \ REG_SZ
             \ RESULT
+            \ SEPARATOR
+            \ SOFTWARE
+            \ STATUS
+            \ STRINGS
+            \ SUBKEYS
+            \ TARGET
             \ TOTAL_PHYSICAL_MEMORY
             \ TOTAL_VIRTUAL_MEMORY
+            \ VALUE_NAMES
+            \ VAR
+            \ VIEW
+            \ WINDOWS_REGISTRY
 
 syn keyword cmakeKWcmake_language contained
             \ AND
+            \ ANY
+            \ APPEND
+            \ ARGN
+            \ BINARY_DIR
+            \ BYPASS_PROVIDER
             \ CALL
             \ CANCEL_CALL
             \ CODE
+            \ COMMAND
+            \ COMMAND_ERROR_IS_FATAL
+            \ DCMAKE_PROJECT_TOP_LEVEL_INCLUDES
             \ DEFER
             \ DIRECTORY
             \ EVAL
             \ FALSE
+            \ FETCHCONTENT_MAKEAVAILABE_SERIAL
+            \ FETCHCONTENT_MAKEAVAILABLE_SERIAL
+            \ FETCHCONTENT_SOURCE_DIR_
+            \ FETCHCONTENT_TRY_FIND_PACKAGE_MODE
+            \ FIND_PACKAGE
+            \ FIND_PACKAGE_ARGS
             \ GET_CALL_IDS
-            \ ID
+            \ GET_MESSAGE_LOG_LEVEL
+            \ GIT_REPOSITORY
+            \ GIT_SUBMODULES
+            \ GIT_TAG
             \ ID_VAR
-            \ OR
+            \ MATCHES
+            \ MYCOMP_PROVIDER_INSTALL_DIR
+            \ NEVER
+            \ NOT
+            \ OVERRIDE_FIND_PACKAGE
+            \ PATH
+            \ POP_BACK
+            \ QUIET
+            \ SET_DEPENDENCY_PROVIDER
+            \ SOURCE_DIR
             \ STATUS
+            \ STREQUAL
+            \ SUPPORTED_METHODS
             \ TRUE
+            \ VERSION
             \ WRITE
+            \ _FOUND
+            \ _PATH
 
 syn keyword cmakeKWcmake_minimum_required contained
             \ FATAL_ERROR
@@ -2407,22 +2824,21 @@
             \ ABSOLUTE_PATH
             \ AND
             \ APPEND
+            \ APPEND_STRING
             \ BASE_DIRECTORY
-            \ CMAKE_PATH
             \ COMPARE
-            \ CONCAT
             \ CONVERT
-            \ ELSEIF
-            \ ENDIF
+            \ EQUAL
             \ EXTENSION
             \ EXTENSION_DEF
             \ FALSE
+            \ FILENAME
             \ FILENAME_DEF
             \ GET
             \ GET_EXTENSION
             \ GET_FILENAME
             \ GET_PARENT_PATH
-            \ GET_RELATIVE_PATH
+            \ GET_RELATIVE_PART
             \ GET_ROOT_DIRECTORY
             \ GET_ROOT_NAME
             \ GET_ROOT_PATH
@@ -2431,12 +2847,11 @@
             \ HAS_EXTENSION
             \ HAS_FILENAME
             \ HAS_PARENT_PATH
-            \ HAS_RELATIVE_PATH
+            \ HAS_RELATIVE_PART
             \ HAS_ROOT_DIRECTORY
             \ HAS_ROOT_NAME
             \ HAS_ROOT_PATH
             \ HAS_STEM
-            \ IF
             \ IS_ABSOLUTE
             \ IS_PREFIX
             \ IS_RELATIVE
@@ -2445,23 +2860,21 @@
             \ NATIVE_PATH
             \ NORMALIZE
             \ NORMAL_PATH
-            \ NOT
             \ NOT_EQUAL
             \ OP
-            \ OS
             \ OUTPUT_VARIABLE
             \ PARENT_PATH
-            \ PROXIMATE_PATH
             \ REAL_PATH
+            \ RELATIVE_PART
             \ RELATIVE_PATH
             \ REMOVE_EXTENSION
             \ REMOVE_FILENAME
             \ REPLACE_EXTENSION
             \ REPLACE_FILENAME
-            \ RETURN
             \ ROOT_DIRECTORY
             \ ROOT_NAME
             \ ROOT_PATH
+            \ SET
             \ STEM
             \ STREQUAL
             \ TO_CMAKE_PATH_LIST
@@ -2476,8 +2889,10 @@
             \ NNNN
             \ NO_POLICY_SCOPE
             \ OLD
+            \ POLICIES
             \ POP
             \ PUSH
+            \ SCOPE_FOR
             \ SET
             \ VERSION
 
@@ -2489,9 +2904,14 @@
             \ FILE_PERMISSIONS
             \ FOO_ENABLE
             \ FOO_STRING
+            \ GENERATE
+            \ INTERFACE
             \ LF
             \ NEWLINE_STYLE
             \ NO_SOURCE_PERMISSIONS
+            \ PRIVATE
+            \ PUBLIC
+            \ SYSTEM
             \ USE_SOURCE_PERMISSIONS
             \ VAR
 
@@ -2506,6 +2926,7 @@
             \ APPEND
             \ BUILD
             \ CAPTURE_CMAKE_ERROR
+            \ CMAKE_BUILD_PARALLEL_LEVEL
             \ CONFIGURATION
             \ CTEST_BUILD_CONFIGURATION
             \ CTEST_BUILD_FLAGS
@@ -2513,6 +2934,7 @@
             \ FLAGS
             \ NUMBER_ERRORS
             \ NUMBER_WARNINGS
+            \ PARALLEL_LEVEL
             \ QUIET
             \ RETURN_VALUE
             \ TARGET
@@ -2537,6 +2959,7 @@
 syn keyword cmakeKWctest_memcheck contained
             \ APPEND
             \ BUILD
+            \ CAPTURE_CMAKE_ERROR
             \ DEFECT_COUNT
             \ EXCLUDE
             \ EXCLUDE_FIXTURE
@@ -2547,11 +2970,15 @@
             \ INCLUDE_LABEL
             \ OFF
             \ ON
+            \ OUTPUT_JUNIT
             \ PARALLEL_LEVEL
             \ QUIET
+            \ REPEAT
+            \ RESOURCE_SPEC_FILE
             \ RETURN_VALUE
             \ SCHEDULE_RANDOM
             \ START
+            \ STOP_ON_FAILURE
             \ STOP_TIME
             \ STRIDE
             \ TEST_LOAD
@@ -2587,6 +3014,8 @@
 syn keyword cmakeKWctest_test contained
             \ AFTER_TIMEOUT
             \ APPEND
+            \ ATTACHED_FILES
+            \ ATTACHED_FILES_ON_FAIL
             \ BUILD
             \ CAPTURE_CMAKE_ERROR
             \ CPU
@@ -2597,8 +3026,10 @@
             \ EXCLUDE_LABEL
             \ INCLUDE
             \ INCLUDE_LABEL
+            \ LABELS
             \ OFF
             \ ON
+            \ OUTPUT_JUNIT
             \ PARALLEL_LEVEL
             \ QUIET
             \ REPEAT
@@ -2612,6 +3043,8 @@
             \ TEST_LOAD
             \ UNTIL_FAIL
             \ UNTIL_PASS
+            \ URL
+            \ XML
 
 syn keyword cmakeKWctest_update contained
             \ CAPTURE_CMAKE_ERROR
@@ -2629,15 +3062,18 @@
             \ APPEND_STRING
             \ BRIEF_DOCS
             \ CACHED_VARIABLE
+            \ CMAKE_
             \ DIRECTORY
             \ FULL_DOCS
             \ GLOBAL
             \ INHERITED
+            \ INITIALIZE_FROM_VARIABLE
             \ PROPERTY
             \ SOURCE
             \ TARGET
             \ TEST
             \ VARIABLE
+            \ _CMAKE_
 
 syn keyword cmakeKWdoxygen_add_docs contained
             \ ALL
@@ -2647,6 +3083,10 @@
 
 syn keyword cmakeKWenable_language contained
             \ ASM
+            \ ASM_MARMASM
+            \ ASM_MASM
+            \ ASM_NASM
+            \ ATT
             \ CUDA
             \ HIP
             \ ISPC
@@ -2684,6 +3124,7 @@
             \ OUTPUT_QUIET
             \ OUTPUT_STRIP_TRAILING_WHITESPACE
             \ OUTPUT_VARIABLE
+            \ POSIX
             \ RESULTS_VARIABLE
             \ RESULT_VARIABLE
             \ RFC
@@ -2691,17 +3132,16 @@
             \ STDOUT
             \ TIMEOUT
             \ UTF
-            \ VERBATIM
             \ WORKING_DIRECTORY
 
 syn keyword cmakeKWexport contained
             \ ANDROID_MK
             \ APPEND
             \ CONFIG
+            \ CXX_MODULES_DIRECTORY
             \ EXPORT
             \ EXPORT_LINK_INTERFACE_LIBRARIES
             \ FILE
-            \ IMPORTED
             \ IMPORTED_
             \ NAMESPACE
             \ NDK
@@ -2717,7 +3157,6 @@
             \ SET
 
 syn keyword cmakeKWfile contained
-            \ ALGO
             \ APPEND
             \ ARCHIVE_CREATE
             \ ARCHIVE_EXTRACT
@@ -2729,9 +3168,8 @@
             \ CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND
             \ CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM
             \ CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL
+            \ CMAKE_INSTALL_MODE
             \ CMAKE_OBJDUMP
-            \ CMAKE_TLS_CAINFO
-            \ CMAKE_TLS_VERIFY
             \ CODE
             \ COMPILE_FEATURES
             \ COMPRESSION
@@ -2742,7 +3180,8 @@
             \ CONFLICTING_DEPENDENCIES_PREFIX
             \ CONTENT
             \ CONVERT
-            \ COPY
+            \ COPYONLY
+            \ COPY_FILE
             \ COPY_ON_ERROR
             \ CREATE_LINK
             \ CRLF
@@ -2755,6 +3194,7 @@
             \ ENCODING
             \ ESCAPE_QUOTES
             \ EXECUTABLES
+            \ EXPAND_TILDE
             \ EXPECTED_HASH
             \ FILES_MATCHING
             \ FILE_PERMISSIONS
@@ -2772,10 +3212,12 @@
             \ GUARD
             \ HASH
             \ HEX
+            \ HOME
             \ HTTPHEADER
             \ IGNORED
             \ INACTIVITY_TIMEOUT
             \ INPUT
+            \ INPUT_MAY_BE_RECENT
             \ INSTALL
             \ IS_ABSOLUTE
             \ LENGTH_MAXIMUM
@@ -2800,9 +3242,11 @@
             \ NEWLINE_STYLE
             \ NOT
             \ NO_HEX_CONVERSION
+            \ NO_REPLACE
             \ NO_SOURCE_PERMISSIONS
             \ OFFSET
             \ ONLY
+            \ ONLY_IF_DIFFERENT
             \ OPTIONAL
             \ OUTPUT
             \ OWNER_EXECUTE
@@ -2812,11 +3256,15 @@
             \ PATTERN
             \ PATTERNS
             \ PERMISSIONS
+            \ POST_EXCLUDE_FILES
             \ POST_EXCLUDE_REGEXES
+            \ POST_INCLUDE_FILES
             \ POST_INCLUDE_REGEXES
             \ PRE_EXCLUDE_REGEXES
             \ PRE_INCLUDE_REGEXES
             \ PROCESS
+            \ RANGE_END
+            \ RANGE_START
             \ READ
             \ READ_SYMLINK
             \ REAL_PATH
@@ -2833,7 +3281,10 @@
             \ RESULT_VARIABLE
             \ RPATH
             \ RUNPATH
+            \ RUNTIME_DEPENDENCY_SET
             \ SCRIPT
+            \ SETGID
+            \ SETUID
             \ SHARED
             \ SHOW_PROGRESS
             \ SIZE
@@ -2856,6 +3307,7 @@
             \ UNRESOLVED_DEPENDENCIES_VAR
             \ UPLOAD
             \ URL
+            \ USERPROFILE
             \ USERPWD
             \ USE_SOURCE_PERMISSIONS
             \ UTC
@@ -2869,54 +3321,87 @@
             \ _FILENAMES
 
 syn keyword cmakeKWfind_file contained
+            \ BOTH
+            \ CATEGORY
             \ CMAKE_FIND_ROOT_PATH_BOTH
+            \ CMAKE_FIND_USE_
             \ DOC
             \ DVAR
             \ FALSE
+            \ FIND_XXX_REGISTRY_VIEW
             \ HINTS
+            \ HOST
             \ INCLUDE
+            \ MATCHES
             \ NAMES
+            \ NOT
+            \ NO_CACHE
             \ NO_CMAKE_ENVIRONMENT_PATH
             \ NO_CMAKE_FIND_ROOT_PATH
+            \ NO_CMAKE_INSTALL_PREFIX
             \ NO_CMAKE_PATH
             \ NO_CMAKE_SYSTEM_PATH
             \ NO_DEFAULT_PATH
             \ NO_PACKAGE_ROOT_PATH
             \ NO_SYSTEM_ENVIRONMENT_PATH
             \ ONLY_CMAKE_FIND_ROOT_PATH
+            \ PACKAGENAME
+            \ PARENT_SCOPE
             \ PATHS
             \ PATH_SUFFIXES
+            \ REGISTRY_VIEW
             \ REQUIRED
+            \ TARGET
+            \ VALIDATOR
             \ VAR
 
 syn keyword cmakeKWfind_library contained
+            \ BOTH
+            \ CATEGORY
             \ CMAKE_FIND_ROOT_PATH_BOTH
+            \ CMAKE_FIND_USE_
             \ DOC
             \ DVAR
             \ FALSE
+            \ FIND_XXX_REGISTRY_VIEW
             \ HINTS
-            \ INCLUDE
+            \ HOST
+            \ LIB
+            \ MATCHES
             \ NAMES
             \ NAMES_PER_DIR
+            \ NOT
+            \ NO_CACHE
             \ NO_CMAKE_ENVIRONMENT_PATH
             \ NO_CMAKE_FIND_ROOT_PATH
+            \ NO_CMAKE_INSTALL_PREFIX
             \ NO_CMAKE_PATH
             \ NO_CMAKE_SYSTEM_PATH
             \ NO_DEFAULT_PATH
             \ NO_PACKAGE_ROOT_PATH
             \ NO_SYSTEM_ENVIRONMENT_PATH
             \ ONLY_CMAKE_FIND_ROOT_PATH
+            \ PACKAGENAME
+            \ PARENT_SCOPE
             \ PATHS
             \ PATH_SUFFIXES
+            \ REGISTRY_VIEW
             \ REQUIRED
+            \ TARGET
+            \ VALIDATOR
             \ VAR
 
 syn keyword cmakeKWfind_package contained
             \ ABI
+            \ BOTH
             \ BUNDLE
+            \ BYPASS_PROVIDER
+            \ CATEGORY
             \ CMAKE_DISABLE_FIND_PACKAGE_
             \ CMAKE_REQUIRE_FIND_PACKAGE_
             \ CMAKE_FIND_ROOT_PATH_BOTH
+            \ CMAKE_FIND_USE_
+            \ CMAKE_REQUIRE_FIND_PACKAGE_
             \ COMPONENTS
             \ CONFIG
             \ CONFIGS
@@ -2927,7 +3412,9 @@
             \ FALSE
             \ FIND_PACKAGE_VERSION_FORMAT
             \ FRAMEWORK
+            \ GLOBAL
             \ HINTS
+            \ HOST
             \ INCLUDE
             \ MODULE
             \ NAMES
@@ -2935,6 +3422,7 @@
             \ NO_CMAKE_BUILDS_PATH
             \ NO_CMAKE_ENVIRONMENT_PATH
             \ NO_CMAKE_FIND_ROOT_PATH
+            \ NO_CMAKE_INSTALL_PREFIX
             \ NO_CMAKE_PACKAGE_REGISTRY
             \ NO_CMAKE_PATH
             \ NO_CMAKE_SYSTEM_PACKAGE_REGISTRY
@@ -2944,8 +3432,10 @@
             \ NO_PACKAGE_ROOT_PATH
             \ NO_POLICY_SCOPE
             \ NO_SYSTEM_ENVIRONMENT_PATH
+            \ OLD
             \ ONLY_CMAKE_FIND_ROOT_PATH
             \ OPTIONAL_COMPONENTS
+            \ PACKAGENAME
             \ PACKAGE_FIND_NAME
             \ PACKAGE_FIND_VERSION
             \ PACKAGE_FIND_VERSION_COMPLETE
@@ -2974,60 +3464,92 @@
             \ PATHS
             \ PATH_SUFFIXES
             \ QUIET
+            \ REGISTRY_VIEW
             \ REQUIRED
             \ SET
+            \ TARGET
             \ TRUE
+            \ VALUE
             \ _CONFIG
             \ _CONSIDERED_CONFIGS
             \ _CONSIDERED_VERSIONS
             \ _DIR
             \ _FIND_COMPONENTS
             \ _FIND_QUIETLY
+            \ _FIND_REGISTRY_VIEW
             \ _FIND_REQUIRED
             \ _FIND_REQUIRED_
             \ _FIND_VERSION_EXACT
             \ _FOUND
 
 syn keyword cmakeKWfind_path contained
+            \ BOTH
+            \ CATEGORY
             \ CMAKE_FIND_ROOT_PATH_BOTH
+            \ CMAKE_FIND_USE_
             \ DOC
             \ DVAR
             \ FALSE
+            \ FIND_XXX_REGISTRY_VIEW
             \ HINTS
+            \ HOST
             \ INCLUDE
+            \ MATCHES
             \ NAMES
+            \ NOT
+            \ NO_CACHE
             \ NO_CMAKE_ENVIRONMENT_PATH
             \ NO_CMAKE_FIND_ROOT_PATH
+            \ NO_CMAKE_INSTALL_PREFIX
             \ NO_CMAKE_PATH
             \ NO_CMAKE_SYSTEM_PATH
             \ NO_DEFAULT_PATH
             \ NO_PACKAGE_ROOT_PATH
             \ NO_SYSTEM_ENVIRONMENT_PATH
             \ ONLY_CMAKE_FIND_ROOT_PATH
+            \ PACKAGENAME
+            \ PARENT_SCOPE
             \ PATHS
             \ PATH_SUFFIXES
+            \ REGISTRY_VIEW
             \ REQUIRED
+            \ TARGET
+            \ VALIDATOR
             \ VAR
 
 syn keyword cmakeKWfind_program contained
+            \ BOTH
+            \ CATEGORY
             \ CMAKE_FIND_ROOT_PATH_BOTH
+            \ CMAKE_FIND_USE_
             \ DOC
             \ DVAR
             \ FALSE
+            \ FIND_XXX_REGISTRY_VIEW
             \ HINTS
+            \ HOST
+            \ MATCHES
             \ NAMES
             \ NAMES_PER_DIR
+            \ NOT
+            \ NO_CACHE
             \ NO_CMAKE_ENVIRONMENT_PATH
             \ NO_CMAKE_FIND_ROOT_PATH
+            \ NO_CMAKE_INSTALL_PREFIX
             \ NO_CMAKE_PATH
             \ NO_CMAKE_SYSTEM_PATH
             \ NO_DEFAULT_PATH
             \ NO_PACKAGE_ROOT_PATH
             \ NO_SYSTEM_ENVIRONMENT_PATH
             \ ONLY_CMAKE_FIND_ROOT_PATH
+            \ PACKAGENAME
+            \ PARENT_SCOPE
             \ PATHS
             \ PATH_SUFFIXES
+            \ REGISTRY_VIEW
             \ REQUIRED
+            \ TARGET
+            \ VALIDATOR
             \ VAR
 
 syn keyword cmakeKWfltk_wrap_ui contained
@@ -3070,17 +3592,19 @@
             \ NAME
             \ NAME_WE
             \ NAME_WLE
-            \ PATH
             \ PROGRAM
             \ PROGRAM_ARGS
+            \ QUERY
             \ REALPATH
             \ REAL_PATH
+            \ WINDOWS_REGISTRY
 
 syn keyword cmakeKWget_property contained
             \ BRIEF_DOCS
             \ DEFINED
             \ DIRECTORY
             \ FULL_DOCS
+            \ GENERATED
             \ GLOBAL
             \ INSTALL
             \ PROPERTY
@@ -3093,6 +3617,7 @@
 
 syn keyword cmakeKWget_source_file_property contained
             \ DIRECTORY
+            \ GENERATED
             \ INHERITED
             \ LOCATION
             \ TARGET_DIRECTORY
@@ -3109,6 +3634,7 @@
             \ CMAKE_MATCH_
             \ CMP
             \ COMMAND
+            \ COMPARE
             \ DEFINED
             \ EQUAL
             \ EXISTS
@@ -3128,6 +3654,7 @@
             \ NOT
             \ OFF
             \ OR
+            \ PATH_EQUAL
             \ POLICY
             \ STREQUAL
             \ STRGREATER
@@ -3172,11 +3699,13 @@
 syn keyword cmakeKWinstall contained
             \ AFTER
             \ AIX
+            \ ALL_COMPONENTS
             \ APT
             \ ARCHIVE
             \ BEFORE
             \ BUILD_TYPE
             \ BUNDLE
+            \ BUNDLE_EXECUTABLE
             \ CMAKE_INSTALL_BINDIR
             \ CMAKE_INSTALL_DATADIR
             \ CMAKE_INSTALL_DATAROOTDIR
@@ -3187,6 +3716,7 @@
             \ CMAKE_INSTALL_LOCALEDIR
             \ CMAKE_INSTALL_LOCALSTATEDIR
             \ CMAKE_INSTALL_MANDIR
+            \ CMAKE_INSTALL_MODE
             \ CMAKE_INSTALL_RUNSTATEDIR
             \ CMAKE_INSTALL_SBINDIR
             \ CMAKE_INSTALL_SHARESTATEDIR
@@ -3195,6 +3725,8 @@
             \ COMPONENT
             \ CONFIGURATIONS
             \ CVS
+            \ CXX_MODULES_BMI
+            \ CXX_MODULES_DIRECTORY
             \ DATA
             \ DATAROOT
             \ DBUILD_TYPE
@@ -3207,6 +3739,7 @@
             \ DOC
             \ ENABLE_EXPORTS
             \ EXCLUDE_FROM_ALL
+            \ EXECUTABLES
             \ EXPORT
             \ EXPORT_ANDROID_MK
             \ EXPORT_LINK_INTERFACE_LIBRARIES
@@ -3214,14 +3747,18 @@
             \ FILES
             \ FILES_MATCHING
             \ FILE_PERMISSIONS
+            \ FILE_SET
             \ FRAMEWORK
+            \ GET_RUNTIME_DEPENDENCIES
             \ GROUP_EXECUTE
             \ GROUP_READ
             \ GROUP_WRITE
-            \ IMPORTED_
+            \ HEADERS
+            \ IMPORTED_RUNTIME_ARTIFACTS
             \ INCLUDES
             \ INFO
             \ INSTALL_PREFIX
+            \ INTERFACE
             \ INTERFACE_INCLUDE_DIRECTORIES
             \ LIBRARY
             \ LOCALE
@@ -3241,18 +3778,24 @@
             \ OWNER_WRITE
             \ PATTERN
             \ PERMISSIONS
+            \ POST_EXCLUDE_FILES
+            \ POST_EXCLUDE_REGEXES
+            \ POST_INCLUDE_FILES
+            \ POST_INCLUDE_REGEXES
             \ POST_INSTALL_SCRIPT
+            \ PRE_EXCLUDE_REGEXES
+            \ PRE_INCLUDE_REGEXES
             \ PRE_INSTALL_SCRIPT
             \ PRIVATE_HEADER
             \ PROGRAMS
             \ PROPERTIES
             \ PUBLIC_HEADER
-            \ REGEX
             \ RENAME
             \ RESOURCE
             \ RPM
             \ RUNSTATE
-            \ RUNTIME
+            \ RUNTIME_DEPENDENCIES
+            \ RUNTIME_DEPENDENCY_SET
             \ SBIN
             \ SCRIPT
             \ SETGID
@@ -3333,6 +3876,11 @@
             \ TOLOWER
             \ TOUPPER
             \ TRANSFORM
+            \ TRANSFORM_APPEND
+            \ TRANSFORM_GENEX_STRIP
+            \ TRANSFORM_REPLACE
+            \ TRANSFORM_STRIP
+            \ TRANSFORM_TOLOWER
 
 syn keyword cmakeKWload_cache contained
             \ EXCLUDE
@@ -3370,10 +3918,15 @@
             \ CHECK_FAIL
             \ CHECK_PASS
             \ CHECK_START
+            \ CONFIGURE_LOG
             \ DEBUG
+            \ DEFINED
             \ DEPRECATION
             \ FATAL_ERROR
+            \ GET_MESSAGE_LOG_LEVEL
             \ GUI
+            \ INTERNAL
+            \ MY_CHECK_RESULT
             \ NOTICE
             \ POP_BACK
             \ SEND_ERROR
@@ -3384,10 +3937,13 @@
 
 syn keyword cmakeKWoption contained
             \ OFF
-            \ ON
 
 syn keyword cmakeKWproject contained
             \ ASM
+            \ ASM_MARMASM
+            \ ASM_MASM
+            \ ASM_NASM
+            \ ATT
             \ CMAKE_PROJECT_
             \ CUDA
             \ DESCRIPTION
@@ -3405,6 +3961,7 @@
             \ _DESCRIPTION
             \ _HOMEPAGE_URL
             \ _INCLUDE_BEFORE
+            \ _IS_TOP_LEVEL
             \ _SOURCE_DIR
             \ _VERSION
             \ _VERSION_MAJOR
@@ -3424,6 +3981,11 @@
 
 syn keyword cmakeKWreturn contained
             \ DEFER
+            \ PARENT_SCOPE
+            \ PROPAGATE
+            \ SCOPE_FOR
+            \ VARIABLES
+            \ VERSION
 
 syn keyword cmakeKWseparate_arguments contained
             \ MSDN
@@ -3439,10 +4001,13 @@
             \ FORCE
             \ INTERNAL
             \ OFF
+            \ OLD
             \ ON
             \ PARENT_SCOPE
+            \ PROPAGATE
             \ STRING
             \ STRINGS
+            \ VAR
 
 syn keyword cmakeKWset_directory_properties contained
             \ DIRECTORY
@@ -3452,9 +4017,11 @@
             \ APPEND
             \ APPEND_STRING
             \ DIRECTORY
+            \ GENERATED
             \ GLOBAL
             \ INHERITED
             \ INSTALL
+            \ NAME
             \ PROPERTY
             \ SOURCE
             \ TARGET
@@ -3464,17 +4031,17 @@
 
 syn keyword cmakeKWset_source_files_properties contained
             \ DIRECTORY
+            \ GENERATED
             \ PROPERTIES
             \ SOURCE
             \ TARGET_DIRECTORY
 
 syn keyword cmakeKWset_target_properties contained
             \ PROPERTIES
-            \ TARGET
 
 syn keyword cmakeKWset_tests_properties contained
+            \ NAME
             \ PROPERTIES
-            \ TEST
 
 syn keyword cmakeKWsite_name contained
             \ HOSTNAME
@@ -3506,9 +4073,9 @@
             \ GUID
             \ HASH
             \ HEX
+            \ ISO
             \ JOIN
             \ JSON
-            \ JSONLENGTH
             \ LENGTH
             \ LESS
             \ LESS_EQUAL
@@ -3573,7 +4140,10 @@
 syn keyword cmakeKWtarget_compile_options contained
             \ ALIAS
             \ BEFORE
+            \ CMAKE_
+            \ COMPILE_LANGUAGE
             \ COMPILE_OPTIONS
+            \ CONFIG
             \ IMPORTED
             \ INTERFACE
             \ INTERFACE_COMPILE_OPTIONS
@@ -3581,8 +4151,11 @@
             \ PUBLIC
             \ SHELL
             \ UNIX_COMMAND
+            \ _FLAGS
+            \ _FLAGS_
 
 syn keyword cmakeKWtarget_include_directories contained
+            \ AFTER
             \ ALIAS
             \ BEFORE
             \ BUILD_INTERFACE
@@ -3619,6 +4192,7 @@
             \ IMPORTED_NO_SONAME
             \ INTERFACE
             \ INTERFACE_LINK_LIBRARIES
+            \ LINK_FLAGS
             \ LINK_INTERFACE_LIBRARIES
             \ LINK_INTERFACE_LIBRARIES_DEBUG
             \ LINK_INTERFACE_MULTIPLICITY
@@ -3631,16 +4205,17 @@
             \ PUBLIC
             \ SHARED
             \ STATIC
+            \ TARGET_OBJECTS
 
 syn keyword cmakeKWtarget_link_options contained
             \ ALIAS
             \ BEFORE
             \ CMAKE_
+            \ CONFIG
             \ CUDA_RESOLVE_DEVICE_SYMBOLS
             \ CUDA_SEPARABLE_COMPILATION
             \ DEVICE_LINK
             \ GCC
-            \ GNU
             \ HOST_LINK
             \ IMPORTED
             \ INTERFACE
@@ -3653,6 +4228,8 @@
             \ SHELL
             \ STATIC_LIBRARY_OPTIONS
             \ UNIX_COMMAND
+            \ _FLAGS
+            \ _FLAGS_
             \ _LINKER_WRAPPER_FLAG
             \ _LINKER_WRAPPER_FLAG_SEP
 
@@ -3677,15 +4254,45 @@
 
 syn keyword cmakeKWtarget_sources contained
             \ ALIAS
+            \ BASE_DIRS
+            \ BUILD_INTERFACE
+            \ CONFIG
+            \ CORRECT
+            \ CXX_MODULES
+            \ CXX_MODULE_DIRS
+            \ CXX_MODULE_DIRS_
+            \ CXX_MODULE_SETS
+            \ CXX_MODULE_SET_
+            \ EXPORT
+            \ FILES
+            \ FILE_SET
+            \ FRAMEWORK
+            \ HEADERS
+            \ HEADER_DIRS
+            \ HEADER_DIRS_
+            \ HEADER_FILE_ONLY
+            \ HEADER_SETS
+            \ HEADER_SET_
             \ IMPORTED
+            \ INCLUDE_DIRECTORIES
             \ INTERFACE
+            \ INTERFACE_CXX_MODULE_SETS
+            \ INTERFACE_HEADER_SETS
+            \ INTERFACE_INCLUDE_DIRECTORIES
             \ INTERFACE_SOURCES
+            \ NAME
             \ PRIVATE
             \ PUBLIC
             \ SOURCES
+            \ SOURCE_DIR
+            \ TARGETS
+            \ TRUE
+            \ TYPE
+            \ WRONG
 
 syn keyword cmakeKWtry_compile contained
             \ ALL_BUILD
+            \ BINARY_DIR
             \ CMAKE_FLAGS
             \ COMPILE_DEFINITIONS
             \ COPY_FILE
@@ -3713,8 +4320,11 @@
             \ LINK_DIRECTORIES
             \ LINK_LIBRARIES
             \ LINK_OPTIONS
+            \ LOG_DESCRIPTION
             \ MULTI
             \ NOT
+            \ NO_CACHE
+            \ NO_LOG
             \ OBJCXX_EXTENSIONS
             \ OBJCXX_STANDARD
             \ OBJCXX_STANDARD_REQUIRED
@@ -3723,9 +4333,16 @@
             \ OBJC_STANDARD_REQUIRED
             \ OUTPUT_VARIABLE
             \ PRIVATE
+            \ PROJECT
+            \ RESULTVAR
             \ SOURCES
+            \ SOURCE_DIR
+            \ SOURCE_FROM_CONTENT
+            \ SOURCE_FROM_FILE
+            \ SOURCE_FROM_VAR
             \ STATIC_LIBRARY
             \ STATIC_LIBRARY_OPTIONS
+            \ TARGET
             \ TRUE
             \ TYPE
             \ VALUE
@@ -3738,18 +4355,28 @@
             \ CMAKE_FLAGS
             \ COMPILE_DEFINITIONS
             \ COMPILE_OUTPUT_VARIABLE
-            \ DLINK_LIBRARIES
-            \ DVAR
+            \ COPY_FILE
+            \ COPY_FILE_ERROR
             \ FAILED_TO_RUN
             \ FALSE
-            \ INCLUDE_DIRECTORIES
-            \ LINK_DIRECTORIES
+            \ LANG
             \ LINK_LIBRARIES
             \ LINK_OPTIONS
+            \ LOG_DESCRIPTION
+            \ NO_CACHE
+            \ NO_LOG
+            \ RUN_OUTPUT_STDERR_VARIABLE
+            \ RUN_OUTPUT_STDOUT_VARIABLE
             \ RUN_OUTPUT_VARIABLE
+            \ SOURCES
+            \ SOURCE_FROM_CONTENT
+            \ SOURCE_FROM_FILE
+            \ SOURCE_FROM_VAR
             \ TRUE
-            \ TYPE
-            \ VALUE
+            \ WORKING_DIRECTORY
+            \ _EXTENSIONS
+            \ _STANDARD
+            \ _STANDARD_REQUIRED
             \ __TRYRUN_OUTPUT
 
 syn keyword cmakeKWunset contained
@@ -3783,26 +4410,38 @@
 
 
 syn keyword cmakeGeneratorExpressions contained
-            \ AND
+            \ ABSOLUTE_PATH
+            \ ACTION
+            \ AIX
             \ ANGLE
+            \ APPEND
             \ ARCHIVE_OUTPUT_NAME
             \ ARCHIVE_OUTPUT_NAME_
+            \ ASCENDING
             \ BAR
             \ BOOL
             \ BUILD_INTERFACE
-            \ CMAKE_
-            \ COMMA
-            \ COMMAND
+            \ BUILD_LOCAL_INTERFACE
+            \ CMAKE_LINK_GROUP_USING_
+            \ CMAKE_LINK_LIBRARY_USING_
+            \ CMAKE_PATH
+            \ CODE
+            \ COMMAND_CONFIG
+            \ COMMAND_EXPAND_LISTS
+            \ COMPARE
             \ COMPILE_DEFINITIONS
             \ COMPILE_FEATURES
             \ COMPILE_LANGUAGE
             \ COMPILE_LANG_AND_ID
+            \ COMPILE_ONLY
             \ COMPILING_CUDA
+            \ COMPILING_CXX
             \ COMPILING_CXX_WITH_CLANG
             \ COMPILING_CXX_WITH_INTEL
             \ COMPILING_C_WITH_CLANG
             \ CONFIG
             \ CONFIGURATION
+            \ CONTENT
             \ CUDA_COMPILER_ID
             \ CUDA_COMPILER_VERSION
             \ CUDA_RESOLVE_DEVICE_SYMBOLS
@@ -3817,71 +4456,146 @@
             \ C_STANDARD
             \ DEBUG_MODE
             \ DEBUG_POSTFIX
+            \ DENABLE_SOME_FEATURE
+            \ DESCENDING
             \ DEVICE_LINK
             \ DLL
+            \ ENABLE_EXPORTS
             \ EXCLUDE
             \ EXPORT
+            \ EXTENSION_DEF
             \ FALSE
+            \ FILENAME_DEF
+            \ FILE_BASENAME
             \ FILTER
+            \ FIND
             \ FOO_EXTRA_THINGS
             \ GENERATE
             \ GENEX_EVAL
-            \ GNU
+            \ GET_EXTENSION
+            \ GET_FILENAME
+            \ GET_PARENT_PATH
+            \ GET_RELATIVE_PART
+            \ GET_ROOT_DIRECTORY
+            \ GET_ROOT_NAME
+            \ GET_ROOT_PATH
+            \ GET_STEM
+            \ HAS_
+            \ HAS_EXTENSION
+            \ HAS_FILENAME
+            \ HAS_PARENT_PATH
+            \ HAS_RELATIVE_PART
+            \ HAS_ROOT_DIRECTORY
+            \ HAS_ROOT_NAME
+            \ HAS_ROOT_PATH
+            \ HAS_STEM
+            \ HAVE_SOME_FEATURE
             \ HIP_COMPILER_ID
             \ HIP_COMPILER_VERSION
             \ HIP_STANDARD
             \ HOST_LINK
             \ IF
             \ IGNORE
+            \ IMPORTED_LOCATION
             \ IMPORT_PREFIX
             \ IMPORT_SUFFIX
             \ INCLUDE_DIRECTORIES
+            \ INSENSITIVE
+            \ INSERT
             \ INSTALL_INTERFACE
             \ INSTALL_NAME_DIR
             \ INSTALL_PREFIX
-            \ INTERFACE
+            \ INSTALL_RPATH
             \ INTERFACE_LINK_LIBRARIES
+            \ INTERFACE_LINK_LIBRARIES_DIRECT
             \ IN_LIST
             \ ISPC_COMPILER_ID
             \ ISPC_COMPILER_VERSION
+            \ IS_ABSOLUTE
+            \ IS_PREFIX
+            \ IS_RELATIVE
             \ JOIN
             \ LANG
             \ LANG_COMPILER_ID
+            \ LAST_ONLY
+            \ LENGTH
             \ LIBRARY_OUTPUT_NAME
             \ LIBRARY_OUTPUT_NAME_
+            \ LINK_GROUP
+            \ LINK_GROUP_PREDEFINED_FEATURES
             \ LINK_LANGUAGE
             \ LINK_LANG_AND_ID
             \ LINK_LIBRARIES
+            \ LINK_LIBRARY
+            \ LINK_LIBRARY_OVERRIDE
+            \ LINK_LIBRARY_OVERRIDE_
+            \ LINK_LIBRARY_PREDEFINED_FEATURES
             \ LINK_ONLY
             \ LOWER_CASE
             \ MAKE_C_IDENTIFIER
             \ MAP_IMPORTED_CONFIG_
+            \ MODULE
+            \ NATURAL
             \ NO
+            \ NORMALIZE
+            \ NORMAL_PATH
             \ NOT
             \ OBJCXX_COMPILER_ID
             \ OBJCXX_COMPILER_VERSION
             \ OBJC_COMPILER_ID
             \ OBJC_COMPILER_VERSION
+            \ OBJECT
             \ OFF
             \ OLD_COMPILER
+            \ ORDER
+            \ OUTPUT
+            \ OUTPUT_CONFIG
             \ OUTPUT_NAME
             \ OUTPUT_NAME_
+            \ PATH
+            \ PATH_EQUAL
             \ PDB_NAME
             \ PDB_NAME_
             \ PDB_OUTPUT_DIRECTORY
             \ PDB_OUTPUT_DIRECTORY_
             \ PLATFORM_ID
+            \ POP_BACK
+            \ POP_FRONT
             \ POSIX
+            \ POST_BUILD
+            \ PREPEND
             \ PRIVATE
             \ PUBLIC
+            \ REGEX
+            \ RELATIVE_PATH
+            \ REMOVE_AT
             \ REMOVE_DUPLICATES
+            \ REMOVE_EXTENSION
+            \ REMOVE_FILENAME
+            \ REMOVE_ITEM
+            \ REPLACE
+            \ REPLACE_EXTENSION
+            \ REPLACE_FILENAME
+            \ REQUIRED
+            \ RESCAN
+            \ REVERSE
+            \ RPATH
+            \ RUNTIME_DEPENDENCY_SET
             \ RUNTIME_OUTPUT_NAME
             \ RUNTIME_OUTPUT_NAME_
+            \ SCRIPT
             \ SDK
+            \ SELECTOR
             \ SEMICOLON
+            \ SENSITIVE
+            \ SHARED
             \ SHELL_PATH
+            \ SORT
             \ STATIC
             \ STREQUAL
+            \ STRING
+            \ STRIP
+            \ SUBLIST
             \ TARGET_BUNDLE_CONTENT_DIR
             \ TARGET_BUNDLE_DIR
             \ TARGET_BUNDLE_DIR_NAME
@@ -3893,12 +4607,30 @@
             \ TARGET_FILE_PREFIX
             \ TARGET_FILE_SUFFIX
             \ TARGET_GENEX_EVAL
+            \ TARGET_IMPORT_FILE
+            \ TARGET_IMPORT_FILE_BASE_NAME
+            \ TARGET_IMPORT_FILE_DIR
+            \ TARGET_IMPORT_FILE_NAME
+            \ TARGET_IMPORT_FILE_PREFIX
+            \ TARGET_IMPORT_FILE_SUFFIX
             \ TARGET_LINKER_FILE
             \ TARGET_LINKER_FILE_BASE_NAME
             \ TARGET_LINKER_FILE_DIR
             \ TARGET_LINKER_FILE_NAME
             \ TARGET_LINKER_FILE_PREFIX
             \ TARGET_LINKER_FILE_SUFFIX
+            \ TARGET_LINKER_IMPORT_FILE
+            \ TARGET_LINKER_IMPORT_FILE_BASE_NAME
+            \ TARGET_LINKER_IMPORT_FILE_DIR
+            \ TARGET_LINKER_IMPORT_FILE_NAME
+            \ TARGET_LINKER_IMPORT_FILE_PREFIX
+            \ TARGET_LINKER_IMPORT_FILE_SUFFIX
+            \ TARGET_LINKER_LIBRARY_FILE
+            \ TARGET_LINKER_LIBRARY_FILE_BASE_NAME
+            \ TARGET_LINKER_LIBRARY_FILE_DIR
+            \ TARGET_LINKER_LIBRARY_FILE_NAME
+            \ TARGET_LINKER_LIBRARY_FILE_PREFIX
+            \ TARGET_LINKER_LIBRARY_FILE_SUFFIX
             \ TARGET_NAME_IF_EXISTS
             \ TARGET_OBJECTS
             \ TARGET_PDB_FILE
@@ -3907,16 +4639,34 @@
             \ TARGET_PDB_FILE_NAME
             \ TARGET_POLICY
             \ TARGET_PROPERTY
+            \ TARGET_RUNTIME_DLLS
+            \ TARGET_RUNTIME_DLL_DIRS
             \ TARGET_SONAME_FILE
             \ TARGET_SONAME_FILE_DIR
             \ TARGET_SONAME_FILE_NAME
+            \ TARGET_SONAME_IMPORT_FILE
+            \ TARGET_SONAME_IMPORT_FILE_DIR
+            \ TARGET_SONAME_IMPORT_FILE_NAME
+            \ TOLOWER
+            \ TOUPPER
+            \ TRANSFORM
+            \ TRANSFORM_APPEND
+            \ TRANSFORM_REPLACE
+            \ TRANSFORM_STRIP
+            \ TRANSFORM_TOLOWER
+            \ UNKNOWN
             \ UPPER_CASE
+            \ VERBATIM
             \ VERSION_EQUAL
-            \ VERSION_GREATER
             \ VERSION_GREATER_EQUAL
             \ VERSION_LESS
             \ VERSION_LESS_EQUAL
+            \ WHOLE_ARCHIVE
+            \ WRONG
+            \ _LINK_GROUP_USING_
+            \ _LINK_LIBRARY_USING_
             \ _POSTFIX
+            \ _SUPPORTED
 
 syn case ignore
 
@@ -3936,6 +4686,7 @@
             \ block
             \ break
             \ build_command
+            \ cmake_file_api
             \ cmake_host_system_information
             \ cmake_language
             \ cmake_minimum_required
@@ -4085,6 +4836,7 @@
 hi def link cmakeVariable Identifier
 
 hi def link cmakeKWExternalProject ModeMsg
+hi def link cmakeKWFetchContent ModeMsg
 hi def link cmakeKWadd_compile_definitions ModeMsg
 hi def link cmakeKWadd_compile_options ModeMsg
 hi def link cmakeKWadd_custom_command ModeMsg
@@ -4096,7 +4848,9 @@
 hi def link cmakeKWadd_link_options ModeMsg
 hi def link cmakeKWadd_subdirectory ModeMsg
 hi def link cmakeKWadd_test ModeMsg
+hi def link cmakeKWblock ModeMsg
 hi def link cmakeKWbuild_command ModeMsg
+hi def link cmakeKWcmake_file_api ModeMsg
 hi def link cmakeKWcmake_host_system_information ModeMsg
 hi def link cmakeKWcmake_language ModeMsg
 hi def link cmakeKWcmake_minimum_required ModeMsg
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d559c08..2823ca4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
 # Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
 # file Copyright.txt or https://cmake.org/licensing for details.
 
-cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake)
 set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake)
 
@@ -131,6 +131,8 @@
   endif()
 endif()
 
+option(CMake_BUILD_PCH "Compile CMake with precompiled headers" OFF)
+
 # Check whether to build support for the debugger mode.
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
   if(NOT DEFINED CMake_ENABLE_DEBUGGER)
@@ -153,7 +155,6 @@
 # simply to improve readability of the main script
 #-----------------------------------------------------------------------
 macro(CMAKE_HANDLE_SYSTEM_LIBRARIES)
-  # Options have dependencies.
   include(CMakeDependentOption)
 
   # Allow the user to enable/disable all system utility library options by
@@ -324,6 +325,9 @@
 
 option(CMake_RUN_IWYU "Run include-what-you-use with the compiler." OFF)
 if(CMake_RUN_IWYU)
+  if(CMake_BUILD_PCH)
+    message(FATAL_ERROR "CMake_RUN_IWYU and CMake_BUILD_PCH are ON, but they are incompatible!")
+  endif()
   find_program(IWYU_COMMAND NAMES include-what-you-use iwyu)
   if(NOT IWYU_COMMAND)
     message(FATAL_ERROR "CMake_RUN_IWYU is ON but include-what-you-use is not found!")
diff --git a/Help/command/FIND_XXX.txt b/Help/command/FIND_XXX.txt
index fe26d2b..56c77c1 100644
--- a/Help/command/FIND_XXX.txt
+++ b/Help/command/FIND_XXX.txt
@@ -211,7 +211,8 @@
    setting the :variable:`CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH` to ``FALSE``.
 
    * |SYSTEM_ENVIRONMENT_PATH_XXX|
-   * |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX|
+
+   |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX|
 
 6. Search cmake variables defined in the Platform files
    for the current system.  The searching of ``CMAKE_INSTALL_PREFIX`` and
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst
index 9bc88d6..5fe4326 100644
--- a/Help/command/add_custom_command.rst
+++ b/Help/command/add_custom_command.rst
@@ -24,6 +24,7 @@
                      [COMMENT comment]
                      [DEPFILE depfile]
                      [JOB_POOL job_pool]
+                     [JOB_SERVER_AWARE <bool>]
                      [VERBATIM] [APPEND] [USES_TERMINAL]
                      [COMMAND_EXPAND_LISTS]
                      [DEPENDS_EXPLICIT_ONLY])
@@ -86,6 +87,11 @@
     :ref:`Target-dependent expressions <Target-Dependent Queries>` are not
     permitted.
 
+  .. versionchanged:: 3.28
+    In targets using :ref:`file sets`, custom command byproducts are now
+    considered private unless they are listed in a non-private file set.
+    See policy :policy:`CMP0154`.
+
 ``COMMAND``
   Specify the command-line(s) to execute at build time.
   If more than one ``COMMAND`` is specified they will be executed in order,
@@ -221,6 +227,19 @@
   Using a pool that is not defined by :prop_gbl:`JOB_POOLS` causes
   an error by ninja at build time.
 
+``JOB_SERVER_AWARE``
+  .. versionadded:: 3.28
+
+  Specify that the command is GNU Make job server aware.
+
+  For the :generator:`Unix Makefiles`, :generator:`MSYS Makefiles`, and
+  :generator:`MinGW Makefiles` generators this will add the ``+`` prefix to the
+  recipe line. See the `GNU Make Documentation`_ for more information.
+
+  This option is silently ignored by other generators.
+
+.. _`GNU Make Documentation`: https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html
+
 ``MAIN_DEPENDENCY``
   Specify the primary input source file to the command.  This is
   treated just like any value given to the ``DEPENDS`` option
@@ -256,6 +275,11 @@
     :ref:`Target-dependent expressions <Target-Dependent Queries>` are not
     permitted.
 
+  .. versionchanged:: 3.28
+    In targets using :ref:`file sets`, custom command outputs are now
+    considered private unless they are listed in a non-private file set.
+    See policy :policy:`CMP0154`.
+
 ``USES_TERMINAL``
   .. versionadded:: 3.2
 
@@ -516,7 +540,7 @@
   Because generator expressions can be used in custom commands,
   it is possible to define ``COMMAND`` lines or whole custom commands
   which evaluate to empty strings for certain configurations.
-  For **Visual Studio 11 2012 (and newer)** generators these command
+  For **Visual Studio 12 2013 (and newer)** generators these command
   lines or custom commands will be omitted for the specific
   configuration and no "empty-string-command" will be added.
 
diff --git a/Help/command/add_custom_target.rst b/Help/command/add_custom_target.rst
index 545b9a5..0385a93 100644
--- a/Help/command/add_custom_target.rst
+++ b/Help/command/add_custom_target.rst
@@ -12,6 +12,7 @@
                     [WORKING_DIRECTORY dir]
                     [COMMENT comment]
                     [JOB_POOL job_pool]
+                    [JOB_SERVER_AWARE <bool>]
                     [VERBATIM] [USES_TERMINAL]
                     [COMMAND_EXPAND_LISTS]
                     [SOURCES src1 [src2...]])
@@ -62,6 +63,11 @@
     :ref:`Target-dependent expressions <Target-Dependent Queries>` are not
     permitted.
 
+  .. versionchanged:: 3.28
+    In custom targets using :ref:`file sets`, byproducts are now
+    considered private unless they are listed in a non-private file set.
+    See policy :policy:`CMP0154`.
+
 ``COMMAND``
   Specify the command-line(s) to execute at build time.
   If more than one ``COMMAND`` is specified they will be executed in order,
@@ -146,6 +152,19 @@
   Using a pool that is not defined by :prop_gbl:`JOB_POOLS` causes
   an error by ninja at build time.
 
+``JOB_SERVER_AWARE``
+  .. versionadded:: 3.28
+
+  Specify that the command is GNU Make job server aware.
+
+  For the :generator:`Unix Makefiles`, :generator:`MSYS Makefiles`, and
+  :generator:`MinGW Makefiles` generators this will add the ``+`` prefix to the
+  recipe line. See the `GNU Make Documentation`_ for more information.
+
+  This option is silently ignored by other generators.
+
+.. _`GNU Make Documentation`: https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html
+
 ``SOURCES``
   Specify additional source files to be included in the custom target.
   Specified source files will be added to IDE project files for
diff --git a/Help/command/block.rst b/Help/command/block.rst
index a352e83..4c6e111 100644
--- a/Help/command/block.rst
+++ b/Help/command/block.rst
@@ -21,7 +21,8 @@
 
   ``POLICIES``
     Create a new policy scope. This is equivalent to
-    :command:`cmake_policy(PUSH)`.
+    :command:`cmake_policy(PUSH)` with an automatic
+    :command:`cmake_policy(POP)` when leaving the block scope.
 
   ``VARIABLES``
     Create a new variable scope.
diff --git a/Help/command/cmake_host_system_information.rst b/Help/command/cmake_host_system_information.rst
index 76824ef..dad0833 100644
--- a/Help/command/cmake_host_system_information.rst
+++ b/Help/command/cmake_host_system_information.rst
@@ -146,6 +146,13 @@
 
   See :variable:`CMAKE_HOST_SYSTEM_PROCESSOR`
 
+``MSYSTEM_PREFIX``
+  .. versionadded:: 3.28
+
+  Available only on Windows hosts.  In a MSYS or MinGW development
+  environment that sets the ``MSYSTEM`` environment variable, this
+  is its installation prefix.  Otherwise, this is the empty string.
+
 ``DISTRIB_INFO``
   .. versionadded:: 3.22
 
diff --git a/Help/command/cmake_policy.rst b/Help/command/cmake_policy.rst
index d7880bc..4a08c01 100644
--- a/Help/command/cmake_policy.rst
+++ b/Help/command/cmake_policy.rst
@@ -24,9 +24,8 @@
 behavior.  While setting policies individually is supported, we
 encourage projects to set policies based on CMake versions:
 
-.. code-block:: cmake
-
-  cmake_policy(VERSION <min>[...<max>])
+.. signature:: cmake_policy(VERSION <min>[...<max>])
+  :target: VERSION
 
 .. versionadded:: 3.12
   The optional ``<max>`` version.
@@ -57,10 +56,8 @@
 Setting Policies Explicitly
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
-  cmake_policy(SET CMP<NNNN> NEW)
-  cmake_policy(SET CMP<NNNN> OLD)
+.. signature:: cmake_policy(SET CMP<NNNN> NEW|OLD)
+  :target: SET
 
 Tell CMake to use the ``OLD`` or ``NEW`` behavior for a given policy.
 Projects depending on the old behavior of a given policy may silence a
@@ -73,9 +70,8 @@
 Checking Policy Settings
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
-.. code-block:: cmake
-
-  cmake_policy(GET CMP<NNNN> <variable>)
+.. signature:: cmake_policy(GET CMP<NNNN> <variable>)
+  :target: GET
 
 Check whether a given policy is set to ``OLD`` or ``NEW`` behavior.
 The output ``<variable>`` value will be ``OLD`` or ``NEW`` if the
@@ -94,23 +90,27 @@
 The ``cmake_policy`` command provides an interface to manage custom
 entries on the policy stack:
 
-.. code-block:: cmake
+.. signature:: cmake_policy(PUSH)
+  :target: PUSH
 
-  cmake_policy(PUSH)
-  cmake_policy(POP)
+  Create a new entry on the policy stack.
+
+.. signature:: cmake_policy(POP)
+  :target: POP
+
+  Remove the last policy stack entry created with ``cmake_policy(PUSH)``.
 
 Each ``PUSH`` must have a matching ``POP`` to erase any changes.
 This is useful to make temporary changes to policy settings.
 Calls to the :command:`cmake_minimum_required(VERSION)`,
-``cmake_policy(VERSION)``, or ``cmake_policy(SET)`` commands
+:command:`cmake_policy(VERSION)`, or :command:`cmake_policy(SET)` commands
 influence only the current top of the policy stack.
 
 .. versionadded:: 3.25
-  The :command:`block` and :command:`endblock` commands offer a more flexible
+  The :command:`block(SCOPE_FOR POLICIES)` command offers a more flexible
   and more secure way to manage the policy stack. The pop action is done
-  automatically when the :command:`endblock` command is executed, so it avoid
-  to call the :command:`cmake_policy(POP)` command before each
-  :command:`return` command.
+  automatically when leaving the block scope, so there is no need to
+  precede each :command:`return` with a call to :command:`cmake_policy(POP)`.
 
   .. code-block:: cmake
 
diff --git a/Help/command/exec_program.rst b/Help/command/exec_program.rst
index 983a6df..6010176 100644
--- a/Help/command/exec_program.rst
+++ b/Help/command/exec_program.rst
@@ -1,6 +1,10 @@
 exec_program
 ------------
 
+.. versionchanged:: 3.28
+  This command is available only if policy :policy:`CMP0153` is not set to ``NEW``.
+  Port projects to the :command:`execute_process` command.
+
 .. deprecated:: 3.0
 
   Use the :command:`execute_process` command instead.
diff --git a/Help/command/export.rst b/Help/command/export.rst
index 2e14a10..cc927bc 100644
--- a/Help/command/export.rst
+++ b/Help/command/export.rst
@@ -54,10 +54,7 @@
   to support consumers using CMake versions older than 2.8.12.
 
 ``CXX_MODULES_DIRECTORY <directory>``
-
-  .. note ::
-
-    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+  .. versionadded:: 3.28
 
   Export C++ module properties to files under the given directory. Each file
   will be named according to the target's export name (without any namespace).
diff --git a/Help/command/file.rst b/Help/command/file.rst
index 30a7f4d..f9d1a79 100644
--- a/Help/command/file.rst
+++ b/Help/command/file.rst
@@ -981,6 +981,11 @@
       if ``USERPROFILE`` is not defined.  On all other platforms, only ``HOME``
       is used.
 
+  .. versionchanged:: 3.28
+
+    All symlinks are resolved before collapsing ``../`` components.
+    See policy :policy:`CMP0152`.
+
 .. signature::
   file(RELATIVE_PATH <variable> <directory> <file>)
 
diff --git a/Help/command/find_file.rst b/Help/command/find_file.rst
index 9f89f52..9ef8a6d 100644
--- a/Help/command/find_file.rst
+++ b/Help/command/find_file.rst
@@ -28,9 +28,11 @@
 
 .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``INCLUDE``
    and ``PATH``.
-.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts:
-      ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
-      is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace::
+   On Windows hosts, CMake 3.3 through 3.27 searched additional paths:
+   ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
+   is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
+   This behavior was removed by CMake 3.28.
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
diff --git a/Help/command/find_library.rst b/Help/command/find_library.rst
index 99e36a4..ba046fa 100644
--- a/Help/command/find_library.rst
+++ b/Help/command/find_library.rst
@@ -27,9 +27,11 @@
 
 .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``LIB``
    and ``PATH``.
-.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts:
-      ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
-      is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace::
+   On Windows hosts, CMake 3.3 through 3.27 searched additional paths:
+   ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
+   is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
+   This behavior was removed by CMake 3.28.
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    ``<prefix>/lib/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE` is set,
@@ -60,6 +62,10 @@
 framework is used as a library, CMake will use a ``-framework A``, and a
 ``-F<fullPath>`` to link the framework to the target.
 
+.. versionadded:: 3.28
+
+  The library found can now be a ``.xcframework`` folder.
+
 If the :variable:`CMAKE_FIND_LIBRARY_CUSTOM_LIB_SUFFIX` variable is set all
 search paths will be tested as normal, with the suffix appended, and with
 all matches of ``lib/`` replaced with
diff --git a/Help/command/find_package.rst b/Help/command/find_package.rst
index b0b6fe1..9a007fa 100644
--- a/Help/command/find_package.rst
+++ b/Help/command/find_package.rst
@@ -323,18 +323,27 @@
 In all cases the ``<name>`` is treated as case-insensitive and corresponds
 to any of the names specified (``<PackageName>`` or names given by ``NAMES``).
 
-Paths with ``lib/<arch>`` are enabled if the
-:variable:`CMAKE_LIBRARY_ARCHITECTURE` variable is set. ``lib*`` includes one
-or more of the values ``lib64``, ``lib32``, ``libx32`` or ``lib`` (searched in
-that order).
+If at least one compiled language has been enabled, the architecture-specific
+``lib/<arch>`` and ``lib*`` directories may be searched based on the compiler's
+target architecture, in the following order:
 
-* Paths with ``lib64`` are searched on 64 bit platforms if the
+``lib/<arch>``
+  Searched if the :variable:`CMAKE_LIBRARY_ARCHITECTURE` variable is set.
+
+``lib64``
+  Searched on 64 bit platforms (:variable:`CMAKE_SIZEOF_VOID_P` is 8) and the
   :prop_gbl:`FIND_LIBRARY_USE_LIB64_PATHS` property is set to ``TRUE``.
-* Paths with ``lib32`` are searched on 32 bit platforms if the
+
+``lib32``
+  Searched on 32 bit platforms (:variable:`CMAKE_SIZEOF_VOID_P` is 4) and the
   :prop_gbl:`FIND_LIBRARY_USE_LIB32_PATHS` property is set to ``TRUE``.
-* Paths with ``libx32`` are searched on platforms using the x32 ABI
+
+``libx32``
+  Searched on platforms using the x32 ABI
   if the :prop_gbl:`FIND_LIBRARY_USE_LIBX32_PATHS` property is set to ``TRUE``.
-* The ``lib`` path is always searched.
+
+``lib``
+  Always searched.
 
 .. versionchanged:: 3.24
   On ``Windows`` platform, it is possible to include registry queries as part
@@ -346,7 +355,7 @@
   ``REGISTRY_VIEW`` can be specified to manage ``Windows`` registry queries
   specified as part of ``PATHS`` and ``HINTS``.
 
-.. include:: FIND_XXX_REGISTRY_VIEW.txt
+  .. include:: FIND_XXX_REGISTRY_VIEW.txt
 
 If ``PATH_SUFFIXES`` is specified, the suffixes are appended to each
 (``W``) or (``U``) directory entry one-by-one.
diff --git a/Help/command/find_path.rst b/Help/command/find_path.rst
index f0522f6..080c231 100644
--- a/Help/command/find_path.rst
+++ b/Help/command/find_path.rst
@@ -27,9 +27,11 @@
 
 .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``INCLUDE``
    and ``PATH``.
-.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts:
-      ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
-      is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace::
+   On Windows hosts, CMake 3.3 through 3.27 searched additional paths:
+   ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
+   is set, and |SYSTEM_ENVIRONMENT_PREFIX_PATH_XXX_SUBDIR|.
+   This behavior was removed by CMake 3.28.
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    ``<prefix>/include/<arch>`` if :variable:`CMAKE_LIBRARY_ARCHITECTURE`
diff --git a/Help/command/find_program.rst b/Help/command/find_program.rst
index fe95a9a..b6b2508 100644
--- a/Help/command/find_program.rst
+++ b/Help/command/find_program.rst
@@ -23,7 +23,7 @@
 .. |ENV_CMAKE_XXX_MAC_PATH| replace:: :envvar:`CMAKE_APPBUNDLE_PATH`
 
 .. |SYSTEM_ENVIRONMENT_PATH_XXX| replace:: The directories in ``PATH`` itself.
-.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: On Windows hosts no extra search paths are included
+.. |SYSTEM_ENVIRONMENT_PATH_WINDOWS_XXX| replace:: \
 
 .. |CMAKE_SYSTEM_PREFIX_PATH_XXX| replace::
    |CMAKE_SYSTEM_PREFIX_PATH_XXX_SUBDIR|
diff --git a/Help/command/get_property.rst b/Help/command/get_property.rst
index 6b9931e..a0a12bb 100644
--- a/Help/command/get_property.rst
+++ b/Help/command/get_property.rst
@@ -12,7 +12,8 @@
                 SOURCE    <source>
                           [DIRECTORY <dir> | TARGET_DIRECTORY <target>] |
                 INSTALL   <file>   |
-                TEST      <test>   |
+                TEST      <test>
+                          [DIRECTORY <dir>] |
                 CACHE     <entry>  |
                 VARIABLE           >
                PROPERTY <name>
@@ -73,6 +74,16 @@
   Scope must name one existing test.
   See also the :command:`get_test_property` command.
 
+  .. versionadded:: 3.28
+    Directory scope can be overridden with the following sub-option:
+
+    ``DIRECTORY <dir>``
+      The test property will be read from the ``<dir>`` directory's
+      scope.  CMake must already know about the directory, either by having added
+      it through a call to :command:`add_subdirectory` or ``<dir>`` being the top
+      level directory. Relative paths are treated as relative to the current
+      source directory. ``<dir>`` may reference a binary directory.
+
 ``CACHE``
   Scope must name one cache entry.
 
diff --git a/Help/command/get_test_property.rst b/Help/command/get_test_property.rst
index 2b6f354..1fcf24e 100644
--- a/Help/command/get_test_property.rst
+++ b/Help/command/get_test_property.rst
@@ -5,7 +5,7 @@
 
 .. code-block:: cmake
 
-  get_test_property(test property VAR)
+  get_test_property(test property [DIRECTORY <dir>] VAR)
 
 Get a property from the test.  The value of the property is stored in
 the variable ``VAR``.  If the test property is not found, the behavior
@@ -19,6 +19,17 @@
 For a list of standard properties you can type
 :option:`cmake --help-property-list`.
 
+.. versionadded:: 3.28
+  Directory scope can be overridden with the following sub-option:
+
+  ``DIRECTORY <dir>``
+    The test property will be read from the ``<dir>`` directory's
+    scope.  CMake must already know about that source directory, either by
+    having added it through a call to :command:`add_subdirectory` or ``<dir>``
+    being the top level source directory.  Relative paths are treated as
+    relative to the current source directory. ``<dir>`` may reference a binary
+    directory.
+
 See Also
 ^^^^^^^^
 
diff --git a/Help/command/if.rst b/Help/command/if.rst
index c47c71b..5d85a1f 100644
--- a/Help/command/if.rst
+++ b/Help/command/if.rst
@@ -228,36 +228,36 @@
 .. signature:: if(<variable|string> LESS <variable|string>)
   :target: LESS
 
-  True if the given string or variable's value is a valid number and less
-  than that on the right.
+  True if the given string or variable's value parses as a real number
+  (like a C ``double``) and less than that on the right.
 
 .. signature:: if(<variable|string> GREATER <variable|string>)
   :target: GREATER
 
-  True if the given string or variable's value is a valid number and greater
-  than that on the right.
+  True if the given string or variable's value parses as a real number
+  (like a C ``double``) and greater than that on the right.
 
 .. signature:: if(<variable|string> EQUAL <variable|string>)
   :target: EQUAL
 
-  True if the given string or variable's value is a valid number and equal
-  to that on the right.
+  True if the given string or variable's value parses as a real number
+  (like a C ``double``) and equal to that on the right.
 
 .. signature:: if(<variable|string> LESS_EQUAL <variable|string>)
   :target: LESS_EQUAL
 
   .. versionadded:: 3.7
 
-  True if the given string or variable's value is a valid number and less
-  than or equal to that on the right.
+  True if the given string or variable's value parses as a real number
+  (like a C ``double``) and less than or equal to that on the right.
 
 .. signature:: if(<variable|string> GREATER_EQUAL <variable|string>)
   :target: GREATER_EQUAL
 
   .. versionadded:: 3.7
 
-  True if the given string or variable's value is a valid number and greater
-  than or equal to that on the right.
+  True if the given string or variable's value parses as a real number
+  (like a C ``double``) and greater than or equal to that on the right.
 
 .. signature:: if(<variable|string> STRLESS <variable|string>)
   :target: STRLESS
diff --git a/Help/command/install.rst b/Help/command/install.rst
index b56f20c..b0698dd 100644
--- a/Help/command/install.rst
+++ b/Help/command/install.rst
@@ -1,6 +1,10 @@
 install
 -------
 
+.. only:: html
+
+   .. contents::
+
 Specify rules to run at install time.
 
 Synopsis
@@ -34,12 +38,14 @@
   The environment variable :envvar:`CMAKE_INSTALL_MODE` can override the
   default copying behavior of ``install()``.
 
+.. _`common options`:
+
 There are multiple signatures for this command.  Some of them define
 installation options for files and targets.  Options common to
 multiple signatures are covered here but they are valid only for
 signatures that specify them.  The common options are:
 
-``DESTINATION``
+``DESTINATION <dir>``
   Specify the directory on disk to which a file will be installed.
   Arguments can be relative or absolute paths.
 
@@ -58,32 +64,28 @@
   :variable:`CMAKE_INSTALL_PREFIX`; this prefix is used by default if
   the DESTINATION is a relative path.
 
-``PERMISSIONS``
+``PERMISSIONS <permission>...``
   Specify permissions for installed files.  Valid permissions are
   ``OWNER_READ``, ``OWNER_WRITE``, ``OWNER_EXECUTE``, ``GROUP_READ``,
   ``GROUP_WRITE``, ``GROUP_EXECUTE``, ``WORLD_READ``, ``WORLD_WRITE``,
   ``WORLD_EXECUTE``, ``SETUID``, and ``SETGID``.  Permissions that do
   not make sense on certain platforms are ignored on those platforms.
 
-``CONFIGURATIONS``
+  If this option is used multiple times in a single call, its list
+  of permissions accumulates.  If an :command:`install(TARGETS)` call
+  uses `\<artifact-kind\>`_ arguments, a separate list of permissions
+  is accumulated for each kind of artifact.
+
+``CONFIGURATIONS <config>...``
   Specify a list of build configurations for which the install rule
-  applies (Debug, Release, etc.). Note that the values specified for
-  this option only apply to options listed AFTER the ``CONFIGURATIONS``
-  option. For example, to set separate install paths for the Debug and
-  Release configurations, do the following:
+  applies (Debug, Release, etc.).
 
-  .. code-block:: cmake
+  If this option is used multiple times in a single call, its list
+  of configurations accumulates.  If an :command:`install(TARGETS)`
+  call uses `\<artifact-kind\>`_ arguments, a separate list of
+  configurations is accumulated for each kind of artifact.
 
-    install(TARGETS target
-            CONFIGURATIONS Debug
-            RUNTIME DESTINATION Debug/bin)
-    install(TARGETS target
-            CONFIGURATIONS Release
-            RUNTIME DESTINATION Release/bin)
-
-  Note that ``CONFIGURATIONS`` appears BEFORE ``RUNTIME DESTINATION``.
-
-``COMPONENT``
+``COMPONENT <component>``
   Specify an installation component name with which the install rule
   is associated, such as ``Runtime`` or ``Development``.  During
   component-specific installation only install rules associated with
@@ -99,7 +101,7 @@
   Specify that the file is excluded from a full installation and only
   installed as part of a component-specific installation
 
-``RENAME``
+``RENAME <name>``
   Specify a name for an installed file that may be different from the
   original file.  Renaming is allowed only when a single file is
   installed by the command.
@@ -121,861 +123,911 @@
   they will be created according to the uname rules on Unix-like platforms.
   Windows platforms are unaffected.
 
-Installing Targets
-^^^^^^^^^^^^^^^^^^
+Signatures
+^^^^^^^^^^
 
-.. _`install(TARGETS)`:
-.. _TARGETS:
+.. signature::
+  install(TARGETS <target>... [...])
 
-.. code-block:: cmake
-
-  install(TARGETS targets... [EXPORT <export-name>]
-          [RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET <set-name>]
-          [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE|
-            PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE|FILE_SET <set-name>|CXX_MODULES_BMI]
-           [DESTINATION <dir>]
-           [PERMISSIONS permissions...]
-           [CONFIGURATIONS [Debug|Release|...]]
-           [COMPONENT <component>]
-           [NAMELINK_COMPONENT <component>]
-           [OPTIONAL] [EXCLUDE_FROM_ALL]
-           [NAMELINK_ONLY|NAMELINK_SKIP]
-          ] [...]
-          [INCLUDES DESTINATION [<dir> ...]]
-          )
-
-The ``TARGETS`` form specifies rules for installing targets from a
-project.  There are several kinds of target :ref:`Output Artifacts`
-that may be installed:
-
-``ARCHIVE``
-  Target artifacts of this kind include:
-
-  * *Static libraries*
-    (except on macOS when marked as ``FRAMEWORK``, see below);
-  * *DLL import libraries*
-    (on all Windows-based systems including Cygwin; they have extension
-    ``.lib``, in contrast to the ``.dll`` libraries that go to ``RUNTIME``);
-  * On AIX, the *linker import file* created for executables with
-    :prop_tgt:`ENABLE_EXPORTS` enabled.
-  * On macOS, the *linker import file* created for shared libraries with
-    :prop_tgt:`ENABLE_EXPORTS` enabled (except when marked as ``FRAMEWORK``,
-    see below).
-
-``LIBRARY``
-  Target artifacts of this kind include:
-
-  * *Shared libraries*, except
-
-    - DLLs (these go to ``RUNTIME``, see below),
-    - on macOS when marked as ``FRAMEWORK`` (see below).
-
-``RUNTIME``
-  Target artifacts of this kind include:
-
-  * *Executables*
-    (except on macOS when marked as ``MACOSX_BUNDLE``, see ``BUNDLE`` below);
-  * DLLs (on all Windows-based systems including Cygwin; note that the
-    accompanying import libraries are of kind ``ARCHIVE``).
-
-``OBJECTS``
-  .. versionadded:: 3.9
-
-  Object files associated with *object libraries*.
-
-``FRAMEWORK``
-  Both static and shared libraries marked with the ``FRAMEWORK``
-  property are treated as ``FRAMEWORK`` targets on macOS.
-
-``BUNDLE``
-  Executables marked with the :prop_tgt:`MACOSX_BUNDLE` property are treated as
-  ``BUNDLE`` targets on macOS.
-
-``PUBLIC_HEADER``
-  Any :prop_tgt:`PUBLIC_HEADER` files associated with a library are installed in
-  the destination specified by the ``PUBLIC_HEADER`` argument on non-Apple
-  platforms. Rules defined by this argument are ignored for :prop_tgt:`FRAMEWORK`
-  libraries on Apple platforms because the associated files are installed
-  into the appropriate locations inside the framework folder. See
-  :prop_tgt:`PUBLIC_HEADER` for details.
-
-``PRIVATE_HEADER``
-  Similar to ``PUBLIC_HEADER``, but for ``PRIVATE_HEADER`` files. See
-  :prop_tgt:`PRIVATE_HEADER` for details.
-
-``RESOURCE``
-  Similar to ``PUBLIC_HEADER`` and ``PRIVATE_HEADER``, but for
-  ``RESOURCE`` files. See :prop_tgt:`RESOURCE` for details.
-
-``FILE_SET <set>``
-  .. versionadded:: 3.23
-
-  File sets are defined by the :command:`target_sources(FILE_SET)` command.
-  If the file set ``<set>`` exists and is ``PUBLIC`` or ``INTERFACE``, any
-  files in the set are installed under the destination (see below).
-  The directory structure relative to the file set's base directories is
-  preserved. For example, a file added to the file set as
-  ``/blah/include/myproj/here.h`` with a base directory ``/blah/include``
-  would be installed to ``myproj/here.h`` below the destination.
-
-``CXX_MODULES_BMI``
-
-  .. note ::
-
-    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
-
-  Any module files from C++ modules from ``PUBLIC`` sources in a file set of
-  type ``CXX_MODULES`` will be installed to the given ``DESTINATION``. All
-  modules are placed directly in the destination as no directory structure is
-  derived from the names of the modules. An empty ``DESTINATION`` may be used
-  to suppress installing these files (for use in generic code).
-
-For each of these arguments given, the arguments following them only apply
-to the target or file type specified in the argument. If none is given, the
-installation properties apply to all target types.
-
-For regular executables, static libraries and shared libraries, the
-``DESTINATION`` argument is not required.  For these target types, when
-``DESTINATION`` is omitted, a default destination will be taken from the
-appropriate variable from :module:`GNUInstallDirs`, or set to a built-in
-default value if that variable is not defined.  The same is true for file
-sets, and the public and private headers associated with the installed
-targets through the :prop_tgt:`PUBLIC_HEADER` and :prop_tgt:`PRIVATE_HEADER`
-target properties. A destination must always be provided for module libraries,
-Apple bundles and frameworks.  A destination can be omitted for interface and
-object libraries, but they are handled differently (see the discussion of this
-topic toward the end of this section).
-
-For shared libraries on DLL platforms, if neither ``RUNTIME`` nor ``ARCHIVE``
-destinations are specified, both the ``RUNTIME`` and ``ARCHIVE`` components are
-installed to their default destinations. If either a ``RUNTIME`` or ``ARCHIVE``
-destination is specified, the component is installed to that destination, and
-the other component is not installed. If both ``RUNTIME`` and ``ARCHIVE``
-destinations are specified, then both components are installed to their
-respective destinations.
-
-The following table shows the target types with their associated variables and
-built-in defaults that apply when no destination is given:
-
-=============================== =============================== ======================
-   Target Type                      GNUInstallDirs Variable        Built-In Default
-=============================== =============================== ======================
-``RUNTIME``                     ``${CMAKE_INSTALL_BINDIR}``     ``bin``
-``LIBRARY``                     ``${CMAKE_INSTALL_LIBDIR}``     ``lib``
-``ARCHIVE``                     ``${CMAKE_INSTALL_LIBDIR}``     ``lib``
-``PRIVATE_HEADER``              ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
-``PUBLIC_HEADER``               ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
-``FILE_SET`` (type ``HEADERS``) ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
-=============================== =============================== ======================
-
-Projects wishing to follow the common practice of installing headers into a
-project-specific subdirectory may prefer using file sets with appropriate
-paths and base directories. Otherwise, they must provide a ``DESTINATION``
-instead of being able to rely on the above (see next example below).
-
-To make packages compliant with distribution filesystem layout policies, if
-projects must specify a ``DESTINATION``, it is recommended that they use a
-path that begins with the appropriate :module:`GNUInstallDirs` variable.
-This allows package maintainers to control the install destination by setting
-the appropriate cache variables.  The following example shows a static library
-being installed to the default destination provided by
-:module:`GNUInstallDirs`, but with its headers installed to a project-specific
-subdirectory without using file sets:
-
-.. code-block:: cmake
-
-  add_library(mylib STATIC ...)
-  set_target_properties(mylib PROPERTIES PUBLIC_HEADER mylib.h)
-  include(GNUInstallDirs)
-  install(TARGETS mylib
-          PUBLIC_HEADER
-            DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/myproj
-  )
-
-In addition to the common options listed above, each target can accept
-the following additional arguments:
-
-``NAMELINK_COMPONENT``
-  .. versionadded:: 3.12
-
-  On some platforms a versioned shared library has a symbolic link such
-  as::
-
-    lib<name>.so -> lib<name>.so.1
-
-  where ``lib<name>.so.1`` is the soname of the library and ``lib<name>.so``
-  is a "namelink" allowing linkers to find the library when given
-  ``-l<name>``. The ``NAMELINK_COMPONENT`` option is similar to the
-  ``COMPONENT`` option, but it changes the installation component of a shared
-  library namelink if one is generated. If not specified, this defaults to the
-  value of ``COMPONENT``. It is an error to use this parameter outside of a
-  ``LIBRARY`` block.
-
-  .. versionchanged:: 3.27
-    This parameter is also usable for an ``ARCHIVE`` block to manage
-    the linker import file created, on macOS, for shared libraries with
-    :prop_tgt:`ENABLE_EXPORTS` enabled.
-
-  Consider the following example:
+  Install target :ref:`Output Artifacts` and associated files:
 
   .. code-block:: cmake
 
+    install(TARGETS <target>... [EXPORT <export-name>]
+            [RUNTIME_DEPENDENCIES <arg>...|RUNTIME_DEPENDENCY_SET <set-name>]
+            [<artifact-option>...]
+            [<artifact-kind> <artifact-option>...]...
+            [INCLUDES DESTINATION [<dir> ...]]
+            )
+
+  where ``<artifact-option>...`` group may contain:
+
+  .. code-block:: cmake
+
+    [DESTINATION <dir>]
+    [PERMISSIONS <permission>...]
+    [CONFIGURATIONS <config>...]
+    [COMPONENT <component>]
+    [NAMELINK_COMPONENT <component>]
+    [OPTIONAL] [EXCLUDE_FROM_ALL]
+    [NAMELINK_ONLY|NAMELINK_SKIP]
+
+  The first ``<artifact-option>...`` group applies to target
+  :ref:`Output Artifacts` that do not have a dedicated group specified
+  later in the same call.
+
+  .. _`<artifact-kind>`:
+
+  Each ``<artifact-kind> <artifact-option>...`` group applies to
+  :ref:`Output Artifacts` of the specified artifact kind:
+
+  ``ARCHIVE``
+    Target artifacts of this kind include:
+
+    * *Static libraries*
+      (except on macOS when marked as ``FRAMEWORK``, see below);
+    * *DLL import libraries*
+      (on all Windows-based systems including Cygwin; they have extension
+      ``.lib``, in contrast to the ``.dll`` libraries that go to ``RUNTIME``);
+    * On AIX, the *linker import file* created for executables with
+      :prop_tgt:`ENABLE_EXPORTS` enabled.
+    * On macOS, the *linker import file* created for shared libraries with
+      :prop_tgt:`ENABLE_EXPORTS` enabled (except when marked as ``FRAMEWORK``,
+      see below).
+
+  ``LIBRARY``
+    Target artifacts of this kind include:
+
+    * *Shared libraries*, except
+
+      - DLLs (these go to ``RUNTIME``, see below),
+      - on macOS when marked as ``FRAMEWORK`` (see below).
+
+  ``RUNTIME``
+    Target artifacts of this kind include:
+
+    * *Executables*
+      (except on macOS when marked as ``MACOSX_BUNDLE``, see ``BUNDLE`` below);
+    * DLLs (on all Windows-based systems including Cygwin; note that the
+      accompanying import libraries are of kind ``ARCHIVE``).
+
+  ``OBJECTS``
+    .. versionadded:: 3.9
+
+    Object files associated with *object libraries*.
+
+  ``FRAMEWORK``
+    Both static and shared libraries marked with the ``FRAMEWORK``
+    property are treated as ``FRAMEWORK`` targets on macOS.
+
+  ``BUNDLE``
+    Executables marked with the :prop_tgt:`MACOSX_BUNDLE` property are treated as
+    ``BUNDLE`` targets on macOS.
+
+  ``PUBLIC_HEADER``
+    Any :prop_tgt:`PUBLIC_HEADER` files associated with a library are installed in
+    the destination specified by the ``PUBLIC_HEADER`` argument on non-Apple
+    platforms. Rules defined by this argument are ignored for :prop_tgt:`FRAMEWORK`
+    libraries on Apple platforms because the associated files are installed
+    into the appropriate locations inside the framework folder. See
+    :prop_tgt:`PUBLIC_HEADER` for details.
+
+  ``PRIVATE_HEADER``
+    Similar to ``PUBLIC_HEADER``, but for ``PRIVATE_HEADER`` files. See
+    :prop_tgt:`PRIVATE_HEADER` for details.
+
+  ``RESOURCE``
+    Similar to ``PUBLIC_HEADER`` and ``PRIVATE_HEADER``, but for
+    ``RESOURCE`` files. See :prop_tgt:`RESOURCE` for details.
+
+  ``FILE_SET <set-name>``
+    .. versionadded:: 3.23
+
+    File sets are defined by the :command:`target_sources(FILE_SET)` command.
+    If the file set ``<set-name>`` exists and is ``PUBLIC`` or ``INTERFACE``,
+    any files in the set are installed under the destination (see below).
+    The directory structure relative to the file set's base directories is
+    preserved. For example, a file added to the file set as
+    ``/blah/include/myproj/here.h`` with a base directory ``/blah/include``
+    would be installed to ``myproj/here.h`` below the destination.
+
+  ``CXX_MODULES_BMI``
+    .. versionadded:: 3.28
+
+    Any module files from C++ modules from ``PUBLIC`` sources in a file set of
+    type ``CXX_MODULES`` will be installed to the given ``DESTINATION``. All
+    modules are placed directly in the destination as no directory structure is
+    derived from the names of the modules. An empty ``DESTINATION`` may be used
+    to suppress installing these files (for use in generic code).
+
+  For regular executables, static libraries and shared libraries, the
+  ``DESTINATION`` argument is not required.  For these target types, when
+  ``DESTINATION`` is omitted, a default destination will be taken from the
+  appropriate variable from :module:`GNUInstallDirs`, or set to a built-in
+  default value if that variable is not defined.  The same is true for file
+  sets, and the public and private headers associated with the installed
+  targets through the :prop_tgt:`PUBLIC_HEADER` and :prop_tgt:`PRIVATE_HEADER`
+  target properties. A destination must always be provided for module libraries,
+  Apple bundles and frameworks.  A destination can be omitted for interface and
+  object libraries, but they are handled differently (see the discussion of this
+  topic toward the end of this section).
+
+  For shared libraries on DLL platforms, if neither ``RUNTIME`` nor ``ARCHIVE``
+  destinations are specified, both the ``RUNTIME`` and ``ARCHIVE`` components are
+  installed to their default destinations. If either a ``RUNTIME`` or ``ARCHIVE``
+  destination is specified, the component is installed to that destination, and
+  the other component is not installed. If both ``RUNTIME`` and ``ARCHIVE``
+  destinations are specified, then both components are installed to their
+  respective destinations.
+
+  The following table shows the target types with their associated variables and
+  built-in defaults that apply when no destination is given:
+
+  =============================== =============================== ======================
+     Target Type                      GNUInstallDirs Variable        Built-In Default
+  =============================== =============================== ======================
+  ``RUNTIME``                     ``${CMAKE_INSTALL_BINDIR}``     ``bin``
+  ``LIBRARY``                     ``${CMAKE_INSTALL_LIBDIR}``     ``lib``
+  ``ARCHIVE``                     ``${CMAKE_INSTALL_LIBDIR}``     ``lib``
+  ``PRIVATE_HEADER``              ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
+  ``PUBLIC_HEADER``               ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
+  ``FILE_SET`` (type ``HEADERS``) ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
+  =============================== =============================== ======================
+
+  Projects wishing to follow the common practice of installing headers into a
+  project-specific subdirectory may prefer using file sets with appropriate
+  paths and base directories. Otherwise, they must provide a ``DESTINATION``
+  instead of being able to rely on the above (see next example below).
+
+  To make packages compliant with distribution filesystem layout policies, if
+  projects must specify a ``DESTINATION``, it is recommended that they use a
+  path that begins with the appropriate :module:`GNUInstallDirs` variable.
+  This allows package maintainers to control the install destination by setting
+  the appropriate cache variables.  The following example shows a static library
+  being installed to the default destination provided by
+  :module:`GNUInstallDirs`, but with its headers installed to a project-specific
+  subdirectory without using file sets:
+
+  .. code-block:: cmake
+
+    add_library(mylib STATIC ...)
+    set_target_properties(mylib PROPERTIES PUBLIC_HEADER mylib.h)
+    include(GNUInstallDirs)
     install(TARGETS mylib
-            LIBRARY
-              COMPONENT Libraries
-              NAMELINK_COMPONENT Development
             PUBLIC_HEADER
-              COMPONENT Development
-           )
+              DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/myproj
+    )
 
-  In this scenario, if you choose to install only the ``Development``
-  component, both the headers and namelink will be installed without the
-  library. (If you don't also install the ``Libraries`` component, the
-  namelink will be a dangling symlink, and projects that link to the library
-  will have build errors.) If you install only the ``Libraries`` component,
-  only the library will be installed, without the headers and namelink.
+  In addition to the `common options`_ listed above, each target can accept
+  the following additional arguments:
 
-  This option is typically used for package managers that have separate
-  runtime and development packages. For example, on Debian systems, the
-  library is expected to be in the runtime package, and the headers and
-  namelink are expected to be in the development package.
+  ``NAMELINK_COMPONENT``
+    .. versionadded:: 3.12
 
-  See the :prop_tgt:`VERSION` and :prop_tgt:`SOVERSION` target properties for
-  details on creating versioned shared libraries.
+    On some platforms a versioned shared library has a symbolic link such
+    as::
 
-``NAMELINK_ONLY``
-  This option causes the installation of only the namelink when a library
-  target is installed. On platforms where versioned shared libraries do not
-  have namelinks or when a library is not versioned, the ``NAMELINK_ONLY``
-  option installs nothing. It is an error to use this parameter outside of a
-  ``LIBRARY`` block.
+      lib<name>.so -> lib<name>.so.1
 
-  .. versionchanged:: 3.27
-    This parameter is also usable for an ``ARCHIVE`` block to manage
-    the linker import file created, on macOS, for shared libraries with
-    :prop_tgt:`ENABLE_EXPORTS` enabled.
+    where ``lib<name>.so.1`` is the soname of the library and ``lib<name>.so``
+    is a "namelink" allowing linkers to find the library when given
+    ``-l<name>``. The ``NAMELINK_COMPONENT`` option is similar to the
+    ``COMPONENT`` option, but it changes the installation component of a shared
+    library namelink if one is generated. If not specified, this defaults to the
+    value of ``COMPONENT``. It is an error to use this parameter outside of a
+    ``LIBRARY`` block.
 
-  When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or
-  ``COMPONENT`` may be used to specify the installation component of the
-  namelink, but ``COMPONENT`` should generally be preferred.
+    .. versionchanged:: 3.27
+      This parameter is also usable for an ``ARCHIVE`` block to manage
+      the linker import file created, on macOS, for shared libraries with
+      :prop_tgt:`ENABLE_EXPORTS` enabled.
 
-``NAMELINK_SKIP``
-  Similar to ``NAMELINK_ONLY``, but it has the opposite effect: it causes the
-  installation of library files other than the namelink when a library target
-  is installed. When neither ``NAMELINK_ONLY`` or ``NAMELINK_SKIP`` are given,
-  both portions are installed. On platforms where versioned shared libraries
-  do not have symlinks or when a library is not versioned, ``NAMELINK_SKIP``
-  installs the library. It is an error to use this parameter outside of a
-  ``LIBRARY`` block.
+    See the `Example: Install Targets with Per-Artifact Components`_
+    for an example using ``NAMELINK_COMPONENT``.
 
-  .. versionchanged:: 3.27
-    This parameter is also usable for an ``ARCHIVE`` block to manage
-    the linker import file created, on macOS, for shared libraries with
-    :prop_tgt:`ENABLE_EXPORTS` enabled.
+    This option is typically used for package managers that have separate
+    runtime and development packages. For example, on Debian systems, the
+    library is expected to be in the runtime package, and the headers and
+    namelink are expected to be in the development package.
 
-  If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It
-  is not recommended to use ``NAMELINK_SKIP`` in conjunction with
-  ``NAMELINK_COMPONENT``.
+    See the :prop_tgt:`VERSION` and :prop_tgt:`SOVERSION` target properties for
+    details on creating versioned shared libraries.
 
-The `install(TARGETS)`_ command can also accept the following options at the
-top level:
+  ``NAMELINK_ONLY``
+    This option causes the installation of only the namelink when a library
+    target is installed. On platforms where versioned shared libraries do not
+    have namelinks or when a library is not versioned, the ``NAMELINK_ONLY``
+    option installs nothing. It is an error to use this parameter outside of a
+    ``LIBRARY`` block.
 
-``EXPORT``
-  This option associates the installed target files with an export called
-  ``<export-name>``.  It must appear before any target options.  To actually
-  install the export file itself, call `install(EXPORT)`_, documented below.
-  See documentation of the :prop_tgt:`EXPORT_NAME` target property to change
-  the name of the exported target.
+    .. versionchanged:: 3.27
+      This parameter is also usable for an ``ARCHIVE`` block to manage
+      the linker import file created, on macOS, for shared libraries with
+      :prop_tgt:`ENABLE_EXPORTS` enabled.
 
-  If ``EXPORT`` is used and the targets include ``PUBLIC`` or ``INTERFACE``
-  file sets, all of them must be specified with ``FILE_SET`` arguments. All
-  ``PUBLIC`` or ``INTERFACE`` file sets associated with a target are included
-  in the export.
+    When ``NAMELINK_ONLY`` is given, either ``NAMELINK_COMPONENT`` or
+    ``COMPONENT`` may be used to specify the installation component of the
+    namelink, but ``COMPONENT`` should generally be preferred.
 
-``INCLUDES DESTINATION``
-  This option specifies a list of directories which will be added to the
-  :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` target property of the
-  ``<targets>`` when exported by the `install(EXPORT)`_ command. If a
-  relative path is specified, it is treated as relative to the
-  :genex:`$<INSTALL_PREFIX>`.
+  ``NAMELINK_SKIP``
+    Similar to ``NAMELINK_ONLY``, but it has the opposite effect: it causes the
+    installation of library files other than the namelink when a library target
+    is installed. When neither ``NAMELINK_ONLY`` or ``NAMELINK_SKIP`` are given,
+    both portions are installed. On platforms where versioned shared libraries
+    do not have symlinks or when a library is not versioned, ``NAMELINK_SKIP``
+    installs the library. It is an error to use this parameter outside of a
+    ``LIBRARY`` block.
 
-``RUNTIME_DEPENDENCY_SET``
+    .. versionchanged:: 3.27
+      This parameter is also usable for an ``ARCHIVE`` block to manage
+      the linker import file created, on macOS, for shared libraries with
+      :prop_tgt:`ENABLE_EXPORTS` enabled.
+
+    If ``NAMELINK_SKIP`` is specified, ``NAMELINK_COMPONENT`` has no effect. It
+    is not recommended to use ``NAMELINK_SKIP`` in conjunction with
+    ``NAMELINK_COMPONENT``.
+
+  The :command:`install(TARGETS)` command can also accept the following
+  options at the top level:
+
+  ``EXPORT``
+    This option associates the installed target files with an export called
+    ``<export-name>``.  It must appear before any target options.
+    To actually install the export file itself, call
+    :command:`install(EXPORT)`, documented below.
+    See documentation of the :prop_tgt:`EXPORT_NAME` target property to change
+    the name of the exported target.
+
+    If ``EXPORT`` is used and the targets include ``PUBLIC`` or ``INTERFACE``
+    file sets, all of them must be specified with ``FILE_SET`` arguments. All
+    ``PUBLIC`` or ``INTERFACE`` file sets associated with a target are included
+    in the export.
+
+  ``INCLUDES DESTINATION``
+    This option specifies a list of directories which will be added to the
+    :prop_tgt:`INTERFACE_INCLUDE_DIRECTORIES` target property of the
+    ``<targets>`` when exported by the :command:`install(EXPORT)` command.
+    If a relative path is specified, it is treated as relative to the
+    :genex:`$<INSTALL_PREFIX>`.
+
+  ``RUNTIME_DEPENDENCY_SET <set-name>``
+    .. versionadded:: 3.21
+
+    This option causes all runtime dependencies of installed executable, shared
+    library, and module targets to be added to the specified runtime dependency
+    set. This set can then be installed with an
+    :command:`install(RUNTIME_DEPENDENCY_SET)` command.
+
+    This keyword and the ``RUNTIME_DEPENDENCIES`` keyword are mutually
+    exclusive.
+
+  ``RUNTIME_DEPENDENCIES <arg>...``
+    .. versionadded:: 3.21
+
+    This option causes all runtime dependencies of installed executable, shared
+    library, and module targets to be installed along with the targets
+    themselves. The ``RUNTIME``, ``LIBRARY``, ``FRAMEWORK``, and generic
+    arguments are used to determine the properties (``DESTINATION``,
+    ``COMPONENT``, etc.) of the installation of these dependencies.
+
+    ``RUNTIME_DEPENDENCIES`` is semantically equivalent to the following pair
+    of calls:
+
+    .. code-block:: cmake
+
+      install(TARGETS ... RUNTIME_DEPENDENCY_SET <set-name>)
+      install(RUNTIME_DEPENDENCY_SET <set-name> <arg>...)
+
+    where ``<set-name>`` will be a randomly generated set name.
+    ``<arg>...`` may include any of the following keywords supported by
+    the :command:`install(RUNTIME_DEPENDENCY_SET)` command:
+
+    * ``DIRECTORIES``
+    * ``PRE_INCLUDE_REGEXES``
+    * ``PRE_EXCLUDE_REGEXES``
+    * ``POST_INCLUDE_REGEXES``
+    * ``POST_EXCLUDE_REGEXES``
+    * ``POST_INCLUDE_FILES``
+    * ``POST_EXCLUDE_FILES``
+
+    The ``RUNTIME_DEPENDENCIES`` and ``RUNTIME_DEPENDENCY_SET`` keywords are
+    mutually exclusive.
+
+  :ref:`Interface Libraries` may be listed among the targets to install.
+  They install no artifacts but will be included in an associated ``EXPORT``.
+  If :ref:`Object Libraries` are listed but given no destination for their
+  object files, they will be exported as :ref:`Interface Libraries`.
+  This is sufficient to satisfy transitive usage requirements of other
+  targets that link to the object libraries in their implementation.
+
+  Installing a target with the :prop_tgt:`EXCLUDE_FROM_ALL` target property
+  set to ``TRUE`` has undefined behavior.
+
+  .. versionadded:: 3.3
+    An install destination given as a ``DESTINATION`` argument may
+    use "generator expressions" with the syntax ``$<...>``.  See the
+    :manual:`cmake-generator-expressions(7)` manual for available expressions.
+
+  .. versionadded:: 3.13
+    :command:`install(TARGETS)` can install targets that were created in
+    other directories.  When using such cross-directory install rules, running
+    ``make install`` (or similar) from a subdirectory will not guarantee that
+    targets from other directories are up-to-date.  You can use
+    :command:`target_link_libraries` or :command:`add_dependencies`
+    to ensure that such out-of-directory targets are built before the
+    subdirectory-specific install rules are run.
+
+.. signature::
+  install(IMPORTED_RUNTIME_ARTIFACTS <target>... [...])
+
   .. versionadded:: 3.21
 
-  This option causes all runtime dependencies of installed executable, shared
-  library, and module targets to be added to the specified runtime dependency
-  set. This set can then be installed with an
-  `install(RUNTIME_DEPENDENCY_SET)`_ command.
-
-  This keyword and the ``RUNTIME_DEPENDENCIES`` keyword are mutually
-  exclusive.
-
-``RUNTIME_DEPENDENCIES``
-  .. versionadded:: 3.21
-
-  This option causes all runtime dependencies of installed executable, shared
-  library, and module targets to be installed along with the targets
-  themselves. The ``RUNTIME``, ``LIBRARY``, ``FRAMEWORK``, and generic
-  arguments are used to determine the properties (``DESTINATION``,
-  ``COMPONENT``, etc.) of the installation of these dependencies.
-
-  ``RUNTIME_DEPENDENCIES`` is semantically equivalent to the following pair
-  of calls:
+  Install runtime artifacts of imported targets:
 
   .. code-block:: cmake
 
-    install(TARGETS ... RUNTIME_DEPENDENCY_SET <set-name>)
-    install(RUNTIME_DEPENDENCY_SET <set-name> args...)
+    install(IMPORTED_RUNTIME_ARTIFACTS <target>...
+            [RUNTIME_DEPENDENCY_SET <set-name>]
+            [[LIBRARY|RUNTIME|FRAMEWORK|BUNDLE]
+             [DESTINATION <dir>]
+             [PERMISSIONS <permission>...]
+             [CONFIGURATIONS <config>...]
+             [COMPONENT <component>]
+             [OPTIONAL] [EXCLUDE_FROM_ALL]
+            ] [...]
+            )
 
-  where ``<set-name>`` will be a randomly generated set name.
-  The ``args...`` may include any of the following keywords supported by
-  the `install(RUNTIME_DEPENDENCY_SET)`_ command:
+  The ``IMPORTED_RUNTIME_ARTIFACTS`` form specifies rules for installing the
+  runtime artifacts of imported targets. Projects may do this if they want to
+  bundle outside executables or modules inside their installation. The
+  ``LIBRARY``, ``RUNTIME``, ``FRAMEWORK``, and ``BUNDLE`` arguments have the
+  same semantics that they do in the `TARGETS`_ mode. Only the runtime artifacts
+  of imported targets are installed (except in the case of :prop_tgt:`FRAMEWORK`
+  libraries, :prop_tgt:`MACOSX_BUNDLE` executables, and :prop_tgt:`BUNDLE`
+  CFBundles.) For example, headers and import libraries associated with DLLs are
+  not installed. In the case of :prop_tgt:`FRAMEWORK` libraries,
+  :prop_tgt:`MACOSX_BUNDLE` executables, and :prop_tgt:`BUNDLE` CFBundles, the
+  entire directory is installed.
 
-  * ``DIRECTORIES``
-  * ``PRE_INCLUDE_REGEXES``
-  * ``PRE_EXCLUDE_REGEXES``
-  * ``POST_INCLUDE_REGEXES``
-  * ``POST_EXCLUDE_REGEXES``
-  * ``POST_INCLUDE_FILES``
-  * ``POST_EXCLUDE_FILES``
+  The ``RUNTIME_DEPENDENCY_SET`` option causes the runtime artifacts of the
+  imported executable, shared library, and module library ``targets`` to be
+  added to the ``<set-name>`` runtime dependency set. This set can then be
+  installed with an :command:`install(RUNTIME_DEPENDENCY_SET)` command.
 
-  The ``RUNTIME_DEPENDENCIES`` and ``RUNTIME_DEPENDENCY_SET`` keywords are
-  mutually exclusive.
+.. signature::
+  install(FILES <file>... [...])
+  install(PROGRAMS <program>... [...])
 
-One or more groups of properties may be specified in a single call to
-the ``TARGETS`` form of this command.  A target may be installed more than
-once to different locations.  Consider hypothetical targets ``myExe``,
-``mySharedLib``, and ``myStaticLib``.  The code:
+  .. note::
 
-.. code-block:: cmake
+    If installing header files, consider using file sets defined by
+    :command:`target_sources(FILE_SET)` instead. File sets associate
+    headers with a target and they install as part of the target.
 
-  install(TARGETS myExe mySharedLib myStaticLib
-          RUNTIME DESTINATION bin
-          LIBRARY DESTINATION lib
-          ARCHIVE DESTINATION lib/static)
-  install(TARGETS mySharedLib DESTINATION /some/full/path)
+  Install files or programs:
 
-will install ``myExe`` to ``<prefix>/bin`` and ``myStaticLib`` to
-``<prefix>/lib/static``.  On non-DLL platforms ``mySharedLib`` will be
-installed to ``<prefix>/lib`` and ``/some/full/path``.  On DLL platforms
-the ``mySharedLib`` DLL will be installed to ``<prefix>/bin`` and
-``/some/full/path`` and its import library will be installed to
-``<prefix>/lib/static`` and ``/some/full/path``.
+  .. code-block:: cmake
 
-:ref:`Interface Libraries` may be listed among the targets to install.
-They install no artifacts but will be included in an associated ``EXPORT``.
-If :ref:`Object Libraries` are listed but given no destination for their
-object files, they will be exported as :ref:`Interface Libraries`.
-This is sufficient to satisfy transitive usage requirements of other
-targets that link to the object libraries in their implementation.
+    install(<FILES|PROGRAMS> <file>...
+            TYPE <type> | DESTINATION <dir>
+            [PERMISSIONS <permission>...]
+            [CONFIGURATIONS <config>...]
+            [COMPONENT <component>]
+            [RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])
 
-Installing a target with the :prop_tgt:`EXCLUDE_FROM_ALL` target property
-set to ``TRUE`` has undefined behavior.
+  The ``FILES`` form specifies rules for installing files for a project.
+  File names given as relative paths are interpreted with respect to the
+  current source directory.  Files installed by this form are by default
+  given permissions ``OWNER_WRITE``, ``OWNER_READ``, ``GROUP_READ``, and
+  ``WORLD_READ`` if no ``PERMISSIONS`` argument is given.
 
-.. versionadded:: 3.3
-  An install destination given as a ``DESTINATION`` argument may
-  use "generator expressions" with the syntax ``$<...>``.  See the
+  The ``PROGRAMS`` form is identical to the ``FILES`` form except that the
+  default permissions for the installed file also include ``OWNER_EXECUTE``,
+  ``GROUP_EXECUTE``, and ``WORLD_EXECUTE``.  This form is intended to install
+  programs that are not targets, such as shell scripts.  Use the ``TARGETS``
+  form to install targets built within the project.
+
+  The list of ``files...`` given to ``FILES`` or ``PROGRAMS`` may use
+  "generator expressions" with the syntax ``$<...>``.  See the
   :manual:`cmake-generator-expressions(7)` manual for available expressions.
+  However, if any item begins in a generator expression it must evaluate
+  to a full path.
 
-.. versionadded:: 3.13
-  `install(TARGETS)`_ can install targets that were created in
-  other directories.  When using such cross-directory install rules, running
-  ``make install`` (or similar) from a subdirectory will not guarantee that
-  targets from other directories are up-to-date.  You can use
-  :command:`target_link_libraries` or :command:`add_dependencies`
-  to ensure that such out-of-directory targets are built before the
-  subdirectory-specific install rules are run.
+  Either a ``TYPE`` or a ``DESTINATION`` must be provided, but not both.
+  A ``TYPE`` argument specifies the generic file type of the files being
+  installed.  A destination will then be set automatically by taking the
+  corresponding variable from :module:`GNUInstallDirs`, or by using a
+  built-in default if that variable is not defined.  See the table below for
+  the supported file types and their corresponding variables and built-in
+  defaults.  Projects can provide a ``DESTINATION`` argument instead of a
+  file type if they wish to explicitly define the install destination.
 
-Installing Imported Runtime Artifacts
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  ======================= ================================== =========================
+     ``TYPE`` Argument         GNUInstallDirs Variable           Built-In Default
+  ======================= ================================== =========================
+  ``BIN``                 ``${CMAKE_INSTALL_BINDIR}``        ``bin``
+  ``SBIN``                ``${CMAKE_INSTALL_SBINDIR}``       ``sbin``
+  ``LIB``                 ``${CMAKE_INSTALL_LIBDIR}``        ``lib``
+  ``INCLUDE``             ``${CMAKE_INSTALL_INCLUDEDIR}``    ``include``
+  ``SYSCONF``             ``${CMAKE_INSTALL_SYSCONFDIR}``    ``etc``
+  ``SHAREDSTATE``         ``${CMAKE_INSTALL_SHARESTATEDIR}`` ``com``
+  ``LOCALSTATE``          ``${CMAKE_INSTALL_LOCALSTATEDIR}`` ``var``
+  ``RUNSTATE``            ``${CMAKE_INSTALL_RUNSTATEDIR}``   ``<LOCALSTATE dir>/run``
+  ``DATA``                ``${CMAKE_INSTALL_DATADIR}``       ``<DATAROOT dir>``
+  ``INFO``                ``${CMAKE_INSTALL_INFODIR}``       ``<DATAROOT dir>/info``
+  ``LOCALE``              ``${CMAKE_INSTALL_LOCALEDIR}``     ``<DATAROOT dir>/locale``
+  ``MAN``                 ``${CMAKE_INSTALL_MANDIR}``        ``<DATAROOT dir>/man``
+  ``DOC``                 ``${CMAKE_INSTALL_DOCDIR}``        ``<DATAROOT dir>/doc``
+  ======================= ================================== =========================
 
-.. _`install(IMPORTED_RUNTIME_ARTIFACTS)`:
-.. _IMPORTED_RUNTIME_ARTIFACTS:
+  Projects wishing to follow the common practice of installing headers into a
+  project-specific subdirectory will need to provide a destination rather than
+  rely on the above. Using file sets for headers instead of ``install(FILES)``
+  would be even better (see :command:`target_sources(FILE_SET)`).
 
-.. versionadded:: 3.21
+  Note that some of the types' built-in defaults use the ``DATAROOT`` directory as
+  a prefix. The ``DATAROOT`` prefix is calculated similarly to the types, with
+  ``CMAKE_INSTALL_DATAROOTDIR`` as the variable and ``share`` as the built-in
+  default. You cannot use ``DATAROOT`` as a ``TYPE`` parameter; please use
+  ``DATA`` instead.
+
+  To make packages compliant with distribution filesystem layout policies, if
+  projects must specify a ``DESTINATION``, it is recommended that they use a
+  path that begins with the appropriate :module:`GNUInstallDirs` variable.
+  This allows package maintainers to control the install destination by setting
+  the appropriate cache variables.  The following example shows how to follow
+  this advice while installing an image to a project-specific documentation
+  subdirectory:
+
+  .. code-block:: cmake
+
+    include(GNUInstallDirs)
+    install(FILES logo.png
+            DESTINATION ${CMAKE_INSTALL_DOCDIR}/myproj
+    )
+
+  .. versionadded:: 3.4
+    An install destination given as a ``DESTINATION`` argument may
+    use "generator expressions" with the syntax ``$<...>``.  See the
+    :manual:`cmake-generator-expressions(7)` manual for available expressions.
+
+  .. versionadded:: 3.20
+    An install rename given as a ``RENAME`` argument may
+    use "generator expressions" with the syntax ``$<...>``.  See the
+    :manual:`cmake-generator-expressions(7)` manual for available expressions.
+
+.. signature::
+  install(DIRECTORY <dir>... [...])
+
+  .. note::
+
+    To install a directory sub-tree of headers, consider using file sets
+    defined by :command:`target_sources(FILE_SET)` instead. File sets not only
+    preserve directory structure, they also associate headers with a target
+    and install as part of the target.
+
+  Install the contents of one or more directories:
+
+  .. code-block:: cmake
+
+    install(DIRECTORY dirs...
+            TYPE <type> | DESTINATION <dir>
+            [FILE_PERMISSIONS <permission>...]
+            [DIRECTORY_PERMISSIONS <permission>...]
+            [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
+            [CONFIGURATIONS <config>...]
+            [COMPONENT <component>] [EXCLUDE_FROM_ALL]
+            [FILES_MATCHING]
+            [[PATTERN <pattern> | REGEX <regex>]
+             [EXCLUDE] [PERMISSIONS <permission>...]] [...])
+
+  The ``DIRECTORY`` form installs contents of one or more directories to a
+  given destination.  The directory structure is copied verbatim to the
+  destination.  The last component of each directory name is appended to
+  the destination directory but a trailing slash may be used to avoid
+  this because it leaves the last component empty.  Directory names
+  given as relative paths are interpreted with respect to the current
+  source directory.  If no input directory names are given the
+  destination directory will be created but nothing will be installed
+  into it.  The ``FILE_PERMISSIONS`` and ``DIRECTORY_PERMISSIONS`` options
+  specify permissions given to files and directories in the destination.
+  If ``USE_SOURCE_PERMISSIONS`` is specified and ``FILE_PERMISSIONS`` is not,
+  file permissions will be copied from the source directory structure.
+  If no permissions are specified files will be given the default
+  permissions specified in the ``FILES`` form of the command, and the
+  directories will be given the default permissions specified in the
+  ``PROGRAMS`` form of the command.
+
+  .. versionadded:: 3.1
+    The ``MESSAGE_NEVER`` option disables file installation status output.
+
+  Installation of directories may be controlled with fine granularity
+  using the ``PATTERN`` or ``REGEX`` options.  These "match" options specify a
+  globbing pattern or regular expression to match directories or files
+  encountered within input directories.  They may be used to apply
+  certain options (see below) to a subset of the files and directories
+  encountered.  The full path to each input file or directory (with
+  forward slashes) is matched against the expression.  A ``PATTERN`` will
+  match only complete file names: the portion of the full path matching
+  the pattern must occur at the end of the file name and be preceded by
+  a slash.  A ``REGEX`` will match any portion of the full path but it may
+  use ``/`` and ``$`` to simulate the ``PATTERN`` behavior.  By default all
+  files and directories are installed whether or not they are matched.
+  The ``FILES_MATCHING`` option may be given before the first match option
+  to disable installation of files (but not directories) not matched by
+  any expression.  For example, the code
+
+  .. code-block:: cmake
+
+    install(DIRECTORY src/ DESTINATION doc/myproj
+            FILES_MATCHING PATTERN "*.png")
+
+  will extract and install images from a source tree.
+
+  Some options may follow a ``PATTERN`` or ``REGEX`` expression as described
+  under :ref:`string(REGEX) <Regex Specification>` and are applied
+  only to files or directories matching them.  The ``EXCLUDE`` option will
+  skip the matched file or directory.  The ``PERMISSIONS`` option overrides
+  the permissions setting for the matched file or directory.  For
+  example the code
+
+  .. code-block:: cmake
+
+    install(DIRECTORY icons scripts/ DESTINATION share/myproj
+            PATTERN "CVS" EXCLUDE
+            PATTERN "scripts/*"
+            PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
+                        GROUP_EXECUTE GROUP_READ)
+
+  will install the ``icons`` directory to ``share/myproj/icons`` and the
+  ``scripts`` directory to ``share/myproj``.  The icons will get default
+  file permissions, the scripts will be given specific permissions, and any
+  ``CVS`` directories will be excluded.
+
+  Either a ``TYPE`` or a ``DESTINATION`` must be provided, but not both.
+  A ``TYPE`` argument specifies the generic file type of the files within the
+  listed directories being installed.  A destination will then be set
+  automatically by taking the corresponding variable from
+  :module:`GNUInstallDirs`, or by using a built-in default if that variable
+  is not defined.  See the table below for the supported file types and their
+  corresponding variables and built-in defaults.  Projects can provide a
+  ``DESTINATION`` argument instead of a file type if they wish to explicitly
+  define the install destination.
+
+  ======================= ================================== =========================
+     ``TYPE`` Argument         GNUInstallDirs Variable           Built-In Default
+  ======================= ================================== =========================
+  ``BIN``                 ``${CMAKE_INSTALL_BINDIR}``        ``bin``
+  ``SBIN``                ``${CMAKE_INSTALL_SBINDIR}``       ``sbin``
+  ``LIB``                 ``${CMAKE_INSTALL_LIBDIR}``        ``lib``
+  ``INCLUDE``             ``${CMAKE_INSTALL_INCLUDEDIR}``    ``include``
+  ``SYSCONF``             ``${CMAKE_INSTALL_SYSCONFDIR}``    ``etc``
+  ``SHAREDSTATE``         ``${CMAKE_INSTALL_SHARESTATEDIR}`` ``com``
+  ``LOCALSTATE``          ``${CMAKE_INSTALL_LOCALSTATEDIR}`` ``var``
+  ``RUNSTATE``            ``${CMAKE_INSTALL_RUNSTATEDIR}``   ``<LOCALSTATE dir>/run``
+  ``DATA``                ``${CMAKE_INSTALL_DATADIR}``       ``<DATAROOT dir>``
+  ``INFO``                ``${CMAKE_INSTALL_INFODIR}``       ``<DATAROOT dir>/info``
+  ``LOCALE``              ``${CMAKE_INSTALL_LOCALEDIR}``     ``<DATAROOT dir>/locale``
+  ``MAN``                 ``${CMAKE_INSTALL_MANDIR}``        ``<DATAROOT dir>/man``
+  ``DOC``                 ``${CMAKE_INSTALL_DOCDIR}``        ``<DATAROOT dir>/doc``
+  ======================= ================================== =========================
+
+  Note that some of the types' built-in defaults use the ``DATAROOT`` directory as
+  a prefix. The ``DATAROOT`` prefix is calculated similarly to the types, with
+  ``CMAKE_INSTALL_DATAROOTDIR`` as the variable and ``share`` as the built-in
+  default. You cannot use ``DATAROOT`` as a ``TYPE`` parameter; please use
+  ``DATA`` instead.
+
+  To make packages compliant with distribution filesystem layout policies, if
+  projects must specify a ``DESTINATION``, it is recommended that they use a
+  path that begins with the appropriate :module:`GNUInstallDirs` variable.
+  This allows package maintainers to control the install destination by setting
+  the appropriate cache variables.
+
+  .. versionadded:: 3.4
+    An install destination given as a ``DESTINATION`` argument may
+    use "generator expressions" with the syntax ``$<...>``.  See the
+    :manual:`cmake-generator-expressions(7)` manual for available expressions.
+
+  .. versionadded:: 3.5
+    The list of ``dirs...`` given to ``DIRECTORY`` may use
+    "generator expressions" too.
+
+.. signature::
+  install(SCRIPT <file> [...])
+  install(CODE <code> [...])
+
+  Invoke CMake scripts or code during installation:
+
+  .. code-block:: cmake
+
+    install([[SCRIPT <file>] [CODE <code>]]
+            [ALL_COMPONENTS | COMPONENT <component>]
+            [EXCLUDE_FROM_ALL] [...])
+
+  The ``SCRIPT`` form will invoke the given CMake script files during
+  installation.  If the script file name is a relative path it will be
+  interpreted with respect to the current source directory.  The ``CODE``
+  form will invoke the given CMake code during installation.  Code is
+  specified as a single argument inside a double-quoted string.  For
+  example, the code
+
+  .. code-block:: cmake
+
+    install(CODE "MESSAGE(\"Sample install message.\")")
+
+  will print a message during installation.
+
+  .. versionadded:: 3.21
+    When the ``ALL_COMPONENTS`` option is given, the custom installation
+    script code will be executed for every component of a component-specific
+    installation.  This option is mutually exclusive with the ``COMPONENT``
+    option.
+
+  .. versionadded:: 3.14
+    ``<file>`` or ``<code>`` may use "generator expressions" with the syntax
+    ``$<...>`` (in the case of ``<file>``, this refers to their use in the file
+    name, not the file's contents).  See the
+    :manual:`cmake-generator-expressions(7)` manual for available expressions.
+
+.. signature::
+  install(EXPORT <export-name> [...])
+
+  Install a CMake file exporting targets for dependent projects:
+
+  .. code-block:: cmake
+
+    install(EXPORT <export-name> DESTINATION <dir>
+            [NAMESPACE <namespace>] [FILE <name>.cmake]
+            [PERMISSIONS <permission>...]
+            [CONFIGURATIONS <config>...]
+            [CXX_MODULES_DIRECTORY <directory>]
+            [EXPORT_LINK_INTERFACE_LIBRARIES]
+            [COMPONENT <component>]
+            [EXCLUDE_FROM_ALL])
+    install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])
+
+  The ``EXPORT`` form generates and installs a CMake file containing code to
+  import targets from the installation tree into another project.
+  Target installations are associated with the export ``<export-name>``
+  using the ``EXPORT`` option of the :command:`install(TARGETS)` signature
+  documented above.  The ``NAMESPACE`` option will prepend ``<namespace>`` to
+  the target names as they are written to the import file.  By default
+  the generated file will be called ``<export-name>.cmake`` but the ``FILE``
+  option may be used to specify a different name.  The value given to
+  the ``FILE`` option must be a file name with the ``.cmake`` extension.
+  If a ``CONFIGURATIONS`` option is given then the file will only be installed
+  when one of the named configurations is installed.  Additionally, the
+  generated import file will reference only the matching target
+  configurations.  See the :variable:`CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>`
+  variable to map configurations of dependent projects to the installed
+  configurations.  The ``EXPORT_LINK_INTERFACE_LIBRARIES`` keyword, if
+  present, causes the contents of the properties matching
+  ``(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)?`` to be exported, when
+  policy :policy:`CMP0022` is ``NEW``.
+
+  .. note::
+    The installed ``<export-name>.cmake`` file may come with additional
+    per-configuration ``<export-name>-*.cmake`` files to be loaded by
+    globbing.  Do not use an export name that is the same as the package
+    name in combination with installing a ``<package-name>-config.cmake``
+    file or the latter may be incorrectly matched by the glob and loaded.
+
+  When a ``COMPONENT`` option is given, the listed ``<component>`` implicitly
+  depends on all components mentioned in the export set. The exported
+  ``<name>.cmake`` file will require each of the exported components to be
+  present in order for dependent projects to build properly. For example, a
+  project may define components ``Runtime`` and ``Development``, with shared
+  libraries going into the ``Runtime`` component and static libraries and
+  headers going into the ``Development`` component. The export set would also
+  typically be part of the ``Development`` component, but it would export
+  targets from both the ``Runtime`` and ``Development`` components. Therefore,
+  the ``Runtime`` component would need to be installed if the ``Development``
+  component was installed, but not vice versa. If the ``Development`` component
+  was installed without the ``Runtime`` component, dependent projects that try
+  to link against it would have build errors. Package managers, such as APT and
+  RPM, typically handle this by listing the ``Runtime`` component as a dependency
+  of the ``Development`` component in the package metadata, ensuring that the
+  library is always installed if the headers and CMake export file are present.
+
+  .. versionadded:: 3.7
+    In addition to cmake language files, the ``EXPORT_ANDROID_MK`` mode may be
+    used to specify an export to the android ndk build system.  This mode
+    accepts the same options as the normal export mode.  The Android
+    NDK supports the use of prebuilt libraries, both static and shared. This
+    allows cmake to build the libraries of a project and make them available
+    to an ndk build system complete with transitive dependencies, include flags
+    and defines required to use the libraries.
+
+  ``CXX_MODULES_DIRECTORY``
+    .. versionadded:: 3.28
+
+    Specify a subdirectory to store C++ module information for targets in the
+    export set. This directory will be populated with files which add the
+    necessary target property information to the relevant targets. Note that
+    without this information, none of the C++ modules which are part of the
+    targets in the export set will support being imported in consuming targets.
+
+  The ``EXPORT`` form is useful to help outside projects use targets built
+  and installed by the current project.  For example, the code
+
+  .. code-block:: cmake
+
+    install(TARGETS myexe EXPORT myproj DESTINATION bin)
+    install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj)
+    install(EXPORT_ANDROID_MK myproj DESTINATION share/ndk-modules)
+
+  will install the executable ``myexe`` to ``<prefix>/bin`` and code to import
+  it in the file ``<prefix>/lib/myproj/myproj.cmake`` and
+  ``<prefix>/share/ndk-modules/Android.mk``.  An outside project
+  may load this file with the include command and reference the ``myexe``
+  executable from the installation tree using the imported target name
+  ``mp_myexe`` as if the target were built in its own tree.
+
+  .. note::
+    This command supersedes the :command:`install_targets` command and
+    the :prop_tgt:`PRE_INSTALL_SCRIPT` and :prop_tgt:`POST_INSTALL_SCRIPT`
+    target properties.  It also replaces the ``FILES`` forms of the
+    :command:`install_files` and :command:`install_programs` commands.
+    The processing order of these install rules relative to
+    those generated by :command:`install_targets`,
+    :command:`install_files`, and :command:`install_programs` commands
+    is not defined.
+
+.. signature::
+  install(RUNTIME_DEPENDENCY_SET <set-name> [...])
+
+  .. versionadded:: 3.21
+
+  Installs a runtime dependency set:
+
+  .. code-block:: cmake
+
+    install(RUNTIME_DEPENDENCY_SET <set-name>
+            [[LIBRARY|RUNTIME|FRAMEWORK]
+             [DESTINATION <dir>]
+             [PERMISSIONS <permission>...]
+             [CONFIGURATIONS <config>...]
+             [COMPONENT <component>]
+             [NAMELINK_COMPONENT <component>]
+             [OPTIONAL] [EXCLUDE_FROM_ALL]
+            ] [...]
+            [PRE_INCLUDE_REGEXES <regex>...]
+            [PRE_EXCLUDE_REGEXES <regex>...]
+            [POST_INCLUDE_REGEXES <regex>...]
+            [POST_EXCLUDE_REGEXES <regex>...]
+            [POST_INCLUDE_FILES <file>...]
+            [POST_EXCLUDE_FILES <file>...]
+            [DIRECTORIES <dir>...]
+            )
+
+  Installs a runtime dependency set previously created by one or more
+  :command:`install(TARGETS)` or :command:`install(IMPORTED_RUNTIME_ARTIFACTS)`
+  commands.  The dependencies of targets belonging to a runtime dependency set
+  are installed in the ``RUNTIME`` destination and component on DLL platforms,
+  and in the ``LIBRARY`` destination and component on non-DLL platforms.
+  macOS frameworks are installed in the ``FRAMEWORK`` destination and component.
+  Targets built within the build tree will never be installed as runtime
+  dependencies, nor will their own dependencies, unless the targets themselves
+  are installed with :command:`install(TARGETS)`.
+
+  The generated install script calls :command:`file(GET_RUNTIME_DEPENDENCIES)`
+  on the build-tree files to calculate the runtime dependencies. The build-tree
+  executable files are passed as the ``EXECUTABLES`` argument, the build-tree
+  shared libraries as the ``LIBRARIES`` argument, and the build-tree modules as
+  the ``MODULES`` argument. On macOS, if one of the executables is a
+  :prop_tgt:`MACOSX_BUNDLE`, that executable is passed as the
+  ``BUNDLE_EXECUTABLE`` argument. At most one such bundle executable may be in
+  the runtime dependency set on macOS. The :prop_tgt:`MACOSX_BUNDLE` property
+  has no effect on other platforms. Note that
+  :command:`file(GET_RUNTIME_DEPENDENCIES)` only supports collecting the runtime
+  dependencies for Windows, Linux and macOS platforms, so
+  ``install(RUNTIME_DEPENDENCY_SET)`` has the same limitation.
+
+  The following sub-arguments are forwarded through as the corresponding
+  arguments to :command:`file(GET_RUNTIME_DEPENDENCIES)` (for those that provide
+  a non-empty list of directories, regular expressions or files).  They all
+  support :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+  * ``DIRECTORIES <dir>...``
+  * ``PRE_INCLUDE_REGEXES <regex>...``
+  * ``PRE_EXCLUDE_REGEXES <regex>...``
+  * ``POST_INCLUDE_REGEXES <regex>...``
+  * ``POST_EXCLUDE_REGEXES <regex>...``
+  * ``POST_INCLUDE_FILES <file>...``
+  * ``POST_EXCLUDE_FILES <file>...``
+
+Examples
+^^^^^^^^
+
+Example: Install Targets with Per-Artifact Components
+"""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+Consider a project that defines targets with different artifact kinds:
 
 .. code-block:: cmake
 
-  install(IMPORTED_RUNTIME_ARTIFACTS targets...
-          [RUNTIME_DEPENDENCY_SET <set-name>]
-          [[LIBRARY|RUNTIME|FRAMEWORK|BUNDLE]
-           [DESTINATION <dir>]
-           [PERMISSIONS permissions...]
-           [CONFIGURATIONS [Debug|Release|...]]
-           [COMPONENT <component>]
-           [OPTIONAL] [EXCLUDE_FROM_ALL]
-          ] [...]
+  add_executable(myExe myExe.c)
+  add_library(myStaticLib STATIC myStaticLib.c)
+  target_sources(myStaticLib PUBLIC FILE_SET HEADERS FILES myStaticLib.h)
+  add_library(mySharedLib SHARED mySharedLib.c)
+  target_sources(mySharedLib PUBLIC FILE_SET HEADERS FILES mySharedLib.h)
+  set_property(TARGET mySharedLib PROPERTY SOVERSION 1)
+
+We may call :command:`install(TARGETS)` with `\<artifact-kind\>`_ arguments
+to specify different options for each kind of artifact:
+
+.. code-block:: cmake
+
+  install(TARGETS
+            myExe
+            mySharedLib
+            myStaticLib
+          RUNTIME           # Following options apply to runtime artifacts.
+            COMPONENT Runtime
+          LIBRARY           # Following options apply to library artifacts.
+            COMPONENT Runtime
+            NAMELINK_COMPONENT Development
+          ARCHIVE           # Following options apply to archive artifacts.
+            COMPONENT Development
+            DESTINATION lib/static
+          FILE_SET HEADERS  # Following options apply to file set HEADERS.
+            COMPONENT Development
           )
 
-The ``IMPORTED_RUNTIME_ARTIFACTS`` form specifies rules for installing the
-runtime artifacts of imported targets. Projects may do this if they want to
-bundle outside executables or modules inside their installation. The
-``LIBRARY``, ``RUNTIME``, ``FRAMEWORK``, and ``BUNDLE`` arguments have the
-same semantics that they do in the `TARGETS`_ mode. Only the runtime artifacts
-of imported targets are installed (except in the case of :prop_tgt:`FRAMEWORK`
-libraries, :prop_tgt:`MACOSX_BUNDLE` executables, and :prop_tgt:`BUNDLE`
-CFBundles.) For example, headers and import libraries associated with DLLs are
-not installed. In the case of :prop_tgt:`FRAMEWORK` libraries,
-:prop_tgt:`MACOSX_BUNDLE` executables, and :prop_tgt:`BUNDLE` CFBundles, the
-entire directory is installed.
+This will:
 
-The ``RUNTIME_DEPENDENCY_SET`` option causes the runtime artifacts of the
-imported executable, shared library, and module library ``targets`` to be
-added to the ``<set-name>`` runtime dependency set. This set can then be
-installed with an `install(RUNTIME_DEPENDENCY_SET)`_ command.
+* Install ``myExe`` to ``<prefix>/bin``, the default RUNTIME artifact
+  destination, as part of the ``Runtime`` component.
 
-Installing Files
-^^^^^^^^^^^^^^^^
+* On non-DLL platforms:
 
-.. _`install(FILES)`:
-.. _`install(PROGRAMS)`:
-.. _FILES:
-.. _PROGRAMS:
+  * Install ``libmySharedLib.so.1`` to ``<prefix>/lib``, the default
+    LIBRARY artifact destination, as part of the ``Runtime`` component.
 
-.. note::
+  * Install the ``libmySharedLib.so`` "namelink" (symbolic link) to
+    ``<prefix>/lib``, the default LIBRARY artifact destination, as part
+    of the ``Development`` component.
 
-  If installing header files, consider using file sets defined by
-  :command:`target_sources(FILE_SET)` instead. File sets associate
-  headers with a target and they install as part of the target.
+* On DLL platforms:
+
+  * Install ``mySharedLib.dll`` to ``<prefix>/bin``, the default RUNTIME
+    artifact destination, as part of the ``Runtime`` component.
+
+  * Install ``mySharedLib.lib`` to ``<prefix>/lib/static``, the specified
+    ARCHIVE artifact destination, as part of the ``Development`` component.
+
+* Install ``myStaticLib`` to ``<prefix>/lib/static``, the specified
+  ARCHIVE artifact destination, as part of the ``Development`` component.
+
+* Install ``mySharedLib.h`` and ``myStaticLib.h`` to ``<prefix>/include``,
+  the default destination for a file set of type HEADERS, as part of the
+  ``Development`` component.
+
+Example: Install Targets to Per-Config Destinations
+"""""""""""""""""""""""""""""""""""""""""""""""""""
+
+Each :command:`install(TARGETS)` call installs a given target
+:ref:`output artifact <Output Artifacts>` to at most one ``DESTINATION``,
+but the install rule itself may be filtered by the ``CONFIGURATIONS`` option.
+In order to install to a different destination for each configuration, one
+call per configuration is needed.  For example, the code:
 
 .. code-block:: cmake
 
-  install(<FILES|PROGRAMS> files...
-          TYPE <type> | DESTINATION <dir>
-          [PERMISSIONS permissions...]
-          [CONFIGURATIONS [Debug|Release|...]]
-          [COMPONENT <component>]
-          [RENAME <name>] [OPTIONAL] [EXCLUDE_FROM_ALL])
-
-The ``FILES`` form specifies rules for installing files for a project.
-File names given as relative paths are interpreted with respect to the
-current source directory.  Files installed by this form are by default
-given permissions ``OWNER_WRITE``, ``OWNER_READ``, ``GROUP_READ``, and
-``WORLD_READ`` if no ``PERMISSIONS`` argument is given.
-
-The ``PROGRAMS`` form is identical to the ``FILES`` form except that the
-default permissions for the installed file also include ``OWNER_EXECUTE``,
-``GROUP_EXECUTE``, and ``WORLD_EXECUTE``.  This form is intended to install
-programs that are not targets, such as shell scripts.  Use the ``TARGETS``
-form to install targets built within the project.
-
-The list of ``files...`` given to ``FILES`` or ``PROGRAMS`` may use
-"generator expressions" with the syntax ``$<...>``.  See the
-:manual:`cmake-generator-expressions(7)` manual for available expressions.
-However, if any item begins in a generator expression it must evaluate
-to a full path.
-
-Either a ``TYPE`` or a ``DESTINATION`` must be provided, but not both.
-A ``TYPE`` argument specifies the generic file type of the files being
-installed.  A destination will then be set automatically by taking the
-corresponding variable from :module:`GNUInstallDirs`, or by using a
-built-in default if that variable is not defined.  See the table below for
-the supported file types and their corresponding variables and built-in
-defaults.  Projects can provide a ``DESTINATION`` argument instead of a
-file type if they wish to explicitly define the install destination.
-
-======================= ================================== =========================
-   ``TYPE`` Argument         GNUInstallDirs Variable           Built-In Default
-======================= ================================== =========================
-``BIN``                 ``${CMAKE_INSTALL_BINDIR}``        ``bin``
-``SBIN``                ``${CMAKE_INSTALL_SBINDIR}``       ``sbin``
-``LIB``                 ``${CMAKE_INSTALL_LIBDIR}``        ``lib``
-``INCLUDE``             ``${CMAKE_INSTALL_INCLUDEDIR}``    ``include``
-``SYSCONF``             ``${CMAKE_INSTALL_SYSCONFDIR}``    ``etc``
-``SHAREDSTATE``         ``${CMAKE_INSTALL_SHARESTATEDIR}`` ``com``
-``LOCALSTATE``          ``${CMAKE_INSTALL_LOCALSTATEDIR}`` ``var``
-``RUNSTATE``            ``${CMAKE_INSTALL_RUNSTATEDIR}``   ``<LOCALSTATE dir>/run``
-``DATA``                ``${CMAKE_INSTALL_DATADIR}``       ``<DATAROOT dir>``
-``INFO``                ``${CMAKE_INSTALL_INFODIR}``       ``<DATAROOT dir>/info``
-``LOCALE``              ``${CMAKE_INSTALL_LOCALEDIR}``     ``<DATAROOT dir>/locale``
-``MAN``                 ``${CMAKE_INSTALL_MANDIR}``        ``<DATAROOT dir>/man``
-``DOC``                 ``${CMAKE_INSTALL_DOCDIR}``        ``<DATAROOT dir>/doc``
-======================= ================================== =========================
-
-Projects wishing to follow the common practice of installing headers into a
-project-specific subdirectory will need to provide a destination rather than
-rely on the above. Using file sets for headers instead of ``install(FILES)``
-would be even better (see :command:`target_sources(FILE_SET)`).
-
-Note that some of the types' built-in defaults use the ``DATAROOT`` directory as
-a prefix. The ``DATAROOT`` prefix is calculated similarly to the types, with
-``CMAKE_INSTALL_DATAROOTDIR`` as the variable and ``share`` as the built-in
-default. You cannot use ``DATAROOT`` as a ``TYPE`` parameter; please use
-``DATA`` instead.
-
-To make packages compliant with distribution filesystem layout policies, if
-projects must specify a ``DESTINATION``, it is recommended that they use a
-path that begins with the appropriate :module:`GNUInstallDirs` variable.
-This allows package maintainers to control the install destination by setting
-the appropriate cache variables.  The following example shows how to follow
-this advice while installing an image to a project-specific documentation
-subdirectory:
-
-.. code-block:: cmake
-
-  include(GNUInstallDirs)
-  install(FILES logo.png
-          DESTINATION ${CMAKE_INSTALL_DOCDIR}/myproj
-  )
-
-.. versionadded:: 3.4
-  An install destination given as a ``DESTINATION`` argument may
-  use "generator expressions" with the syntax ``$<...>``.  See the
-  :manual:`cmake-generator-expressions(7)` manual for available expressions.
-
-.. versionadded:: 3.20
-  An install rename given as a ``RENAME`` argument may
-  use "generator expressions" with the syntax ``$<...>``.  See the
-  :manual:`cmake-generator-expressions(7)` manual for available expressions.
-
-Installing Directories
-^^^^^^^^^^^^^^^^^^^^^^
-
-.. _`install(DIRECTORY)`:
-.. _DIRECTORY:
-
-.. note::
-
-  To install a directory sub-tree of headers, consider using file sets
-  defined by :command:`target_sources(FILE_SET)` instead. File sets not only
-  preserve directory structure, they also associate headers with a target
-  and install as part of the target.
-
-.. code-block:: cmake
-
-  install(DIRECTORY dirs...
-          TYPE <type> | DESTINATION <dir>
-          [FILE_PERMISSIONS permissions...]
-          [DIRECTORY_PERMISSIONS permissions...]
-          [USE_SOURCE_PERMISSIONS] [OPTIONAL] [MESSAGE_NEVER]
-          [CONFIGURATIONS [Debug|Release|...]]
-          [COMPONENT <component>] [EXCLUDE_FROM_ALL]
-          [FILES_MATCHING]
-          [[PATTERN <pattern> | REGEX <regex>]
-           [EXCLUDE] [PERMISSIONS permissions...]] [...])
-
-The ``DIRECTORY`` form installs contents of one or more directories to a
-given destination.  The directory structure is copied verbatim to the
-destination.  The last component of each directory name is appended to
-the destination directory but a trailing slash may be used to avoid
-this because it leaves the last component empty.  Directory names
-given as relative paths are interpreted with respect to the current
-source directory.  If no input directory names are given the
-destination directory will be created but nothing will be installed
-into it.  The ``FILE_PERMISSIONS`` and ``DIRECTORY_PERMISSIONS`` options
-specify permissions given to files and directories in the destination.
-If ``USE_SOURCE_PERMISSIONS`` is specified and ``FILE_PERMISSIONS`` is not,
-file permissions will be copied from the source directory structure.
-If no permissions are specified files will be given the default
-permissions specified in the ``FILES`` form of the command, and the
-directories will be given the default permissions specified in the
-``PROGRAMS`` form of the command.
-
-.. versionadded:: 3.1
-  The ``MESSAGE_NEVER`` option disables file installation status output.
-
-Installation of directories may be controlled with fine granularity
-using the ``PATTERN`` or ``REGEX`` options.  These "match" options specify a
-globbing pattern or regular expression to match directories or files
-encountered within input directories.  They may be used to apply
-certain options (see below) to a subset of the files and directories
-encountered.  The full path to each input file or directory (with
-forward slashes) is matched against the expression.  A ``PATTERN`` will
-match only complete file names: the portion of the full path matching
-the pattern must occur at the end of the file name and be preceded by
-a slash.  A ``REGEX`` will match any portion of the full path but it may
-use ``/`` and ``$`` to simulate the ``PATTERN`` behavior.  By default all
-files and directories are installed whether or not they are matched.
-The ``FILES_MATCHING`` option may be given before the first match option
-to disable installation of files (but not directories) not matched by
-any expression.  For example, the code
-
-.. code-block:: cmake
-
-  install(DIRECTORY src/ DESTINATION doc/myproj
-          FILES_MATCHING PATTERN "*.png")
-
-will extract and install images from a source tree.
-
-Some options may follow a ``PATTERN`` or ``REGEX`` expression as described
-under :ref:`string(REGEX) <Regex Specification>` and are applied
-only to files or directories matching them.  The ``EXCLUDE`` option will
-skip the matched file or directory.  The ``PERMISSIONS`` option overrides
-the permissions setting for the matched file or directory.  For
-example the code
-
-.. code-block:: cmake
-
-  install(DIRECTORY icons scripts/ DESTINATION share/myproj
-          PATTERN "CVS" EXCLUDE
-          PATTERN "scripts/*"
-          PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
-                      GROUP_EXECUTE GROUP_READ)
-
-will install the ``icons`` directory to ``share/myproj/icons`` and the
-``scripts`` directory to ``share/myproj``.  The icons will get default
-file permissions, the scripts will be given specific permissions, and any
-``CVS`` directories will be excluded.
-
-Either a ``TYPE`` or a ``DESTINATION`` must be provided, but not both.
-A ``TYPE`` argument specifies the generic file type of the files within the
-listed directories being installed.  A destination will then be set
-automatically by taking the corresponding variable from
-:module:`GNUInstallDirs`, or by using a built-in default if that variable
-is not defined.  See the table below for the supported file types and their
-corresponding variables and built-in defaults.  Projects can provide a
-``DESTINATION`` argument instead of a file type if they wish to explicitly
-define the install destination.
-
-======================= ================================== =========================
-   ``TYPE`` Argument         GNUInstallDirs Variable           Built-In Default
-======================= ================================== =========================
-``BIN``                 ``${CMAKE_INSTALL_BINDIR}``        ``bin``
-``SBIN``                ``${CMAKE_INSTALL_SBINDIR}``       ``sbin``
-``LIB``                 ``${CMAKE_INSTALL_LIBDIR}``        ``lib``
-``INCLUDE``             ``${CMAKE_INSTALL_INCLUDEDIR}``    ``include``
-``SYSCONF``             ``${CMAKE_INSTALL_SYSCONFDIR}``    ``etc``
-``SHAREDSTATE``         ``${CMAKE_INSTALL_SHARESTATEDIR}`` ``com``
-``LOCALSTATE``          ``${CMAKE_INSTALL_LOCALSTATEDIR}`` ``var``
-``RUNSTATE``            ``${CMAKE_INSTALL_RUNSTATEDIR}``   ``<LOCALSTATE dir>/run``
-``DATA``                ``${CMAKE_INSTALL_DATADIR}``       ``<DATAROOT dir>``
-``INFO``                ``${CMAKE_INSTALL_INFODIR}``       ``<DATAROOT dir>/info``
-``LOCALE``              ``${CMAKE_INSTALL_LOCALEDIR}``     ``<DATAROOT dir>/locale``
-``MAN``                 ``${CMAKE_INSTALL_MANDIR}``        ``<DATAROOT dir>/man``
-``DOC``                 ``${CMAKE_INSTALL_DOCDIR}``        ``<DATAROOT dir>/doc``
-======================= ================================== =========================
-
-Note that some of the types' built-in defaults use the ``DATAROOT`` directory as
-a prefix. The ``DATAROOT`` prefix is calculated similarly to the types, with
-``CMAKE_INSTALL_DATAROOTDIR`` as the variable and ``share`` as the built-in
-default. You cannot use ``DATAROOT`` as a ``TYPE`` parameter; please use
-``DATA`` instead.
-
-To make packages compliant with distribution filesystem layout policies, if
-projects must specify a ``DESTINATION``, it is recommended that they use a
-path that begins with the appropriate :module:`GNUInstallDirs` variable.
-This allows package maintainers to control the install destination by setting
-the appropriate cache variables.
-
-.. versionadded:: 3.4
-  An install destination given as a ``DESTINATION`` argument may
-  use "generator expressions" with the syntax ``$<...>``.  See the
-  :manual:`cmake-generator-expressions(7)` manual for available expressions.
-
-.. versionadded:: 3.5
-  The list of ``dirs...`` given to ``DIRECTORY`` may use
-  "generator expressions" too.
-
-Custom Installation Logic
-^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. _`install(CODE)`:
-.. _`install(SCRIPT)`:
-.. _CODE:
-.. _SCRIPT:
-
-.. code-block:: cmake
-
-  install([[SCRIPT <file>] [CODE <code>]]
-          [ALL_COMPONENTS | COMPONENT <component>]
-          [EXCLUDE_FROM_ALL] [...])
-
-The ``SCRIPT`` form will invoke the given CMake script files during
-installation.  If the script file name is a relative path it will be
-interpreted with respect to the current source directory.  The ``CODE``
-form will invoke the given CMake code during installation.  Code is
-specified as a single argument inside a double-quoted string.  For
-example, the code
-
-.. code-block:: cmake
-
-  install(CODE "MESSAGE(\"Sample install message.\")")
-
-will print a message during installation.
-
-.. versionadded:: 3.21
-  When the ``ALL_COMPONENTS`` option is given, the custom installation
-  script code will be executed for every component of a component-specific
-  installation.  This option is mutually exclusive with the ``COMPONENT``
-  option.
-
-.. versionadded:: 3.14
-  ``<file>`` or ``<code>`` may use "generator expressions" with the syntax
-  ``$<...>`` (in the case of ``<file>``, this refers to their use in the file
-  name, not the file's contents).  See the
-  :manual:`cmake-generator-expressions(7)` manual for available expressions.
-
-Installing Exports
-^^^^^^^^^^^^^^^^^^
-
-.. _`install(EXPORT)`:
-.. _EXPORT:
-
-.. code-block:: cmake
-
-  install(EXPORT <export-name> DESTINATION <dir>
-          [NAMESPACE <namespace>] [FILE <name>.cmake]
-          [PERMISSIONS permissions...]
-          [CONFIGURATIONS [Debug|Release|...]
-          [CXX_MODULES_DIRECTORY <directory>]
-          [EXPORT_LINK_INTERFACE_LIBRARIES]
-          [COMPONENT <component>]
-          [EXCLUDE_FROM_ALL])
-  install(EXPORT_ANDROID_MK <export-name> DESTINATION <dir> [...])
-
-The ``EXPORT`` form generates and installs a CMake file containing code to
-import targets from the installation tree into another project.
-Target installations are associated with the export ``<export-name>``
-using the ``EXPORT`` option of the `install(TARGETS)`_ signature
-documented above.  The ``NAMESPACE`` option will prepend ``<namespace>`` to
-the target names as they are written to the import file.  By default
-the generated file will be called ``<export-name>.cmake`` but the ``FILE``
-option may be used to specify a different name.  The value given to
-the ``FILE`` option must be a file name with the ``.cmake`` extension.
-If a ``CONFIGURATIONS`` option is given then the file will only be installed
-when one of the named configurations is installed.  Additionally, the
-generated import file will reference only the matching target
-configurations.  See the :variable:`CMAKE_MAP_IMPORTED_CONFIG_<CONFIG>`
-variable to map configurations of dependent projects to the installed
-configurations.  The ``EXPORT_LINK_INTERFACE_LIBRARIES`` keyword, if
-present, causes the contents of the properties matching
-``(IMPORTED_)?LINK_INTERFACE_LIBRARIES(_<CONFIG>)?`` to be exported, when
-policy :policy:`CMP0022` is ``NEW``.
-
-.. note::
-  The installed ``<export-name>.cmake`` file may come with additional
-  per-configuration ``<export-name>-*.cmake`` files to be loaded by
-  globbing.  Do not use an export name that is the same as the package
-  name in combination with installing a ``<package-name>-config.cmake``
-  file or the latter may be incorrectly matched by the glob and loaded.
-
-When a ``COMPONENT`` option is given, the listed ``<component>`` implicitly
-depends on all components mentioned in the export set. The exported
-``<name>.cmake`` file will require each of the exported components to be
-present in order for dependent projects to build properly. For example, a
-project may define components ``Runtime`` and ``Development``, with shared
-libraries going into the ``Runtime`` component and static libraries and
-headers going into the ``Development`` component. The export set would also
-typically be part of the ``Development`` component, but it would export
-targets from both the ``Runtime`` and ``Development`` components. Therefore,
-the ``Runtime`` component would need to be installed if the ``Development``
-component was installed, but not vice versa. If the ``Development`` component
-was installed without the ``Runtime`` component, dependent projects that try
-to link against it would have build errors. Package managers, such as APT and
-RPM, typically handle this by listing the ``Runtime`` component as a dependency
-of the ``Development`` component in the package metadata, ensuring that the
-library is always installed if the headers and CMake export file are present.
-
-.. versionadded:: 3.7
-  In addition to cmake language files, the ``EXPORT_ANDROID_MK`` mode may be
-  used to specify an export to the android ndk build system.  This mode
-  accepts the same options as the normal export mode.  The Android
-  NDK supports the use of prebuilt libraries, both static and shared. This
-  allows cmake to build the libraries of a project and make them available
-  to an ndk build system complete with transitive dependencies, include flags
-  and defines required to use the libraries.
-
-``CXX_MODULES_DIRECTORY``
-
-  .. note ::
-
-    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
-
-  Specify a subdirectory to store C++ module information for targets in the
-  export set. This directory will be populated with files which add the
-  necessary target property information to the relevant targets. Note that
-  without this information, none of the C++ modules which are part of the
-  targets in the export set will support being imported in consuming targets.
-
-The ``EXPORT`` form is useful to help outside projects use targets built
-and installed by the current project.  For example, the code
-
-.. code-block:: cmake
-
-  install(TARGETS myexe EXPORT myproj DESTINATION bin)
-  install(EXPORT myproj NAMESPACE mp_ DESTINATION lib/myproj)
-  install(EXPORT_ANDROID_MK myproj DESTINATION share/ndk-modules)
-
-will install the executable ``myexe`` to ``<prefix>/bin`` and code to import
-it in the file ``<prefix>/lib/myproj/myproj.cmake`` and
-``<prefix>/share/ndk-modules/Android.mk``.  An outside project
-may load this file with the include command and reference the ``myexe``
-executable from the installation tree using the imported target name
-``mp_myexe`` as if the target were built in its own tree.
-
-.. note::
-  This command supersedes the :command:`install_targets` command and
-  the :prop_tgt:`PRE_INSTALL_SCRIPT` and :prop_tgt:`POST_INSTALL_SCRIPT`
-  target properties.  It also replaces the ``FILES`` forms of the
-  :command:`install_files` and :command:`install_programs` commands.
-  The processing order of these install rules relative to
-  those generated by :command:`install_targets`,
-  :command:`install_files`, and :command:`install_programs` commands
-  is not defined.
-
-Installing Runtime Dependencies
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-.. _`install(RUNTIME_DEPENDENCY_SET)`:
-.. _RUNTIME_DEPENDENCY_SET:
-
-.. versionadded:: 3.21
-
-.. code-block:: cmake
-
-  install(RUNTIME_DEPENDENCY_SET <set-name>
-          [[LIBRARY|RUNTIME|FRAMEWORK]
-           [DESTINATION <dir>]
-           [PERMISSIONS permissions...]
-           [CONFIGURATIONS [Debug|Release|...]]
-           [COMPONENT <component>]
-           [NAMELINK_COMPONENT <component>]
-           [OPTIONAL] [EXCLUDE_FROM_ALL]
-          ] [...]
-          [PRE_INCLUDE_REGEXES regexes...]
-          [PRE_EXCLUDE_REGEXES regexes...]
-          [POST_INCLUDE_REGEXES regexes...]
-          [POST_EXCLUDE_REGEXES regexes...]
-          [POST_INCLUDE_FILES files...]
-          [POST_EXCLUDE_FILES files...]
-          [DIRECTORIES directories...]
+  install(TARGETS myExe
+          CONFIGURATIONS Debug
+          RUNTIME
+            DESTINATION Debug/bin
+          )
+  install(TARGETS myExe
+          CONFIGURATIONS Release
+          RUNTIME
+            DESTINATION Release/bin
           )
 
-Installs a runtime dependency set previously created by one or more
-`install(TARGETS)`_ or `install(IMPORTED_RUNTIME_ARTIFACTS)`_ commands. The
-dependencies of targets belonging to a runtime dependency set are installed in
-the ``RUNTIME`` destination and component on DLL platforms, and in the
-``LIBRARY`` destination and component on non-DLL platforms. macOS frameworks
-are installed in the ``FRAMEWORK`` destination and component.
-Targets built within the build tree will never be installed as runtime
-dependencies, nor will their own dependencies, unless the targets themselves
-are installed with `install(TARGETS)`_.
-
-The generated install script calls :command:`file(GET_RUNTIME_DEPENDENCIES)`
-on the build-tree files to calculate the runtime dependencies. The build-tree
-executable files are passed as the ``EXECUTABLES`` argument, the build-tree
-shared libraries as the ``LIBRARIES`` argument, and the build-tree modules as
-the ``MODULES`` argument. On macOS, if one of the executables is a
-:prop_tgt:`MACOSX_BUNDLE`, that executable is passed as the
-``BUNDLE_EXECUTABLE`` argument. At most one such bundle executable may be in
-the runtime dependency set on macOS. The :prop_tgt:`MACOSX_BUNDLE` property
-has no effect on other platforms. Note that
-:command:`file(GET_RUNTIME_DEPENDENCIES)` only supports collecting the runtime
-dependencies for Windows, Linux and macOS platforms, so
-``install(RUNTIME_DEPENDENCY_SET)`` has the same limitation.
-
-The following sub-arguments are forwarded through as the corresponding
-arguments to :command:`file(GET_RUNTIME_DEPENDENCIES)` (for those that provide
-a non-empty list of directories, regular expressions or files).  They all
-support :manual:`generator expressions <cmake-generator-expressions(7)>`.
-
-* ``DIRECTORIES <directories>``
-* ``PRE_INCLUDE_REGEXES <regexes>``
-* ``PRE_EXCLUDE_REGEXES <regexes>``
-* ``POST_INCLUDE_REGEXES <regexes>``
-* ``POST_EXCLUDE_REGEXES <regexes>``
-* ``POST_INCLUDE_FILES <files>``
-* ``POST_EXCLUDE_FILES <files>``
+will install ``myExe`` to ``<prefix>/Debug/bin`` in the Debug configuration,
+and to ``<prefix>/Release/bin`` in the Release configuration.
 
 Generated Installation Script
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Help/command/set.rst b/Help/command/set.rst
index aeb88b3..fa635c6 100644
--- a/Help/command/set.rst
+++ b/Help/command/set.rst
@@ -27,11 +27,12 @@
   If the ``PARENT_SCOPE`` option is given the variable will be set in
   the scope above the current scope.  Each new directory or :command:`function`
   command creates a new scope.  A scope can also be created with the
-  :command:`block` command. This command will set the value of a variable into
-  the parent directory, calling function or encompassing scope (whichever is
-  applicable to the case at hand). The previous state of the variable's value
-  stays the same in the current scope (e.g., if it was undefined before, it is
-  still undefined and if it had a value, it is still that value).
+  :command:`block` command. ``set(PARENT_SCOPE)`` will set the value
+  of a variable into the parent directory, calling function, or
+  encompassing scope (whichever is applicable to the case at hand).
+  The previous state of the variable's value stays the same in the
+  current scope (e.g., if it was undefined before, it is still undefined
+  and if it had a value, it is still that value).
 
   The :command:`block(PROPAGATE)` and :command:`return(PROPAGATE)` commands
   can be used as an alternate method to the :command:`set(PARENT_SCOPE)`
diff --git a/Help/command/set_property.rst b/Help/command/set_property.rst
index fc43974..f14b63d 100644
--- a/Help/command/set_property.rst
+++ b/Help/command/set_property.rst
@@ -12,7 +12,8 @@
                           [DIRECTORY <dirs> ...]
                           [TARGET_DIRECTORY <targets> ...] |
                 INSTALL   [<file1> ...]     |
-                TEST      [<test1> ...]     |
+                TEST      [<test1> ...]
+                          [DIRECTORY <dir>] |
                 CACHE     [<entry1> ...]    >
                [APPEND] [APPEND_STRING]
                PROPERTY <name> [<value1> ...])
@@ -91,6 +92,17 @@
   :manual:`generator expressions <cmake-generator-expressions(7)>`
   for tests created by the :command:`add_test(NAME)` signature.
 
+  .. versionadded:: 3.28
+
+    Visibility can be set in other directory scopes using the following sub-option:
+
+    ``DIRECTORY <dir>``
+      The test property will be set in the ``<dir>`` directory's scope. CMake must
+      already know about this directory, either by having added it through a call
+      to :command:`add_subdirectory` or it being the top level source directory.
+      Relative paths are treated as relative to the current source directory.
+      ``<dir>`` may reference a binary directory.
+
 ``CACHE``
   Scope must name zero or more existing cache entries.
 
diff --git a/Help/command/set_tests_properties.rst b/Help/command/set_tests_properties.rst
index 125e460..da750e3 100644
--- a/Help/command/set_tests_properties.rst
+++ b/Help/command/set_tests_properties.rst
@@ -14,10 +14,20 @@
 :manual:`generator expressions <cmake-generator-expressions(7)>`
 for tests created by the :command:`add_test(NAME)` signature.
 
+.. versionadded:: 3.28
+  Visibility can be set in other directory scopes using the following option:
+
+  ``DIRECTORY <dir>``
+    The test properties will be set in the ``<dir>`` directory's scope.
+    CMake must already know about this directory, either by having added it
+    through a call to :command:`add_subdirectory` or it being the top level
+    source directory. Relative paths are treated as relative to the current
+    source directory. ``<dir>`` may reference a binary directory.
+
 See Also
 ^^^^^^^^
 
 * :command:`add_test`
 * :command:`define_property`
 * the more general :command:`set_property` command
-* :ref:`Target Properties` for the list of properties known to CMake
+* :ref:`Test Properties` for the list of properties known to CMake
diff --git a/Help/command/target_link_libraries.rst b/Help/command/target_link_libraries.rst
index 1d27660..cc6bc0f 100644
--- a/Help/command/target_link_libraries.rst
+++ b/Help/command/target_link_libraries.rst
@@ -66,6 +66,12 @@
   :ref:`usage requirement <Target Usage Requirements>`.  This has the same
   effect as passing the framework directory as an include directory.
 
+  .. versionadded:: 3.28
+
+    The library file may point to a ``.xcframework`` folder on macOS. If it
+    does, the target will get the selected library's ``Headers`` directory as
+    a usage requirement.
+
   .. versionadded:: 3.8
     On :ref:`Visual Studio Generators` for VS 2010 and above, library files
     ending in ``.targets`` will be treated as MSBuild targets files and
diff --git a/Help/command/target_precompile_headers.rst b/Help/command/target_precompile_headers.rst
index c795713..db55bc2 100644
--- a/Help/command/target_precompile_headers.rst
+++ b/Help/command/target_precompile_headers.rst
@@ -33,7 +33,7 @@
 Repeated calls for the same ``<target>`` will append items in the order called.
 
 Projects should generally avoid using ``PUBLIC`` or ``INTERFACE`` for targets
-that will be :ref:`exported <install(EXPORT)>`, or they should at least use
+that will be :command:`exported <install(EXPORT)>`, or they should at least use
 the :genex:`$<BUILD_INTERFACE:...>` generator expression to prevent precompile
 headers from appearing in an installed exported target.  Consumers of a target
 should typically be in control of what precompile headers they use, not have
diff --git a/Help/command/target_sources.rst b/Help/command/target_sources.rst
index 4a8eda2..40755c5 100644
--- a/Help/command/target_sources.rst
+++ b/Help/command/target_sources.rst
@@ -60,6 +60,8 @@
 See the :manual:`cmake-buildsystem(7)` manual for more on defining
 buildsystem properties.
 
+.. _`File Sets`:
+
 File Sets
 ^^^^^^^^^
 
@@ -82,10 +84,7 @@
   Sources intended to be used via a language's ``#include`` mechanism.
 
 ``CXX_MODULES``
-
-  .. note ::
-
-    Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+  .. versionadded:: 3.28
 
   Sources which contain C++ interface module or partition units (i.e., those
   using the ``export`` keyword). This file set type may not have an
diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst
index 8abb6e0..0255b4d9 100644
--- a/Help/command/try_compile.rst
+++ b/Help/command/try_compile.rst
@@ -65,6 +65,7 @@
 .. code-block:: cmake
 
   try_compile(<compileResultVar>
+              [SOURCES_TYPE <type>]
               <SOURCES <srcfile...>                 |
                SOURCE_FROM_CONTENT <name> <content> |
                SOURCE_FROM_VAR <name> <var>         |
@@ -143,7 +144,12 @@
 call at a time.  Use of the newer signature is recommended to simplify
 debugging of multiple ``try_compile`` operations.
 
-The options are:
+.. _`try_compile Options`:
+
+Options
+^^^^^^^
+
+The options for the above signatures are:
 
 ``CMAKE_FLAGS <flags>...``
   Specify flags of the form :option:`-DVAR:TYPE=VALUE <cmake -D>` to be passed
@@ -244,6 +250,24 @@
 
   ``SOURCE_FROM_VAR`` may be specified multiple times.
 
+``SOURCES_TYPE <type>``
+  .. versionadded:: 3.28
+
+  Sources may be classified using the ``SOURCES_TYPE`` argument. Once
+  specified, all subsequent sources specified will be treated as that type
+  until another ``SOURCES_TYPE`` is given. Available types are:
+
+  ``NORMAL``
+    Sources are not added to any ``FILE_SET`` in the generated project.
+
+  ``CXX_MODULE``
+    .. versionadded:: 3.28
+
+    Sources are added to a ``FILE_SET`` of type ``CXX_MODULES`` in the
+    generated project.
+
+  The default type of sources is ``NORMAL``.
+
 ``<LANG>_STANDARD <std>``
   .. versionadded:: 3.8
 
diff --git a/Help/command/try_run.rst b/Help/command/try_run.rst
index 3a4e203..1b5087d 100644
--- a/Help/command/try_run.rst
+++ b/Help/command/try_run.rst
@@ -13,6 +13,7 @@
 .. code-block:: cmake
 
   try_run(<runResultVar> <compileResultVar>
+          [SOURCES_TYPE <type>]
           <SOURCES <srcfile...>                 |
            SOURCE_FROM_CONTENT <name> <content> |
            SOURCE_FROM_VAR <name> <var>         |
@@ -77,6 +78,11 @@
           [ARGS <args>...]
           )
 
+.. _`try_run Options`:
+
+Options
+^^^^^^^
+
 The options specific to ``try_run`` are:
 
 ``COMPILE_OUTPUT_VARIABLE <var>``
diff --git a/Help/dev/documentation.rst b/Help/dev/documentation.rst
index c6fb7a6..65c0ccf 100644
--- a/Help/dev/documentation.rst
+++ b/Help/dev/documentation.rst
@@ -431,6 +431,7 @@
 * ``-``: Subsection or `CMake Domain`_ object document title
 * ``^``: Subsubsection or `CMake Domain`_ object document section
 * ``"``: Paragraph or `CMake Domain`_ object document subsection
+* ``~``: `CMake Domain`_ object document subsubsection
 
 Style: Whitespace
 ^^^^^^^^^^^^^^^^^
diff --git a/Help/dev/experimental.rst b/Help/dev/experimental.rst
index eac86ab..87ac031 100644
--- a/Help/dev/experimental.rst
+++ b/Help/dev/experimental.rst
@@ -13,127 +13,3 @@
 When used, a warning will be generated to indicate that an experimental
 feature is in use and that the affected behavior in the project is not part of
 CMake's stability guarantees.
-
-C++20 Module APIs
-=================
-
-Variable: ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
-Value: ``aa1f7df0-828a-4fcd-9afc-2dc80491aca7``
-
-In order to support C++20 modules, there are a number of behaviors that have
-CMake APIs to provide the required features to build and export them from a
-project.
-
-Limitations
------------
-
-There are a number of known limitations of the current C++20 module support in
-CMake.  This does not document known limitations or bugs in compilers as these
-can change over time.
-
-For all generators:
-
-- Only in-project modules may be used.  While there is some support for
-  exporting module information, there is no mechanism for using it at the
-  moment.
-
-For the Ninja Generators:
-
-- ``ninja`` 1.10 or newer is required.
-
-For the Visual Studio Generators:
-
-- Only Visual Studio 2022 and toolchains newer than 19.34 (Visual Studio
-  17.4).
-- No support for exporting or installing BMI or module information.
-- No diagnosis of using modules provided by ``PRIVATE`` sources from
-  ``PUBLIC`` module sources.
-
-C++20 Module Dependencies
-=========================
-
-The Ninja generator has experimental infrastructure supporting C++20 module
-dependency scanning.  This is similar to the Fortran modules support, but
-relies on external tools to scan C++20 translation units for module
-dependencies.  The approach is described by Kitware's `D1483r1`_ paper.
-
-In order to activate CMake's experimental support for C++20 module
-dependencies, set the following variables:
-
-``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
-  Set this to the UUID documented above.
-
-Some compilers already have support for module dependency scanning:
-
-* MSVC 19.34 and newer (provided with Visual Studio 17.4 and newer)
-* LLVM/Clang 16.0 and newer
-
-For those, only the above variables need to be set by project code.
-For compilers with in-development support, additional variables must
-be set as follows.
-
-``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE``
-  Set this to tell CMake how to invoke the C++20 module dependency
-  scanning tool.
-
-``CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT``
-  Set this for compilers that generate module maps.  See below.
-
-``CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG``
-  Set this for compilers that generate module maps.  See below.
-
-For example, add code like the following to a test project:
-
-.. code-block:: cmake
-
-  string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
-    "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE>"
-    " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
-    " ${flags_to_scan_deps} -fdep-file=<DYNDEP_FILE> -fdep-output=<OBJECT>"
-    )
-
-The tool specified by ``CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE`` is
-expected to process the translation unit, write preprocessor dependencies
-to the file specified by the ``<DEP_FILE>`` placeholder, and write module
-dependencies to the file specified by the ``<DYNDEP_FILE>`` placeholder. The
-``CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT`` file may be set to ``msvc``
-for scandep rules which use ``msvc``-style dependency reporting.
-
-The module dependencies should be written in the format described
-by the `P1689r5`_ paper.
-
-Compiler writers may try out their scanning functionality using
-the `cxx-modules-sandbox`_ test project, modified to set variables
-as above for their compiler.
-
-For compilers that generate module maps, tell CMake as follows:
-
-.. code-block:: cmake
-
-  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "gcc")
-  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
-    "${compiler_flags_for_module_map} -fmodule-mapper=<MODULE_MAP_FILE>")
-
-Currently, the only supported formats are, ``clang``, ``gcc``, and ``msvc``.
-The ``gcc`` format is described in the GCC documentation, but the relevant
-section for the purposes of CMake is:
-
-    A mapping file consisting of space-separated module-name, filename
-    pairs, one per line.  Only the mappings for the direct imports and any
-    module export name need be provided.  If other mappings are provided,
-    they override those stored in any imported CMI files.  A repository
-    root may be specified in the mapping file by using ``$root`` as the
-    module name in the first active line.
-
-    -- GCC module mapper documentation
-
-The ``msvc`` format is a response file containing flags required to compile
-any module interfaces properly as well as find any required files to satisfy
-``import`` statements as required for Microsoft's Visual Studio toolchains.
-
-Similarly, the ``clang`` format is a response file containing flags using
-Clang's module flags.
-
-.. _`D1483r1`: https://mathstuf.fedorapeople.org/fortran-modules/fortran-modules.html
-.. _`P1689r5`: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p1689r5.html
-.. _`cxx-modules-sandbox`: https://github.com/mathstuf/cxx-modules-sandbox
diff --git a/Help/envvar/CMAKE_CROSSCOMPILING_EMULATOR.rst b/Help/envvar/CMAKE_CROSSCOMPILING_EMULATOR.rst
new file mode 100644
index 0000000..3e397d8
--- /dev/null
+++ b/Help/envvar/CMAKE_CROSSCOMPILING_EMULATOR.rst
@@ -0,0 +1,11 @@
+CMAKE_CROSSCOMPILING_EMULATOR
+-----------------------------
+
+.. versionadded:: 3.28
+
+.. include:: ENV_VAR.txt
+
+The default value for :variable:`CMAKE_CROSSCOMPILING_EMULATOR` when there
+is no explicit configuration given on the first run while creating a new
+build tree.  On later runs in an existing build tree the value persists in
+the cache as :variable:`CMAKE_CROSSCOMPILING_EMULATOR`.
diff --git a/Help/envvar/CUDAHOSTCXX.rst b/Help/envvar/CUDAHOSTCXX.rst
index 74f5d48..4e78afa 100644
--- a/Help/envvar/CUDAHOSTCXX.rst
+++ b/Help/envvar/CUDAHOSTCXX.rst
@@ -8,8 +8,10 @@
 Preferred executable for compiling host code when compiling ``CUDA``
 language files. Will only be used by CMake on the first configuration to
 determine ``CUDA`` host compiler, after which the value for ``CUDAHOSTCXX`` is
-stored in the cache as :variable:`CMAKE_CUDA_HOST_COMPILER`. This environment
-variable is preferred over :variable:`CMAKE_CUDA_HOST_COMPILER`.
+stored in the cache as
+:variable:`CMAKE_CUDA_HOST_COMPILER <CMAKE_<LANG>_HOST_COMPILER>`.
+This environment variable is preferred over
+:variable:`CMAKE_CUDA_HOST_COMPILER <CMAKE_<LANG>_HOST_COMPILER>`.
 
 This environment variable is primarily meant for use with projects that
 enable ``CUDA`` as a first-class language.
diff --git a/Help/envvar/HIPHOSTCXX.rst b/Help/envvar/HIPHOSTCXX.rst
new file mode 100644
index 0000000..871fbfb
--- /dev/null
+++ b/Help/envvar/HIPHOSTCXX.rst
@@ -0,0 +1,19 @@
+HIPHOSTCXX
+-----------
+
+.. versionadded:: 3.28
+
+.. include:: ENV_VAR.txt
+
+Preferred executable for compiling host code when compiling ``HIP``
+language files with the NVIDIA CUDA Compiler. Will only be used by CMake
+on the first configuration to determine ``HIP`` host compiler, after which
+the value for ``HIPHOSTCXX`` is stored in the cache as
+:variable:`CMAKE_HIP_HOST_COMPILER <CMAKE_<LANG>_HOST_COMPILER>`.
+
+This environment variable is primarily meant for use with projects that
+enable ``HIP`` as a first-class language.
+
+.. note::
+
+  Ignored when using :ref:`Visual Studio Generators`.
diff --git a/Help/generator/Visual Studio 10 2010.rst b/Help/generator/Visual Studio 10 2010.rst
index 888164f..a36046a 100644
--- a/Help/generator/Visual Studio 10 2010.rst
+++ b/Help/generator/Visual Studio 10 2010.rst
@@ -3,6 +3,6 @@
 
 Removed.  This once generated Visual Studio 10 2010 project files, but
 the generator has been removed since CMake 3.25.  It is still possible
-to build with VS 10 2010 tools using the :generator:`Visual Studio 12 2013`
+to build with VS 10 2010 tools using the :generator:`Visual Studio 14 2015`
 (or above) generator with :variable:`CMAKE_GENERATOR_TOOLSET` set to
 ``v100``, or by using the :generator:`NMake Makefiles` generator.
diff --git a/Help/generator/Visual Studio 11 2012.rst b/Help/generator/Visual Studio 11 2012.rst
index 4e7195c..5ded24c 100644
--- a/Help/generator/Visual Studio 11 2012.rst
+++ b/Help/generator/Visual Studio 11 2012.rst
@@ -1,57 +1,8 @@
 Visual Studio 11 2012
 ---------------------
 
-Deprecated.  Generates Visual Studio 11 (VS 2012) project files.
-
-.. note::
-  This generator is deprecated and will be removed in a future version
-  of CMake.  It will still be possible to build with VS 11 2012 tools
-  using the :generator:`Visual Studio 12 2013` (or above) generator
-  with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v110``, or by
-  using the :generator:`NMake Makefiles` generator.
-
-For compatibility with CMake versions prior to 3.0, one may specify this
-generator using the name "Visual Studio 11" without the year component.
-
-Project Types
-^^^^^^^^^^^^^
-
-Only Visual C++ and C# projects may be generated (and Fortran with
-Intel compiler integration).  Other types of projects (JavaScript,
-Database, Website, etc.) are not supported.
-
-Platform Selection
-^^^^^^^^^^^^^^^^^^
-
-The default target platform name (architecture) is ``Win32``.
-
-.. versionadded:: 3.1
-  The :variable:`CMAKE_GENERATOR_PLATFORM` variable may be set, perhaps
-  via the :option:`cmake -A` option, to specify a target platform
-  name (architecture).  For example:
-
-  * ``cmake -G "Visual Studio 11 2012" -A Win32``
-  * ``cmake -G "Visual Studio 11 2012" -A x64``
-  * ``cmake -G "Visual Studio 11 2012" -A ARM``
-  * ``cmake -G "Visual Studio 11 2012" -A <WinCE-SDK>``
-    (Specify a target platform matching a Windows CE SDK name.)
-
-For compatibility with CMake versions prior to 3.1, one may specify
-a target platform name optionally at the end of the generator name.
-This is supported only for:
-
-``Visual Studio 11 2012 Win64``
-  Specify target platform ``x64``.
-
-``Visual Studio 11 2012 ARM``
-  Specify target platform ``ARM``.
-
-``Visual Studio 11 2012 <WinCE-SDK>``
-  Specify target platform matching a Windows CE SDK name.
-
-Toolset Selection
-^^^^^^^^^^^^^^^^^
-
-The ``v110`` toolset that comes with Visual Studio 11 2012 is selected by
-default.  The :variable:`CMAKE_GENERATOR_TOOLSET` option may be set, perhaps
-via the :option:`cmake -T` option, to specify another toolset.
+Removed.  This once generated Visual Studio 11 2012 project files, but
+the generator has been removed since CMake 3.28.  It is still possible
+to build with VS 11 2012 tools using the :generator:`Visual Studio 14 2015`
+(or above) generator with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v110``,
+or by using the :generator:`NMake Makefiles` generator.
diff --git a/Help/generator/Visual Studio 12 2013.rst b/Help/generator/Visual Studio 12 2013.rst
index 3dbcfe6..522522c 100644
--- a/Help/generator/Visual Studio 12 2013.rst
+++ b/Help/generator/Visual Studio 12 2013.rst
@@ -1,7 +1,14 @@
 Visual Studio 12 2013
 ---------------------
 
-Generates Visual Studio 12 (VS 2013) project files.
+Deprecated.  Generates Visual Studio 12 (VS 2013) project files.
+
+.. note::
+  This generator is deprecated and will be removed in a future version
+  of CMake.  It will still be possible to build with VS 12 2013 tools
+  using the :generator:`Visual Studio 14 2015` (or above) generator
+  with :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v120``, or by
+  using the :generator:`NMake Makefiles` generator.
 
 For compatibility with CMake versions prior to 3.0, one may specify this
 generator using the name "Visual Studio 12" without the year component.
diff --git a/Help/generator/Visual Studio 9 2008.rst b/Help/generator/Visual Studio 9 2008.rst
index 816969d..1439270 100644
--- a/Help/generator/Visual Studio 9 2008.rst
+++ b/Help/generator/Visual Studio 9 2008.rst
@@ -6,7 +6,7 @@
 .. note::
   This generator is deprecated and will be removed in a future version
   of CMake.  It will still be possible to build with VS 9 2008 tools
-  using the :generator:`Visual Studio 12 2013` generator (or above,
+  using the :generator:`Visual Studio 14 2015` generator (or above,
   and with VS 10 2010 also installed) with
   :variable:`CMAKE_GENERATOR_TOOLSET` set to ``v90``,
   or by using the :generator:`NMake Makefiles` generator.
diff --git a/Help/guide/tutorial/A Basic Starting Point.rst b/Help/guide/tutorial/A Basic Starting Point.rst
index 37b0668..2325e9e 100644
--- a/Help/guide/tutorial/A Basic Starting Point.rst
+++ b/Help/guide/tutorial/A Basic Starting Point.rst
@@ -102,7 +102,14 @@
 
   cmake --build .
 
-Finally, try to use the newly built ``Tutorial`` with these commands:
+For multi-config generators (e.g. Visual Studio), first navigate to the
+appropriate subdirectory, for example:
+
+.. code-block:: console
+
+  cd Debug
+
+Finally, try to use the newly built ``Tutorial``:
 
 .. code-block:: console
 
@@ -110,6 +117,11 @@
   Tutorial 10
   Tutorial
 
+
+**Note:** Depending on the shell, the correct syntax may be ``Tutorial``,
+``./Tutorial`` or ``.\Tutorial``. For simplicity, the exercises will use
+``Tutorial`` throughout.
+
 Solution
 --------
 
diff --git a/Help/guide/tutorial/Adding Generator Expressions.rst b/Help/guide/tutorial/Adding Generator Expressions.rst
index 3dab97f..910eacb 100644
--- a/Help/guide/tutorial/Adding Generator Expressions.rst
+++ b/Help/guide/tutorial/Adding Generator Expressions.rst
@@ -149,8 +149,8 @@
 
 Lastly, we only want these warning flags to be used during builds. Consumers
 of our installed project should not inherit our warning flags. To specify
-this, we wrap our flags in a generator expression using the ``BUILD_INTERFACE``
-condition. The resulting full code looks like the following:
+this, we wrap our flags from TODO 3 in a generator expression using the
+``BUILD_INTERFACE`` condition. The resulting full code looks like the following:
 
 .. raw:: html
 
diff --git a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst
index 2273063..e7aff9c 100644
--- a/Help/guide/tutorial/Adding Usage Requirements for a Library.rst
+++ b/Help/guide/tutorial/Adding Usage Requirements for a Library.rst
@@ -127,7 +127,7 @@
 
   </details>
 
-And the lines:
+And remove ``EXTRA_INCLUDES`` from ``target_include_directories``:
 
 .. raw:: html
 
@@ -143,23 +143,6 @@
 
   </details>
 
-The remaining code looks like:
-
-.. raw:: html
-
-  <details><summary>Click to show/hide the resulting code</summary>
-
-.. literalinclude:: Step4/CMakeLists.txt
-  :caption: Remaining code after removing EXTRA_INCLUDES
-  :name: CMakeLists.txt-after-removing-EXTRA_INCLUDES
-  :language: cmake
-  :start-after: add_subdirectory(MathFunctions)
-
-.. raw:: html
-
-  </details>
-
-
 Notice that with this technique, the only thing our executable target does to
 use our library is call :command:`target_link_libraries` with the name
 of the library target. In larger projects, the classic method of specifying
@@ -262,10 +245,9 @@
   </details>
 
 Finally, with our interface library set up, we need to link our
-executable ``Target``, our ``MathFunctions`` library, and our ``SqrtLibrary``
-library to our new
-``tutorial_compiler_flags`` library. Respectively, the code will look like
-this:
+executable ``Tutorial``, our ``SqrtLibrary`` library and our ``MathFunctions``
+library to our new ``tutorial_compiler_flags`` library. Respectively, the code
+will look like this:
 
 .. raw:: html
 
@@ -292,7 +274,7 @@
   :caption: TODO 6: MathFunctions/CMakeLists.txt
   :name: MathFunctions-CMakeLists.txt-target_link_libraries-step4
   :language: cmake
-  :start-after: # link our compiler flags interface library
+  :start-after: # link SqrtLibrary to tutorial_compiler_flags
   :end-before: target_link_libraries(MathFunctions
 
 .. raw:: html
@@ -309,8 +291,7 @@
   :caption: TODO 7: MathFunctions/CMakeLists.txt
   :name: MathFunctions-SqrtLibrary-target_link_libraries-step4
   :language: cmake
-  :start-after: target_link_libraries(SqrtLibrary
-  :end-before: endif()
+  :start-after: # link MathFunctions to tutorial_compiler_flags
 
 .. raw:: html
 
diff --git a/Help/guide/tutorial/Adding a Library.rst b/Help/guide/tutorial/Adding a Library.rst
index 694dfaf..18ced97 100644
--- a/Help/guide/tutorial/Adding a Library.rst
+++ b/Help/guide/tutorial/Adding a Library.rst
@@ -96,7 +96,7 @@
 source files for the library are passed as an argument to
 :command:`add_library`. This looks like the following line:
 
-.. raw:: html/
+.. raw:: html
 
   <details><summary>TODO 1: Click to show/hide answer</summary>
 
@@ -184,7 +184,7 @@
 
   </details>
 
-Lastly, replace ``sqrt`` with our library function ``mathfunctions::mysqrt``.
+Lastly, replace ``sqrt`` with the wrapper function ``mathfunctions::sqrt``.
 
 .. raw:: html
 
diff --git a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
index b221506..1654564 100644
--- a/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Complete/MathFunctions/CMakeLists.txt
@@ -33,10 +33,13 @@
                         POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                         )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # define the symbol stating we are using the declspec(dllexport) when
diff --git a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst
index 504e42f..a2f5e2a 100644
--- a/Help/guide/tutorial/Selecting Static or Shared Libraries.rst
+++ b/Help/guide/tutorial/Selecting Static or Shared Libraries.rst
@@ -44,7 +44,18 @@
   :caption: MathFunctions/CMakeLists.txt
   :name: MathFunctions/CMakeLists.txt-POSITION_INDEPENDENT_CODE
   :language: cmake
-  :lines: 37-42
+  :start-at: # state that SqrtLibrary need PIC when the default is shared libraries
+  :end-at:  )
+
+Define ``EXPORTING_MYMATH`` stating we are using ``declspec(dllexport)`` when
+building on Windows.
+
+.. literalinclude:: Step11/MathFunctions/CMakeLists.txt
+  :caption: MathFunctions/CMakeLists.txt
+  :name: MathFunctions/CMakeLists.txt-dll-export
+  :language: cmake
+  :start-at: # define the symbol stating we are using the declspec(dllexport) when
+  :end-at: target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
 
 **Exercise**: We modified ``MathFunctions.h`` to use dll export defines.
 Using CMake documentation can you find a helper module to simplify this?
diff --git a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
index 36b3fe1..210563a 100644
--- a/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step10/MathFunctions/CMakeLists.txt
@@ -26,10 +26,13 @@
                              ${CMAKE_CURRENT_BINARY_DIR}
                              )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # install libs
diff --git a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
index 813bf90..eacc538 100644
--- a/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step11/MathFunctions/CMakeLists.txt
@@ -31,10 +31,13 @@
                         POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                         )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # define the symbol stating we are using the declspec(dllexport) when
diff --git a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
index 38694dd..8aa5904 100644
--- a/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step12/MathFunctions/CMakeLists.txt
@@ -33,10 +33,13 @@
                         POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                         )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # define the symbol stating we are using the declspec(dllexport) when
diff --git a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
index 0ffb9e1..ffb2f35 100644
--- a/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step3/MathFunctions/CMakeLists.txt
@@ -16,7 +16,7 @@
 
   # TODO 7: Link SqrtLibrary to tutorial_compiler_flags
 
-  target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
 # TODO 6: Link MathFunctions to tutorial_compiler_flags
diff --git a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
index 48561eb..6931898 100644
--- a/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step4/MathFunctions/CMakeLists.txt
@@ -17,10 +17,11 @@
               mysqrt.cxx
               )
 
-  # link our compiler flags interface library
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
-  target_link_libraries(MathFunctions PUBLIC SqrtLibrary)
+
+  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
-# link our compiler flags interface library
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
diff --git a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
index 0c688f2..61b3899 100644
--- a/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step5/MathFunctions/CMakeLists.txt
@@ -16,12 +16,13 @@
               mysqrt.cxx
               )
 
-  # link our compiler flags interface library
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
-# link our compiler flags interface library
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # TODO 1: Create a variable called installable_libs that is a list of all
diff --git a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
index b1b925e..8499a51 100644
--- a/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step6/MathFunctions/CMakeLists.txt
@@ -16,11 +16,13 @@
               mysqrt.cxx
               )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
-# link our compiler flags interface library
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # install libs
diff --git a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
index 897ec0e..a0b3037 100644
--- a/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step7/MathFunctions/CMakeLists.txt
@@ -16,6 +16,7 @@
               mysqrt.cxx
               )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
 
   # TODO 1: Include CheckCXXSourceCompiles
@@ -41,7 +42,7 @@
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
-# link our compiler flags interface library
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # install libs
diff --git a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
index 872a24a..b14d180 100644
--- a/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step8/MathFunctions/CMakeLists.txt
@@ -10,6 +10,7 @@
               mysqrt.cxx
               )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
 
   # does this system provide the log and exp functions?
@@ -45,7 +46,7 @@
                            INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                            )
 
-# link our compiler flags interface library
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # install libs
diff --git a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
index 54cecf8..5addc6d 100644
--- a/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
+++ b/Help/guide/tutorial/Step9/MathFunctions/CMakeLists.txt
@@ -25,10 +25,13 @@
                              ${CMAKE_CURRENT_BINARY_DIR}
                              )
 
+  # link SqrtLibrary to tutorial_compiler_flags
   target_link_libraries(SqrtLibrary PUBLIC tutorial_compiler_flags)
+
   target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
 endif()
 
+# link MathFunctions to tutorial_compiler_flags
 target_link_libraries(MathFunctions PUBLIC tutorial_compiler_flags)
 
 # install libs
diff --git a/Help/index.rst b/Help/index.rst
index 16c8f25..ca03213 100644
--- a/Help/index.rst
+++ b/Help/index.rst
@@ -58,6 +58,7 @@
    /manual/cmake-commands.7
    /manual/cmake-compile-features.7
    /manual/cmake-configure-log.7
+   /manual/cmake-cxxmodules.7
    /manual/cmake-developer.7
    /manual/cmake-env-variables.7
    /manual/cmake-file-api.7
diff --git a/Help/manual/OPTIONS_BUILD.txt b/Help/manual/OPTIONS_BUILD.txt
index 94adac8..fb1e7d4 100644
--- a/Help/manual/OPTIONS_BUILD.txt
+++ b/Help/manual/OPTIONS_BUILD.txt
@@ -105,11 +105,15 @@
 
 .. option:: --toolchain <path-to-file>
 
+ .. versionadded:: 3.21
+
  Specify the cross compiling toolchain file, equivalent to setting
  :variable:`CMAKE_TOOLCHAIN_FILE` variable.
 
 .. option:: --install-prefix <directory>
 
+ .. versionadded:: 3.21
+
  Specify the installation directory, used by the
  :variable:`CMAKE_INSTALL_PREFIX` variable. Must be an absolute path.
 
diff --git a/Help/manual/OPTIONS_HELP.txt b/Help/manual/OPTIONS_HELP.txt
index 78ee245..f4f2dbe 100644
--- a/Help/manual/OPTIONS_HELP.txt
+++ b/Help/manual/OPTIONS_HELP.txt
@@ -11,6 +11,21 @@
 
  Usage describes the basic command line interface and its options.
 
+.. option:: --help <keyword> [<file>]
+
+ Print help for one CMake keyword.
+
+ ``<keyword>`` can be a property, variable, command, policy, generator
+ or module.
+
+ The relevant manual entry for ``<keyword>`` is
+ printed in a human-readable text format.
+ |file|
+
+ .. versionchanged:: 3.28
+
+   Prior to CMake 3.28, this option supported command names only.
+
 .. option:: --help-full [<file>]
 
  Print all help manuals and exit.
diff --git a/Help/manual/cmake-cxxmodules.7.rst b/Help/manual/cmake-cxxmodules.7.rst
new file mode 100644
index 0000000..5776421
--- /dev/null
+++ b/Help/manual/cmake-cxxmodules.7.rst
@@ -0,0 +1,72 @@
+.. cmake-manual-description: CMake C++ Modules Support Reference
+
+cmake-cxxmodules(7)
+*******************
+
+.. versionadded:: 3.28
+
+C++ 20 introduced the concept of "modules" to the language.  The design
+requires build systems to order compilations among each other to satisfy
+``import`` statements reliably.  CMake's implementation asks the compiler
+to scan source files for module dependencies during the build, collates
+scanning results to infer ordering constraints, and tells the build tool
+how to dynamically update the build graph.
+
+Scanning Control
+================
+
+Whether or not sources get scanned for C++ module usage is dependent on the
+following queries. The first query that provides a yes/no answer is used.
+
+- If the source file belongs to a file set of type ``CXX_MODULES``, it will
+  be scanned.
+- If the target does not use at least C++ 20, it will not be scanned.
+- If the source file is not the language ``CXX``, it will not be scanned.
+- If the :prop_sf:`CXX_SCAN_FOR_MODULES` source file property is set, its
+  value will be used.
+- If the :prop_tgt:`CXX_SCAN_FOR_MODULES` target property is set, its value
+  will be used.  Set the :variable:`CMAKE_CXX_SCAN_FOR_MODULES` variable
+  to initialize this property on all targets as they are created.
+- Otherwise, the source file will be scanned.  See policy :policy:`CMP0155`.
+
+Compiler Support
+================
+
+Compilers which CMake natively supports module dependency scanning include:
+
+* MSVC toolset 14.34 and newer (provided with Visual Studio 17.4 and newer)
+* LLVM/Clang 16.0 and newer
+* GCC 14 (for the in-development branch, after 2023-09-20) and newer
+
+Generator Support
+=================
+
+The list of generators which support scanning sources for C++ modules include:
+
+- :generator:`Ninja`
+- :generator:`Ninja Multi-Config`
+- :generator:`Visual Studio 17 2022`
+
+Limitations
+-----------
+
+There are a number of known limitations of the current C++ module support in
+CMake.  This does not document known limitations or bugs in compilers as these
+can change over time.
+
+For all generators:
+
+- Header units are not supported.
+- No builtin support for ``import std;`` or other compiler-provided modules.
+
+For the Ninja Generators:
+
+- ``ninja`` 1.11 or newer is required.
+
+For the :ref:`Visual Studio Generators`:
+
+- Only Visual Studio 2022 and MSVC toolsets 14.34 (Visual Studio
+  17.4) and newer.
+- No support for exporting or installing BMI or module information.
+- No diagnosis of using modules provided by ``PRIVATE`` sources from
+  ``PUBLIC`` module sources.
diff --git a/Help/manual/cmake-env-variables.7.rst b/Help/manual/cmake-env-variables.7.rst
index 197e56e..55f07b7 100644
--- a/Help/manual/cmake-env-variables.7.rst
+++ b/Help/manual/cmake-env-variables.7.rst
@@ -43,6 +43,7 @@
    /envvar/CMAKE_COLOR_DIAGNOSTICS
    /envvar/CMAKE_CONFIGURATION_TYPES
    /envvar/CMAKE_CONFIG_TYPE
+   /envvar/CMAKE_CROSSCOMPILING_EMULATOR
    /envvar/CMAKE_EXPORT_COMPILE_COMMANDS
    /envvar/CMAKE_GENERATOR
    /envvar/CMAKE_GENERATOR_INSTANCE
@@ -83,6 +84,7 @@
    /envvar/FFLAGS
    /envvar/HIPCXX
    /envvar/HIPFLAGS
+   /envvar/HIPHOSTCXX
    /envvar/ISPC
    /envvar/ISPCFLAGS
    /envvar/OBJC
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index 86ae0a4..d4a43de 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -195,6 +195,12 @@
   if ``condition`` is ``0``.  Any other value for ``condition`` results in an
   error.
 
+  .. versionadded:: 3.28
+
+    This generator expression short-circuits such that generator expressions in
+    ``false_string`` will not evaluate when ``condition`` is ``1``, and generator
+    expressions in ``true_string`` will not evaluate when condition is ``0``.
+
 Typically, the ``condition`` is itself a generator expression.  For instance,
 the following expression expands to ``DEBUG_MODE`` when the ``Debug``
 configuration is used, and the empty string for all other configurations:
@@ -252,6 +258,11 @@
   ``condition`` must be ``0`` or ``1``.  The result of the expression is
   ``0`` if ``condition`` is ``1``, else ``1``.
 
+.. versionadded:: 3.28
+
+  Logical operators short-circuit such that generator expressions in the
+  arguments list will not be evaluated once a return value can be determined.
+
 .. _`Comparison Expressions`:
 
 Primary Comparison Expressions
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index 7c48806..047dfd1 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -51,6 +51,17 @@
 to determine whether to report an error on use of deprecated macros or
 functions.
 
+Policies Introduced by CMake 3.28
+=================================
+
+.. toctree::
+   :maxdepth: 1
+
+   CMP0155: C++ sources in targets with at least C++20 are scanned for imports. </policy/CMP0155>
+   CMP0154: Generated files are private by default in targets using file sets. </policy/CMP0154>
+   CMP0153: The exec_program command should not be called. </policy/CMP0153>
+   CMP0152: file(REAL_PATH) resolves symlinks before collapsing ../ components.  </policy/CMP0152>
+
 Policies Introduced by CMake 3.27
 =================================
 
diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index e2366da..46b5fc7 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/Help/manual/cmake-presets.7.rst
@@ -41,6 +41,15 @@
 
 The root object recognizes the following fields:
 
+``$schema``
+  An optional string that provides a URI to the JSON schema that describes the
+  structure of this JSON document. This field is used for validation and
+  autocompletion in editors that support JSON schema. It doesn't affect the
+  behavior of the document itself. If this field is not specified, the JSON
+  document will still be valid, but tools that use JSON schema for validation
+  and autocompletion may not function correctly.
+  This is allowed in preset files specifying version ``8`` or above.
+
 ``version``
   A required integer representing the version of the JSON schema.
   The supported versions are:
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 4df0547..fa1d297 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -240,6 +240,11 @@
    /prop_tgt/IMPORTED
    /prop_tgt/IMPORTED_COMMON_LANGUAGE_RUNTIME
    /prop_tgt/IMPORTED_CONFIGURATIONS
+   /prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS
+   /prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES
+   /prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS
+   /prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES
+   /prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES
    /prop_tgt/IMPORTED_GLOBAL
    /prop_tgt/IMPORTED_IMPLIB
    /prop_tgt/IMPORTED_IMPLIB_CONFIG
@@ -503,6 +508,7 @@
    /prop_test/FIXTURES_CLEANUP
    /prop_test/FIXTURES_REQUIRED
    /prop_test/FIXTURES_SETUP
+   /prop_test/GENERATED_RESOURCE_SPEC_FILE
    /prop_test/LABELS
    /prop_test/MEASUREMENT
    /prop_test/PASS_REGULAR_EXPRESSION
diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst
index d0d6ea0..e5a39f5 100644
--- a/Help/manual/cmake-qt.7.rst
+++ b/Help/manual/cmake-qt.7.rst
@@ -14,7 +14,7 @@
 by the :module:`FindQt4` find-module shipped with CMake, whereas the
 Qt 5 libraries are found using "Config-file Packages" shipped with Qt 5. See
 :manual:`cmake-packages(7)` for more information about CMake packages, and
-see `the Qt cmake manual <https://contribute.qt-project.org/doc/qt-5/cmake-manual.html>`_
+see `the Qt cmake manual <https://doc.qt.io/qt-5/cmake-manual.html>`_
 for your Qt version.
 
 Qt 4 and Qt 5 may be used together in the same
diff --git a/Help/manual/cmake-toolchains.7.rst b/Help/manual/cmake-toolchains.7.rst
index a831fa6..e32bd29 100644
--- a/Help/manual/cmake-toolchains.7.rst
+++ b/Help/manual/cmake-toolchains.7.rst
@@ -573,12 +573,12 @@
 * :prop_tgt:`ANDROID_SKIP_ANT_STEP`
 * :prop_tgt:`ANDROID_STL_TYPE`
 
-.. _`Cross Compiling for iOS, tvOS, or watchOS`:
+.. _`Cross Compiling for iOS, tvOS, visionOS, or watchOS`:
 
-Cross Compiling for iOS, tvOS, or watchOS
------------------------------------------
+Cross Compiling for iOS, tvOS, visionOS, or watchOS
+---------------------------------------------------
 
-For cross-compiling to iOS, tvOS, or watchOS, the :generator:`Xcode`
+For cross-compiling to iOS, tvOS, visionOS, or watchOS, the :generator:`Xcode`
 generator is recommended.  The :generator:`Unix Makefiles` or
 :generator:`Ninja` generators can also be used, but they require the
 project to handle more areas like target CPU selection and code signing.
@@ -591,13 +591,14 @@
 necessary (see :ref:`Switching Between Device and Simulator` below).
 A list of available SDKs can be obtained by running ``xcodebuild -showsdks``.
 
-=======  ================= ==================== ================
-OS       CMAKE_SYSTEM_NAME Device SDK (default) Simulator SDK
-=======  ================= ==================== ================
-iOS      iOS               iphoneos             iphonesimulator
-tvOS     tvOS              appletvos            appletvsimulator
-watchOS  watchOS           watchos              watchsimulator
-=======  ================= ==================== ================
+========  ================= ==================== ================
+OS        CMAKE_SYSTEM_NAME Device SDK (default) Simulator SDK
+========  ================= ==================== ================
+iOS       iOS               iphoneos             iphonesimulator
+tvOS      tvOS              appletvos            appletvsimulator
+visionOS  visionOS          xros                 xrsimulator
+watchOS   watchOS           watchos              watchsimulator
+========  ================= ==================== ================
 
 For example, to create a CMake configuration for iOS, the following
 command is sufficient:
@@ -608,7 +609,7 @@
 
 Variable :variable:`CMAKE_OSX_ARCHITECTURES` can be used to set architectures
 for both device and simulator. Variable :variable:`CMAKE_OSX_DEPLOYMENT_TARGET`
-can be used to set an iOS/tvOS/watchOS deployment target.
+can be used to set an iOS/tvOS/visionOS/watchOS deployment target.
 
 Next configuration will install fat 5 architectures iOS library
 and add the ``-miphoneos-version-min=9.3``/``-mios-simulator-version-min=9.3``
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index fa7a90f..d9df773 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -590,6 +590,7 @@
    /variable/CMAKE_Fortran_MODOUT_FLAG
    /variable/CMAKE_HIP_ARCHITECTURES
    /variable/CMAKE_HIP_EXTENSIONS
+   /variable/CMAKE_HIP_PLATFORM
    /variable/CMAKE_HIP_STANDARD
    /variable/CMAKE_HIP_STANDARD_REQUIRED
    /variable/CMAKE_ISPC_HEADER_DIRECTORY
@@ -627,6 +628,7 @@
    /variable/CMAKE_LANG_FLAGS_RELEASE_INIT
    /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO
    /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT
+   /variable/CMAKE_LANG_HOST_COMPILER
    /variable/CMAKE_LANG_IGNORE_EXTENSIONS
    /variable/CMAKE_LANG_IMPLICIT_INCLUDE_DIRECTORIES
    /variable/CMAKE_LANG_IMPLICIT_LINK_DIRECTORIES
diff --git a/Help/manual/cmake.1.rst b/Help/manual/cmake.1.rst
index b5848f7..5223acb 100644
--- a/Help/manual/cmake.1.rst
+++ b/Help/manual/cmake.1.rst
@@ -309,6 +309,8 @@
 
 .. option:: --debug-find
 
+ .. versionadded:: 3.17
+
  Put cmake find commands in a debug mode.
 
  Print extra find call information during the cmake run to standard
@@ -318,6 +320,8 @@
 
 .. option:: --debug-find-pkg=<pkg>[,...]
 
+ .. versionadded:: 3.23
+
  Put cmake find commands in a debug mode when running under calls
  to :command:`find_package(\<pkg\>) <find_package>`, where ``<pkg>``
  is an entry in the given comma-separated list of case-sensitive package
@@ -328,6 +332,8 @@
 
 .. option:: --debug-find-var=<var>[,...]
 
+ .. versionadded:: 3.23
+
  Put cmake find commands in a debug mode when called with ``<var>``
  as the result variable, where ``<var>`` is an entry in the given
  comma-separated list.
@@ -349,6 +355,8 @@
 
 .. option:: --trace-format=<format>
 
+ .. versionadded:: 3.17
+
  Put cmake in trace mode and sets the trace output format.
 
  ``<format>`` can be one of the following values.
@@ -471,12 +479,16 @@
 
 .. option:: --compile-no-warning-as-error
 
+ .. versionadded:: 3.24
+
  Ignore target property :prop_tgt:`COMPILE_WARNING_AS_ERROR` and variable
  :variable:`CMAKE_COMPILE_WARNING_AS_ERROR`, preventing warnings from being
  treated as errors on compile.
 
 .. option:: --profiling-output=<path>
 
+ .. versionadded:: 3.18
+
  Used in conjunction with
  :option:`--profiling-format <cmake --profiling-format>` to output to a
  given path.
@@ -1325,6 +1337,8 @@
 Run a Workflow Preset
 =====================
 
+.. versionadded:: 3.25
+
 .. program:: cmake
 
 :manual:`CMake Presets <cmake-presets(7)>` provides a way to execute multiple
diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst
index 5c3889e..7009bf1 100644
--- a/Help/manual/ctest.1.rst
+++ b/Help/manual/ctest.1.rst
@@ -1818,6 +1818,25 @@
   uppercase in the ``CTEST_RESOURCE_GROUP_<num>_<resource-type>`` environment
   variable.
 
+.. _`ctest-resource-dynamically-generated-spec-file`:
+
+Dynamically-Generated Resource Specification File
+-------------------------------------------------
+
+.. versionadded:: 3.28
+
+A project may optionally specify a single test which will be used to
+dynamically generate the resource specification file that CTest will use for
+scheduling tests that use resources. The test that generates the file must
+have the :prop_test:`GENERATED_RESOURCE_SPEC_FILE` property set, and must have
+exactly one fixture in its :prop_test:`FIXTURES_SETUP` property. This fixture
+is considered by CTest to have special meaning: it's the fixture that generates
+the resource spec file. The fixture may have any name. If such a fixture
+exists, all tests that have :prop_test:`RESOURCE_GROUPS` set must have the
+fixture in their :prop_test:`FIXTURES_REQUIRED`, and a resource spec file may
+not be specified with the ``--resource-spec-file`` argument or the
+:variable:`CTEST_RESOURCE_SPEC_FILE` variable.
+
 See Also
 ========
 
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index adfb1cb..d27faa1 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -9,9 +9,9 @@
           "const": 1,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV1"}
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV1" }
       },
       "additionalProperties": false
     },
@@ -21,11 +21,11 @@
           "const": 2,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV1"},
-        "buildPresets": { "$ref": "#/definitions/buildPresetsV2"},
-        "testPresets": { "$ref": "#/definitions/testPresetsV2"}
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV1" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV2" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV2" }
       },
       "additionalProperties": false
     },
@@ -35,11 +35,11 @@
           "const": 3,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
-        "buildPresets": { "$ref": "#/definitions/buildPresetsV3"},
-        "testPresets": { "$ref": "#/definitions/testPresetsV3"}
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV3" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV3" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV3" }
       },
       "additionalProperties": false
     },
@@ -49,12 +49,12 @@
           "const": 4,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
-        "buildPresets": { "$ref": "#/definitions/buildPresetsV4"},
-        "testPresets": { "$ref": "#/definitions/testPresetsV3"},
-        "include": { "$ref": "#/definitions/include"}
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV3" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV4" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV3" },
+        "include": { "$ref": "#/definitions/include" }
       },
       "additionalProperties": false
     },
@@ -64,12 +64,12 @@
           "const": 5,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
-        "buildPresets": { "$ref": "#/definitions/buildPresetsV4"},
-        "testPresets": { "$ref": "#/definitions/testPresetsV5"},
-        "include": { "$ref": "#/definitions/include"}
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV3" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV4" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV5" },
+        "include": { "$ref": "#/definitions/include" }
       },
       "additionalProperties": false
     },
@@ -79,14 +79,14 @@
           "const": 6,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
-        "buildPresets": { "$ref": "#/definitions/buildPresetsV4"},
-        "testPresets": { "$ref": "#/definitions/testPresetsV6"},
-        "packagePresets": { "$ref": "#/definitions/packagePresetsV6"},
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV3" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV4" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV6" },
+        "packagePresets": { "$ref": "#/definitions/packagePresetsV6" },
         "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" },
-        "include": { "$ref": "#/definitions/include"}
+        "include": { "$ref": "#/definitions/include" }
       },
       "additionalProperties": false
     },
@@ -96,14 +96,32 @@
           "const": 7,
           "description": "A required integer representing the version of the JSON schema."
         },
-        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
         "vendor": { "$ref": "#/definitions/vendor" },
-        "configurePresets": { "$ref": "#/definitions/configurePresetsV7"},
-        "buildPresets": { "$ref": "#/definitions/buildPresetsV4"},
-        "testPresets": { "$ref": "#/definitions/testPresetsV6"},
-        "packagePresets": { "$ref": "#/definitions/packagePresetsV6"},
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV7" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV4" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV6" },
+        "packagePresets": { "$ref": "#/definitions/packagePresetsV6" },
         "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" },
-        "include": { "$ref": "#/definitions/include"}
+        "include": { "$ref": "#/definitions/include" }
+      },
+      "additionalProperties": false
+    },
+    {
+      "properties": {
+        "$schema": { "$ref": "#/definitions/$schema" },
+        "version": {
+          "const": 8,
+          "description": "A required integer representing the version of the JSON schema."
+        },
+        "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired" },
+        "vendor": { "$ref": "#/definitions/vendor" },
+        "configurePresets": { "$ref": "#/definitions/configurePresetsV7" },
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV4" },
+        "testPresets": { "$ref": "#/definitions/testPresetsV6" },
+        "packagePresets": { "$ref": "#/definitions/packagePresetsV6" },
+        "workflowPresets": { "$ref": "#/definitions/workflowPresetsV6" },
+        "include": { "$ref": "#/definitions/include" }
       },
       "additionalProperties": false
     }
@@ -112,6 +130,11 @@
     "version"
   ],
   "definitions": {
+    "$schema": {
+      "type": "string",
+      "description": "The schema against which to verify this document.",
+      "format": "uri-reference"
+    },
     "cmakeMinimumRequired": {
       "type": "object",
       "description": "An optional object representing the minimum version of CMake needed to build this project.",
diff --git a/Help/policy/CMP0152.rst b/Help/policy/CMP0152.rst
new file mode 100644
index 0000000..d7e8692
--- /dev/null
+++ b/Help/policy/CMP0152.rst
@@ -0,0 +1,20 @@
+CMP0152
+-------
+
+.. versionadded:: 3.28
+
+:command:`file(REAL_PATH)` resolves symlinks before collapsing ../ components.
+
+In CMake 3.27 and below, :command:`file(REAL_PATH)` collapsed any ``../``
+components in a path before resolving symlinks.  This produced incorrect
+results when the ``../`` collapsed away a symlink.
+
+The ``OLD`` behavior for this policy is to collapse ``../`` components before
+resolving symlinks.
+The ``NEW`` behavior for this policy is to resolve all symlinks before
+collapsing ``../`` components.
+
+This policy was introduced in CMake version 3.28.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0153.rst b/Help/policy/CMP0153.rst
new file mode 100644
index 0000000..838f082
--- /dev/null
+++ b/Help/policy/CMP0153.rst
@@ -0,0 +1,14 @@
+CMP0153
+-------
+
+.. versionadded:: 3.28
+
+The :command:`exec_program` command should not be called.
+
+This command has long been superseded by the :command:`execute_process`
+command and has been deprecated since CMake 3.0.
+
+.. |disallowed_version| replace:: 3.28
+.. include:: DISALLOWED_COMMAND.txt
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0154.rst b/Help/policy/CMP0154.rst
new file mode 100644
index 0000000..cb93d41
--- /dev/null
+++ b/Help/policy/CMP0154.rst
@@ -0,0 +1,53 @@
+CMP0154
+-------
+
+.. versionadded:: 3.28
+
+Generated files are private by default in targets using :ref:`file sets`.
+
+CMake 3.27 and below assume that any file generated as an output or byproduct
+of :command:`add_custom_command` or :command:`add_custom_target` may be a
+public header file meant for inclusion by dependents' source files.  This
+requires :ref:`Ninja Generators` to add conservative order-only dependencies
+that prevent a target's source files from compiling before custom commands
+from the target's dependencies are finished, even if those custom commands
+only produce sources private to their own target.
+
+:ref:`File Sets`, introduced by CMake 3.23, provide a way to express the
+visibility of generated header files.  CMake 3.28 and above prefer to
+assume that, in targets using file sets, generated files are private to
+their own target by default.  Generated public headers must be specified
+as members of a ``PUBLIC`` (or ``INTERFACE``) ``FILE_SET``, typically of
+type ``HEADERS``.  With this information, :ref:`Ninja Generators` may omit
+the above-mentioned conservative dependencies and produce more efficient
+build graphs.
+
+This policy provides compatibility for projects using file sets in targets
+with generated header files that have not been updated.  Such projects
+should be updated to express generated public headers in a file set.
+For example:
+
+.. code-block:: cmake
+
+  add_custom_command(
+    OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/foo.h
+    ...
+  )
+  target_sources(foo
+    PUBLIC FILE_SET HEADERS
+      BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}
+      FILES     ${CMAKE_CURRENT_BINARY_DIR}/foo.h
+  )
+
+The ``OLD`` behavior for this policy is to assume generated files are
+public, even in targets using file sets, and for :ref:`Ninja Generators`
+to produce conservative build graphs.  The ``NEW`` behavior for this
+policy is to assume generated files are private in targets using file sets,
+and for :ref:`Ninja Generators` to produce more efficient build graphs.
+
+This policy was introduced in CMake version 3.28.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/policy/CMP0155.rst b/Help/policy/CMP0155.rst
new file mode 100644
index 0000000..2dafadf
--- /dev/null
+++ b/Help/policy/CMP0155.rst
@@ -0,0 +1,26 @@
+CMP0155
+-------
+
+.. versionadded:: 3.28
+
+C++ sources in targets with at least C++20 are scanned for imports.
+
+CMake 3.27 and below assume that C++ sources do not ``import`` modules.
+CMake 3.28 and above prefer to assume that C++ sources in targets using C++20
+or higher might ``import`` modules, and must be scanned before compiling,
+unless explicitly disabled.  This policy provides compatibility for projects
+that use C++20 or higher, without modules, that have not been updated to turn
+off scanning, e.g., via the :variable:`CMAKE_CXX_SCAN_FOR_MODULES` variable.
+See the :manual:`cmake-cxxmodules(7)` manual for more details on C++ module
+support.
+
+The ``OLD`` behavior for this policy is to assume that C++ 20 and newer
+sources do not import modules.  The ``NEW`` behavior for this policy is to
+assume that C++ 20 and newer files may import modules, and need to be scanned.
+
+This policy was introduced in CMake version 3.28.  Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_gbl/XCODE_EMIT_EFFECTIVE_PLATFORM_NAME.rst b/Help/prop_gbl/XCODE_EMIT_EFFECTIVE_PLATFORM_NAME.rst
index 392b704..6794a26 100644
--- a/Help/prop_gbl/XCODE_EMIT_EFFECTIVE_PLATFORM_NAME.rst
+++ b/Help/prop_gbl/XCODE_EMIT_EFFECTIVE_PLATFORM_NAME.rst
@@ -16,8 +16,8 @@
 - If set to ``ON`` it will always be emitted
 - If set to ``OFF`` it will never be emitted
 - If unset (the default) it will only be emitted when the project was
-  configured for an embedded Xcode SDK like iOS, tvOS, watchOS or any
-  of the simulators.
+  configured for an embedded Xcode SDK like iOS, tvOS, visionOS, watchOS
+  or any of the simulators.
 
 .. note::
 
diff --git a/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst b/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst
index 5b704dc..d10969a 100644
--- a/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst
+++ b/Help/prop_sf/CXX_SCAN_FOR_MODULES.rst
@@ -1,7 +1,7 @@
 CXX_SCAN_FOR_MODULES
 --------------------
 
-.. versionadded:: 3.26
+.. versionadded:: 3.28
 
 ``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan the
 source for C++ module dependencies.  See also the
@@ -16,8 +16,3 @@
 Note that scanning is only performed if C++20 or higher is enabled for the
 target and the source uses the ``CXX`` language.  Scanning for modules in
 sources belonging to file sets of type ``CXX_MODULES`` is always performed.
-
-.. note ::
-
-  This setting is meaningful only when experimental support for C++ modules
-  has been enabled by the ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` gate.
diff --git a/Help/prop_test/GENERATED_RESOURCE_SPEC_FILE.rst b/Help/prop_test/GENERATED_RESOURCE_SPEC_FILE.rst
new file mode 100644
index 0000000..89596ac
--- /dev/null
+++ b/Help/prop_test/GENERATED_RESOURCE_SPEC_FILE.rst
@@ -0,0 +1,7 @@
+GENERATED_RESOURCE_SPEC_FILE
+----------------------------
+
+.. versionadded:: 3.28
+
+Path to the :ref:`dynamically-generated resource spec file
+<ctest-resource-dynamically-generated-spec-file>` generated by this test.
diff --git a/Help/prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN.rst b/Help/prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN.rst
index d5fd825..9255d8e 100644
--- a/Help/prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN.rst
+++ b/Help/prop_tgt/COMPATIBLE_INTERFACE_NUMBER_MIN.rst
@@ -1,7 +1,7 @@
 COMPATIBLE_INTERFACE_NUMBER_MIN
 -------------------------------
 
-Properties whose maximum value from the link interface will be used.
+Properties whose minimum value from the link interface will be used.
 
 The ``COMPATIBLE_INTERFACE_NUMBER_MIN`` property may contain a list of
 properties for this target whose minimum value may be read at generate
diff --git a/Help/prop_tgt/CXX_MODULE_DIRS.rst b/Help/prop_tgt/CXX_MODULE_DIRS.rst
index a32b5b1..6c5dd3a 100644
--- a/Help/prop_tgt/CXX_MODULE_DIRS.rst
+++ b/Help/prop_tgt/CXX_MODULE_DIRS.rst
@@ -1,11 +1,7 @@
 CXX_MODULE_DIRS
 ---------------
 
-.. versionadded:: 3.25
-
-.. note ::
-
-  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+.. versionadded:: 3.28
 
 Semicolon-separated list of base directories of the target's default
 C++ module set (i.e. the file set with name and type ``CXX_MODULES``). The
diff --git a/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst b/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst
index 9190991..8b6e4dd 100644
--- a/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst
+++ b/Help/prop_tgt/CXX_MODULE_DIRS_NAME.rst
@@ -1,11 +1,7 @@
 CXX_MODULE_DIRS_<NAME>
 ----------------------
 
-.. versionadded:: 3.25
-
-.. note ::
-
-  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+.. versionadded:: 3.28
 
 Semicolon-separated list of base directories of the target's ``<NAME>`` C++
 module set, which has the set type ``CXX_MODULES``. The property supports
diff --git a/Help/prop_tgt/CXX_MODULE_SET.rst b/Help/prop_tgt/CXX_MODULE_SET.rst
index f5cd8b2..851de86 100644
--- a/Help/prop_tgt/CXX_MODULE_SET.rst
+++ b/Help/prop_tgt/CXX_MODULE_SET.rst
@@ -1,11 +1,7 @@
 CXX_MODULE_SET
 --------------
 
-.. versionadded:: 3.25
-
-.. note ::
-
-  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+.. versionadded:: 3.28
 
 Semicolon-separated list of files in the target's default C++ module set,
 (i.e. the file set with name and type ``CXX_MODULES``). If any of the paths
diff --git a/Help/prop_tgt/CXX_MODULE_SETS.rst b/Help/prop_tgt/CXX_MODULE_SETS.rst
index 0e8945a..2165027 100644
--- a/Help/prop_tgt/CXX_MODULE_SETS.rst
+++ b/Help/prop_tgt/CXX_MODULE_SETS.rst
@@ -1,11 +1,7 @@
 CXX_MODULE_SETS
 ---------------
 
-.. versionadded:: 3.25
-
-.. note ::
-
-  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+.. versionadded:: 3.28
 
 Read-only list of the target's ``PRIVATE`` and ``PUBLIC`` C++ module sets (i.e.
 all file sets with the type ``CXX_MODULES``). Files listed in these file sets
diff --git a/Help/prop_tgt/CXX_MODULE_SET_NAME.rst b/Help/prop_tgt/CXX_MODULE_SET_NAME.rst
index 5674c99..e73e28a 100644
--- a/Help/prop_tgt/CXX_MODULE_SET_NAME.rst
+++ b/Help/prop_tgt/CXX_MODULE_SET_NAME.rst
@@ -1,11 +1,7 @@
 CXX_MODULE_SET_<NAME>
 ---------------------
 
-.. versionadded:: 3.25
-
-.. note ::
-
-  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+.. versionadded:: 3.28
 
 Semicolon-separated list of files in the target's ``<NAME>`` C++ module set,
 which has the set type ``CXX_MODULES``. If any of the paths are relative, they
diff --git a/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst b/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst
index 93a1b73..791914e 100644
--- a/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst
+++ b/Help/prop_tgt/CXX_SCAN_FOR_MODULES.rst
@@ -1,7 +1,7 @@
 CXX_SCAN_FOR_MODULES
 --------------------
 
-.. versionadded:: 3.26
+.. versionadded:: 3.28
 
 ``CXX_SCAN_FOR_MODULES`` is a boolean specifying whether CMake will scan C++
 sources in the target for module dependencies.  See also the
@@ -20,8 +20,3 @@
 Note that scanning is only performed if C++20 or higher is enabled for the
 target.  Scanning for modules in the target's sources belonging to file sets
 of type ``CXX_MODULES`` is always performed.
-
-.. note ::
-
-  This setting is meaningful only when experimental support for C++ modules
-  has been enabled by the ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` gate.
diff --git a/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst b/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst
index 59ef00f..c86b218 100644
--- a/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst
+++ b/Help/prop_tgt/DLL_NAME_WITH_SOVERSION.rst
@@ -3,8 +3,8 @@
 
 .. versionadded:: 3.27
 
-This property control whether the :prop_tgt:`SOVERSION` target
-property are added to the filename of generated DLL filenames
+This property controls whether the :prop_tgt:`SOVERSION` target
+property is added to the filename of generated DLL filenames
 for the Windows platform, which is selected when the
 :variable:`WIN32` variable is set.
 
diff --git a/Help/prop_tgt/HIP_ARCHITECTURES.rst b/Help/prop_tgt/HIP_ARCHITECTURES.rst
index 06f956b..58a813d 100644
--- a/Help/prop_tgt/HIP_ARCHITECTURES.rst
+++ b/Help/prop_tgt/HIP_ARCHITECTURES.rst
@@ -3,7 +3,8 @@
 
 .. versionadded:: 3.21
 
-List of AMD GPU architectures to generate device code for.
+List of GPU architectures to for which to generate device code.
+Architecture names are interpreted based on :variable:`CMAKE_HIP_PLATFORM`.
 
 A non-empty false value (e.g. ``OFF``) disables adding architectures.
 This is intended to support packagers and rare cases where full control
diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst
new file mode 100644
index 0000000..b068191
--- /dev/null
+++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS.rst
@@ -0,0 +1,10 @@
+IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS
+----------------------------------------
+
+.. versionadded:: 3.28
+
+Preprocessor definitions for compiling an ``IMPORTED`` target's C++ module
+sources.
+
+CMake will automatically drop some definitions that are not supported
+by the native build tool.
diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst
new file mode 100644
index 0000000..cea359f
--- /dev/null
+++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_FEATURES.rst
@@ -0,0 +1,9 @@
+IMPORTED_CXX_MODULES_COMPILE_FEATURES
+-------------------------------------
+
+.. versionadded:: 3.28
+
+Compiler features enabled for this ``IMPORTED`` target's C++ modules.
+
+The value of this property is used by the generators to set the include
+paths for the compiler.
diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst
new file mode 100644
index 0000000..cf7bc5b
--- /dev/null
+++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_COMPILE_OPTIONS.rst
@@ -0,0 +1,9 @@
+IMPORTED_CXX_MODULES_COMPILE_OPTIONS
+------------------------------------
+
+.. versionadded:: 3.28
+
+List of options to pass to the compiler for this ``IMPORTED`` target's C++
+modules.
+
+.. include:: ../command/OPTIONS_SHELL.txt
diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst
new file mode 100644
index 0000000..46d74ea
--- /dev/null
+++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES.rst
@@ -0,0 +1,10 @@
+IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES
+----------------------------------------
+
+.. versionadded:: 3.28
+
+List of preprocessor include file search directories when compiling C++
+modules for ``IMPORTED`` targets.
+
+The value of this property is used by the generators to set the include
+paths for the compiler.
diff --git a/Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst b/Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst
new file mode 100644
index 0000000..ff5cec8
--- /dev/null
+++ b/Help/prop_tgt/IMPORTED_CXX_MODULES_LINK_LIBRARIES.rst
@@ -0,0 +1,7 @@
+IMPORTED_CXX_MODULES_LINK_LIBRARIES
+-----------------------------------
+
+.. versionadded:: 3.28
+
+List of direct dependencies to use for usage requirements for C++ modules in
+the target's C++ modules.
diff --git a/Help/prop_tgt/IMPORTED_IMPLIB.rst b/Help/prop_tgt/IMPORTED_IMPLIB.rst
index e67acba..27601d2 100644
--- a/Help/prop_tgt/IMPORTED_IMPLIB.rst
+++ b/Help/prop_tgt/IMPORTED_IMPLIB.rst
@@ -11,6 +11,12 @@
 * On macOS, to an import file (e.g. ``.tbd``) created for shared libraries (see
   the :prop_tgt:`ENABLE_EXPORTS` target property). For frameworks this is the
   location of the ``.tbd`` file symlink just inside the framework folder.
+* .. versionadded:: 3.28
+    On non-DLL platforms, to the location of a shared library.
+    When set without also specifying an :prop_tgt:`IMPORTED_LOCATION`,
+    the library is considered to be a stub, and its location will not
+    be added as a runtime search path to dependents that link it.
+
 
 The ``IMPORTED_IMPLIB`` target property may be overridden for a
 given configuration ``<CONFIG>`` by the configuration-specific
diff --git a/Help/prop_tgt/IMPORTED_LOCATION.rst b/Help/prop_tgt/IMPORTED_LOCATION.rst
index a7207d8..915085b 100644
--- a/Help/prop_tgt/IMPORTED_LOCATION.rst
+++ b/Help/prop_tgt/IMPORTED_LOCATION.rst
@@ -15,6 +15,17 @@
 libraries this is the location of the file to be linked.  Ignored for
 non-imported targets.
 
+.. versionadded:: 3.28
+
+  For frameworks on macOS, this may be the location of the framework folder
+  itself.
+
+.. versionadded:: 3.28
+
+  This may be the location of a ``.xcframework`` folder on macOS. If it is,
+  any target that links against it will get the selected library's ``Headers``
+  directory as a usage requirement.
+
 The ``IMPORTED_LOCATION`` target property may be overridden for a
 given configuration ``<CONFIG>`` by the configuration-specific
 :prop_tgt:`IMPORTED_LOCATION_<CONFIG>` target property.  Furthermore,
diff --git a/Help/prop_tgt/IMPORTED_OBJECTS.rst b/Help/prop_tgt/IMPORTED_OBJECTS.rst
index d71c219..9aacea7 100644
--- a/Help/prop_tgt/IMPORTED_OBJECTS.rst
+++ b/Help/prop_tgt/IMPORTED_OBJECTS.rst
@@ -31,7 +31,7 @@
 it does not generate universal object file binaries.
 
 A further complication with the :generator:`Xcode` generator is that when
-targeting device platforms (iOS, tvOS or watchOS), the :generator:`Xcode`
+targeting device platforms (iOS, tvOS, visionOS or watchOS), the :generator:`Xcode`
 generator has the ability to use either the device or simulator SDK without
 needing CMake to be re-run.  The SDK can be selected at build time.
 But since some architectures can be supported by both the device and the
diff --git a/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst b/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst
index c7ed46d..fd6e34b 100644
--- a/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst
+++ b/Help/prop_tgt/INTERFACE_CXX_MODULE_SETS.rst
@@ -1,11 +1,7 @@
 INTERFACE_CXX_MODULE_SETS
 -------------------------
 
-.. versionadded:: 3.25
-
-.. note ::
-
-  Experimental. Gated by ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API``
+.. versionadded:: 3.28
 
 Read-only list of the target's ``PUBLIC`` C++ module sets (i.e. all file sets
 with the type ``CXX_MODULES``). Files listed in these C++ module sets can be
diff --git a/Help/prop_tgt/IOS_INSTALL_COMBINED.rst b/Help/prop_tgt/IOS_INSTALL_COMBINED.rst
index 92d60dc..c296691 100644
--- a/Help/prop_tgt/IOS_INSTALL_COMBINED.rst
+++ b/Help/prop_tgt/IOS_INSTALL_COMBINED.rst
@@ -2,18 +2,27 @@
 --------------------
 
 .. versionadded:: 3.5
+.. deprecated:: 3.28
+
+  :prop_tgt:`IOS_INSTALL_COMBINED` was designed to make universal binaries
+  containing iOS/arm* device code paired with iOS Simulator/x86_64 code
+  (or similar for other Apple embedded platforms). Universal binaries can only
+  differentiate code based on CPU type, so this only made sense before the
+  days of arm64 macOS machines (i.e. iOS Simulator/arm64). Apple now
+  recommends xcframeworks, which contain multiple binaries for different
+  platforms, for this use case.
 
 Build a combined (device and simulator) target when installing.
 
-When this property is set to set to false (which is the default) then it will
+When this property is set to false, which is the default, then it will
 either be built with the device SDK or the simulator SDK depending on the SDK
 set. But if this property is set to true then the target will at install time
-also be built for the corresponding SDK and combined into one library.
+also be built for the other SDK and combined into one library.
 
 .. note::
 
-  If a selected architecture is available for both: device SDK and simulator
+  If a selected architecture is available for both device SDK and simulator
   SDK it will be built for the SDK selected by :variable:`CMAKE_OSX_SYSROOT`
-  and removed from the corresponding SDK.
+  and removed from the other SDK.
 
 This feature requires at least Xcode version 6.
diff --git a/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst b/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst
index afc6b31..d1d80a8 100644
--- a/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst
+++ b/Help/prop_tgt/Swift_LANGUAGE_VERSION.rst
@@ -4,5 +4,6 @@
 .. versionadded:: 3.16
 
 This property sets the language version for the Swift sources in the target.  If
-one is not specified, it will default to ``<CMAKE_Swift_LANGUAGE_VERSION>`` if
-specified, otherwise it is the latest version supported by the compiler.
+one is not specified, it will default to
+:variable:`CMAKE_Swift_LANGUAGE_VERSION` if specified, otherwise it is the
+latest version supported by the compiler.
diff --git a/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst b/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst
index 5bf47a3..8c136f2 100644
--- a/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst
+++ b/Help/prop_tgt/VS_DEBUGGER_COMMAND.rst
@@ -11,5 +11,5 @@
 :variable:`CMAKE_VS_DEBUGGER_COMMAND` if it is set when a target is
 created.
 
-This property only works for Visual Studio 11 2012 and above;
+This property only works for Visual Studio 12 2013 and above;
 it is ignored on other generators.
diff --git a/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst b/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst
index 4b9dff7..2656826 100644
--- a/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst
+++ b/Help/prop_tgt/VS_DEBUGGER_COMMAND_ARGUMENTS.rst
@@ -11,5 +11,5 @@
 :variable:`CMAKE_VS_DEBUGGER_COMMAND_ARGUMENTS` if it is set when a target is
 created.
 
-This property only works for Visual Studio 11 2012 and above;
+This property only works for Visual Studio 12 2013 and above;
 it is ignored on other generators.
diff --git a/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst b/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst
index 8373dbb..d78d594 100644
--- a/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst
+++ b/Help/prop_tgt/VS_DEBUGGER_ENVIRONMENT.rst
@@ -11,5 +11,5 @@
 :variable:`CMAKE_VS_DEBUGGER_ENVIRONMENT` if it is set when a target is
 created.
 
-This property only works for Visual Studio 11 2012 and above;
+This property only works for Visual Studio 12 2013 and above;
 it is ignored on other generators.
diff --git a/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst b/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst
index 3942047..1026dfa 100644
--- a/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst
+++ b/Help/prop_tgt/VS_DEBUGGER_WORKING_DIRECTORY.rst
@@ -11,5 +11,5 @@
 :variable:`CMAKE_VS_DEBUGGER_WORKING_DIRECTORY` if it is set when a target is
 created.
 
-This property only works for Visual Studio 11 2012 and above;
+This property only works for Visual Studio 12 2013 and above;
 it is ignored on other generators.
diff --git a/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst b/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst
index 8a85ba4..eeb7dda 100644
--- a/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst
+++ b/Help/prop_tgt/VS_DOTNET_STARTUP_OBJECT.rst
@@ -12,7 +12,7 @@
 than one ``Main()`` method is available in the current project, the property
 becomes mandatory for building the project.
 
-This property only works for Visual Studio 11 2012 and above;
+This property only works for Visual Studio 12 2013 and above;
 it is ignored on other generators.
 
 .. code-block:: cmake
diff --git a/Help/prop_tgt/VS_KEYWORD.rst b/Help/prop_tgt/VS_KEYWORD.rst
index 221b986..f04d109 100644
--- a/Help/prop_tgt/VS_KEYWORD.rst
+++ b/Help/prop_tgt/VS_KEYWORD.rst
@@ -7,4 +7,4 @@
 integration works better if this is set to Qt4VSv1.0.
 
 Use the :prop_tgt:`VS_GLOBAL_KEYWORD` target property to set the
-keyword for Visual Studio 11 (2012) and newer.
+keyword for Visual Studio 12 (2013) and newer.
diff --git a/Help/prop_tgt/XCODE_EMBED_type.rst b/Help/prop_tgt/XCODE_EMBED_type.rst
index da744c2..76b0e3d 100644
--- a/Help/prop_tgt/XCODE_EMBED_type.rst
+++ b/Help/prop_tgt/XCODE_EMBED_type.rst
@@ -37,6 +37,12 @@
   The specified items will be added to the ``Embed PlugIns`` build phase.
   They must be CMake target names.
 
+``RESOURCES``
+  .. versionadded:: 3.28
+
+  The specified items will be added to the ``Embed Resources`` build phase.
+  They must be CMake target names.
+
 See also :prop_tgt:`XCODE_EMBED_<type>_PATH`,
 :prop_tgt:`XCODE_EMBED_<type>_REMOVE_HEADERS_ON_COPY` and
 :prop_tgt:`XCODE_EMBED_<type>_CODE_SIGN_ON_COPY`.
diff --git a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
index 5a5c65f..ef04d14 100644
--- a/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
+++ b/Help/prop_tgt/XCODE_EMBED_type_PATH.rst
@@ -22,3 +22,6 @@
 
 ``PLUGINS``
   .. versionadded:: 3.23
+
+``RESOURCES``
+  .. versionadded:: 3.28
diff --git a/Help/release/3.14.rst b/Help/release/3.14.rst
index 8a9738c..5fedf7d 100644
--- a/Help/release/3.14.rst
+++ b/Help/release/3.14.rst
@@ -63,7 +63,8 @@
 Platforms
 ---------
 
-* CMake now supports :ref:`Cross Compiling for iOS, tvOS, or watchOS`
+* CMake now supports
+  :ref:`Cross Compiling for iOS, tvOS, or watchOS <Cross Compiling for iOS, tvOS, visionOS, or watchOS>`
   using simple toolchain files.
 
 Command-Line
diff --git a/Help/release/3.28.rst b/Help/release/3.28.rst
new file mode 100644
index 0000000..200c0a3
--- /dev/null
+++ b/Help/release/3.28.rst
@@ -0,0 +1,203 @@
+CMake 3.28 Release Notes
+************************
+
+.. only:: html
+
+  .. contents::
+
+Changes made since CMake 3.27 include the following.
+
+New Features
+============
+
+Languages
+---------
+
+* C++ 20 named modules are now supported by :ref:`Ninja Generators`
+  and :ref:`Visual Studio Generators` for VS 2022 and newer, in combination
+  with the MSVC 14.34 toolset (provided with VS 17.4) and newer, LLVM/Clang
+  16.0 and newer, and GCC 14 (after the 2023-09-20 daily bump) and newer.
+  See :manual:`cmake-cxxmodules(7)` for details.
+
+* ``HIP`` language code may now be compiled for NVIDIA GPUs
+  using the NVIDIA CUDA Compiler (NVCC).
+  See the :variable:`CMAKE_HIP_PLATFORM` variable.
+
+Platforms
+---------
+
+* On Apple platforms, ``.xcframework`` folders are now supported:
+
+  * The :command:`find_library` command now finds ``.xcframework`` folders.
+
+  * The :command:`target_link_libraries` command now supports
+    linking against a ``.xcframework`` folder.
+
+  * The :prop_tgt:`IMPORTED_LOCATION` target property of an imported
+    library target may now be the path to a ``.xcframework`` folder.
+
+* Apple visionOS and its ``xros`` and ``xrsimulator`` SDKs are now supported.
+  Compiling for Apple visionOS can be requested by setting
+  :variable:`CMAKE_SYSTEM_NAME` to ``visionOS``.
+  See :ref:`Cross Compiling for iOS, tvOS, visionOS, or watchOS`
+  for more information.
+
+Presets
+-------
+
+* :manual:`cmake-presets(7)` files now support schema version ``8``.
+  It adds support for a ``$schema`` field.
+
+Compilers
+---------
+
+* Cray Clang-based compilers are now supported with
+  :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``CrayClang``.
+
+* The OrangeC compiler is now supported with
+  :variable:`compiler id <CMAKE_<LANG>_COMPILER_ID>` ``OrangeC``.
+
+Commands
+--------
+
+* The :command:`add_custom_command` and :command:`add_custom_target`
+  commands gained a ``JOB_SERVER_AWARE`` option.
+
+* The :command:`cmake_host_system_information` command gained a
+  ``MSYSTEM_PREFIX`` query for the installation prefix of a MSYS
+  or MinGW development environment on Windows hosts.
+
+* The :command:`set_property` command ``TEST`` mode gained a ``DIRECTORY``
+  option to set properties on tests in other directories.
+
+* The :command:`set_tests_properties` command gained a ``DIRECTORY``
+  option to set properties on tests in other directories.
+
+* The :command:`get_property` command ``TEST`` mode gained a ``DIRECTORY``
+  option to get properties on tests in other directories.
+
+* The :command:`get_test_property` command gained a ``DIRECTORY``
+  option to get properties on tests in other directories.
+
+Variables
+---------
+
+* The :envvar:`CMAKE_CROSSCOMPILING_EMULATOR` environment variable
+  was added to initialize the :variable:`CMAKE_CROSSCOMPILING_EMULATOR`
+  cache variable.
+
+* The :variable:`CMAKE_HIP_PLATFORM` variable was added to specify
+  the GPU platform for which HIP language sources are to be compiled
+  (``amd`` or ``nvidia``).
+
+Properties
+----------
+
+* On imported shared libraries, the :prop_tgt:`IMPORTED_IMPLIB` target
+  property may now be used without :prop_tgt:`IMPORTED_LOCATION`.
+  This can be used to represent a stub library whose location should not
+  be added as a runtime search path to dependents that link it.
+
+* The :prop_tgt:`IMPORTED_LOCATION` property of a macOS framework
+  may now be a path to the ``.framework`` folder itself.
+
+* The :prop_tgt:`XCODE_EMBED_RESOURCES <XCODE_EMBED_<type>>` target property
+  was added to tell the :generator:`Xcode` generator what targets to put in
+  the ``Embed Resources`` build phase.
+
+Modules
+-------
+
+* The :module:`ExternalProject` module now includes the
+  ``BUILD_JOB_SERVER_AWARE`` option for the
+  :command:`ExternalProject_Add` command. This option enables
+  the integration of the GNU Make job server when using an
+  explicit ``BUILD_COMMAND`` with certain :ref:`Makefile Generators`.
+  Additionally, the :command:`ExternalProject_Add_Step` command
+  has been updated to support the new ``JOB_SERVER_AWARE`` option.
+
+* The :module:`ExternalProject` module now declares ``BYPRODUCTS`` for the
+  downloaded file for generated ``download`` steps. Previously, if multiple
+  external projects downloaded to the same file, hash verification could fail.
+  Now, when using the :ref:`Ninja Generators`, this scenario is detected and
+  Ninja will raise an error stating that multiple rules generate the same file.
+
+* The :module:`FetchContent` module's :command:`FetchContent_Declare` command
+  gained an ``EXCLUDE_FROM_ALL`` option, which propagates through to the
+  :command:`add_subdirectory` call made by
+  :command:`FetchContent_MakeAvailable` for the dependency.
+
+* The :module:`FindCURL` module gained a ``CURL_USE_STATIC_LIBS`` hint
+  to select static libraries.
+
+* The :module:`FindEXPAT` module gained an ``EXPAT_USE_STATIC_LIBS`` hint
+  to select static libraries.
+
+* The :module:`FindPkgConfig` module :command:`pkg_get_variable` command
+  gained a ``DEFINE_VARIABLES`` option to pass variables to ``pkg-config``.
+
+Generator Expressions
+---------------------
+
+* The :manual:`generator expressions <cmake-generator-expressions(7)>`
+  :genex:`$<IF:...>`, :genex:`$<AND:...>`, and :genex:`$<OR:...>`
+  short-circuit to avoid unnecessary evaluation of parameters.
+
+CTest
+-----
+
+* CTest may now take a :ref:`dynamically-generated resource spec file
+  <ctest-resource-dynamically-generated-spec-file>`, which can be specified by the
+  :prop_test:`GENERATED_RESOURCE_SPEC_FILE` test property.
+
+Deprecated and Removed Features
+===============================
+
+* The :command:`exec_program` command, which has been deprecated
+  since CMake 3.0, has been removed by policy :policy:`CMP0153`.
+  Use the :command:`execute_process` command instead.
+
+* The :generator:`Visual Studio 11 2012` generator has been removed.
+
+* The :generator:`Visual Studio 12 2013` generator is now deprecated
+  and will be removed in a future version of CMake.
+
+* The :prop_tgt:`IOS_INSTALL_COMBINED` target property and corresponding
+  :variable:`CMAKE_IOS_INSTALL_COMBINED` variable have been deprecated.
+  Their functionality does not make sense on Apple Silicon hosts.
+
+* The :generator:`Xcode` generator will now issue a fatal error if
+  the Legacy Build System has been selected for Xcode 14 and
+  newer. Those Xcode versions dropped support for the Legacy Build
+  System and expect the project to be set-up for their current
+  Build System.
+
+Other Changes
+=============
+
+* Generated files, in targets using :ref:`file sets`, are now considered
+  private by default.  Generated public headers must be specified using
+  file sets.  This allows :ref:`Ninja Generators` to produce more
+  efficient build graphs.  See policy :policy:`CMP0154`.
+
+* The :command:`find_library`, :command:`find_path`, and :command:`find_file`
+  commands no longer search in installation prefixes derived from the ``PATH``
+  environment variable.  This behavior was added in CMake 3.3 to support
+  MSYS and MinGW (``MSYSTEM``) development environments on Windows, but
+  it can search undesired prefixes that happen to be in the ``PATH`` for
+  unrelated reasons.  Users who keep some ``<prefix>/bin`` directories in
+  the ``PATH`` just for their tools do not necessarily want any corresponding
+  ``<prefix>/lib`` or ``<prefix>/include`` directories searched.
+  The behavior was reverted for non-Windows platforms by CMake 3.6.
+  Now it has been reverted on Windows platforms too.
+
+  One may set the ``CMAKE_PREFIX_PATH`` environment variable with a
+  :ref:`semicolon-separated list <CMake Language Lists>` of prefixes
+  that are to be searched.
+
+* When using MinGW tools in a ``MSYSTEM`` environment on Windows,
+  the ``$MSYSTEM_PREFIX/local`` and ``$MSYSTEM_PREFIX`` prefixes are
+  now added to :variable:`CMAKE_SYSTEM_PREFIX_PATH`.
+
+* The precompiled Linux ``x86_64`` binaries provided on
+  `cmake.org <https://cmake.org/download/>`_ now require GLIBC 2.17 or higher.
diff --git a/Help/release/index.rst b/Help/release/index.rst
index fc1f744..b84bdb4 100644
--- a/Help/release/index.rst
+++ b/Help/release/index.rst
@@ -13,6 +13,7 @@
 .. toctree::
    :maxdepth: 1
 
+   3.28 <3.28>
    3.27 <3.27>
    3.26 <3.26>
    3.25 <3.25>
diff --git a/Help/variable/APPLE.rst b/Help/variable/APPLE.rst
index 810d5fc..e799397 100644
--- a/Help/variable/APPLE.rst
+++ b/Help/variable/APPLE.rst
@@ -2,4 +2,4 @@
 -----
 
 Set to ``True`` when the target system is an Apple platform
-(macOS, iOS, tvOS or watchOS).
+(macOS, iOS, tvOS, visionOS or watchOS).
diff --git a/Help/variable/CMAKE_CFG_INTDIR.rst b/Help/variable/CMAKE_CFG_INTDIR.rst
index 3a57659..3045d91 100644
--- a/Help/variable/CMAKE_CFG_INTDIR.rst
+++ b/Help/variable/CMAKE_CFG_INTDIR.rst
@@ -19,7 +19,7 @@
 ::
 
   $(ConfigurationName) = Visual Studio 9
-  $(Configuration)     = Visual Studio 11 and above
+  $(Configuration)     = Visual Studio 12 and above
   $(CONFIGURATION)     = Xcode
   .                    = Make-based tools
   .                    = Ninja
diff --git a/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst b/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst
index e21b35d..1c3a26c 100644
--- a/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst
+++ b/Help/variable/CMAKE_CROSSCOMPILING_EMULATOR.rst
@@ -12,6 +12,10 @@
   Lists>`, then the first value is the command and remaining values are its
   arguments.
 
+.. versionadded:: 3.28
+  This variable can be initialized via an
+  :envvar:`CMAKE_CROSSCOMPILING_EMULATOR` environment variable.
+
 The command will be used to run :command:`try_run` generated executables,
 which avoids manual population of the ``TryRunResults.cmake`` file.
 
diff --git a/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst b/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst
index 9817b1a..498e2e5 100644
--- a/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst
+++ b/Help/variable/CMAKE_CUDA_HOST_COMPILER.rst
@@ -3,27 +3,6 @@
 
 .. versionadded:: 3.10
 
-When :variable:`CMAKE_CUDA_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` is
-``NVIDIA``, ``CMAKE_CUDA_HOST_COMPILER`` selects the compiler executable to use
-when compiling host code for ``CUDA`` language files.
-This maps to the ``nvcc -ccbin`` option.
-
-The ``CMAKE_CUDA_HOST_COMPILER`` variable may be set explicitly before CUDA is
-first enabled by a :command:`project` or :command:`enable_language` command.
-This can be done via ``-DCMAKE_CUDA_HOST_COMPILER=...`` on the command line
-or in a :ref:`toolchain file <Cross Compiling Toolchain>`.  Or, one may set
-the :envvar:`CUDAHOSTCXX` environment variable to provide a default value.
-
-Once the CUDA language is enabled, the ``CMAKE_CUDA_HOST_COMPILER`` variable
-is read-only and changes to it are undefined behavior.
-
-.. note::
-
-  Since ``CMAKE_CUDA_HOST_COMPILER`` is meaningful only when the
-  :variable:`CMAKE_CUDA_COMPILER_ID <CMAKE_<LANG>_COMPILER_ID>` is ``NVIDIA``,
-  it does not make sense to set ``CMAKE_CUDA_HOST_COMPILER`` without also
-  setting ``CMAKE_CUDA_COMPILER`` to NVCC.
-
-.. note::
-
-  Ignored when using :ref:`Visual Studio Generators`.
+This is the original CUDA-specific name for the more general
+:variable:`CMAKE_<LANG>_HOST_COMPILER` variable.  See the latter
+for details.
diff --git a/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst b/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst
index a40bf75..28601ba 100644
--- a/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst
+++ b/Help/variable/CMAKE_CXX_SCAN_FOR_MODULES.rst
@@ -1,15 +1,10 @@
 CMAKE_CXX_SCAN_FOR_MODULES
 --------------------------
 
-.. versionadded:: 3.26
+.. versionadded:: 3.28
 
 Whether to scan C++ source files for module dependencies.
 
 This variable is used to initialize the :prop_tgt:`CXX_SCAN_FOR_MODULES`
 property on all the targets.  See that target property for additional
 information.
-
-.. note ::
-
-  This setting is meaningful only when experimental support for C++ modules
-  has been enabled by the ``CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API`` gate.
diff --git a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
index a7e9029..2e7f7c4 100644
--- a/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
+++ b/Help/variable/CMAKE_EXPORT_COMPILE_COMMANDS.rst
@@ -15,7 +15,8 @@
     {
       "directory": "/home/user/development/project",
       "command": "/usr/bin/c++ ... -c ../foo/foo.cc",
-      "file": "../foo/foo.cc"
+      "file": "../foo/foo.cc",
+      "output": "../foo.dir/foo.cc.o"
     },
 
     ...
@@ -23,7 +24,8 @@
     {
       "directory": "/home/user/development/project",
       "command": "/usr/bin/c++ ... -c ../foo/bar.cc",
-      "file": "../foo/bar.cc"
+      "file": "../foo/bar.cc",
+      "output": "../foo.dir/bar.cc.o"
     }
   ]
 
diff --git a/Help/variable/CMAKE_HIP_ARCHITECTURES.rst b/Help/variable/CMAKE_HIP_ARCHITECTURES.rst
index bcc6b35..3f17983 100644
--- a/Help/variable/CMAKE_HIP_ARCHITECTURES.rst
+++ b/Help/variable/CMAKE_HIP_ARCHITECTURES.rst
@@ -3,10 +3,14 @@
 
 .. versionadded:: 3.21
 
-Default value for :prop_tgt:`HIP_ARCHITECTURES` property of targets.
+List of GPU architectures to for which to generate device code.
+Architecture names are interpreted based on :variable:`CMAKE_HIP_PLATFORM`.
 
-This is initialized to the architectures reported by ``rocm_agent_enumerator``,
-if available, and otherwise to the default chosen by the compiler.
+This is initialized based on the value of :variable:`CMAKE_HIP_PLATFORM`:
+
+``amd``
+  Uses architectures reported by ``rocm_agent_enumerator``, if available,
+  and otherwise to a default chosen by the compiler.
 
 This variable is used to initialize the :prop_tgt:`HIP_ARCHITECTURES` property
 on all targets. See the target property for additional information.
diff --git a/Help/variable/CMAKE_HIP_PLATFORM.rst b/Help/variable/CMAKE_HIP_PLATFORM.rst
new file mode 100644
index 0000000..5e3a2b7
--- /dev/null
+++ b/Help/variable/CMAKE_HIP_PLATFORM.rst
@@ -0,0 +1,22 @@
+CMAKE_HIP_PLATFORM
+------------------
+
+.. versionadded:: 3.28
+
+GPU platform for which HIP language sources are to be compiled.
+
+The value must be one of:
+
+``amd``
+  AMD GPUs
+
+``nvidia``
+  NVIDIA GPUs
+
+If not specified, a default is computed via ``hipconfig --platform``.
+
+:variable:`CMAKE_HIP_ARCHITECTURES` entries are interpreted with
+as architectures of the GPU platform.
+
+:variable:`CMAKE_HIP_COMPILER <CMAKE_<LANG>_COMPILER>` must target
+the same GPU platform.
diff --git a/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst b/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst
index cd7fd8d..0024ba9 100644
--- a/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst
+++ b/Help/variable/CMAKE_IOS_INSTALL_COMBINED.rst
@@ -2,6 +2,9 @@
 --------------------------
 
 .. versionadded:: 3.5
+.. deprecated:: 3.28
+
+  This is deprecated because :prop_tgt:`IOS_INSTALL_COMBINED` is deprecated.
 
 Default value for :prop_tgt:`IOS_INSTALL_COMBINED` of targets.
 
diff --git a/Help/variable/CMAKE_LANG_COMPILER_ID.rst b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
index 5eb86c6..6893eea 100644
--- a/Help/variable/CMAKE_LANG_COMPILER_ID.rst
+++ b/Help/variable/CMAKE_LANG_COMPILER_ID.rst
@@ -18,6 +18,7 @@
 ``CCur``                        Concurrent Fortran
 ``Clang``                       `LLVM Clang`_
 ``Cray``                        Cray Compiler
+``CrayClang``                   Cray Clang-based Compiler
 ``Embarcadero``, ``Borland``    `Embarcadero`_
 ``Flang``                       `Classic Flang Fortran Compiler`_
 ``LLVMFlang``                   `LLVM Flang Fortran Compiler`_
@@ -34,6 +35,7 @@
 ``MSVC``                        `Microsoft Visual Studio`_
 ``NVHPC``                       `NVIDIA HPC Compiler`_
 ``NVIDIA``                      `NVIDIA CUDA Compiler`_
+``OrangeC``                     `OrangeC Compiler`_
 ``OpenWatcom``                  `Open Watcom`_
 ``PGI``                         The Portland Group
 ``PathScale``                   PathScale
@@ -62,6 +64,7 @@
 .. _NVIDIA HPC Compiler: https://developer.nvidia.com/hpc-compilers
 .. _NVIDIA CUDA Compiler: https://developer.nvidia.com/cuda-llvm-compiler
 .. _Open Watcom: https://open-watcom.github.io
+.. _OrangeC Compiler: https://github.com/LADSoft/OrangeC
 .. _Small Device C Compiler: https://sdcc.sourceforge.net
 .. _Tiny C Compiler: https://bellard.org/tcc
 .. _Tasking Compiler Toolsets: https://www.tasking.com
diff --git a/Help/variable/CMAKE_LANG_HOST_COMPILER.rst b/Help/variable/CMAKE_LANG_HOST_COMPILER.rst
new file mode 100644
index 0000000..cf3ba62
--- /dev/null
+++ b/Help/variable/CMAKE_LANG_HOST_COMPILER.rst
@@ -0,0 +1,44 @@
+CMAKE_<LANG>_HOST_COMPILER
+--------------------------
+
+.. versionadded:: 3.10
+  ``CMAKE_CUDA_HOST_COMPILER``
+
+.. versionadded:: 3.28
+  ``CMAKE_HIP_HOST_COMPILER``
+
+This variable is available when ``<LANG>`` is ``CUDA`` or ``HIP``.
+
+When :variable:`CMAKE_<LANG>_COMPILER_ID` is
+``NVIDIA``, ``CMAKE_<LANG>_HOST_COMPILER`` selects the compiler executable
+to use when compiling host code for ``CUDA`` or ``HIP`` language files.
+This maps to the ``nvcc -ccbin`` option.
+
+The ``CMAKE_<LANG>_HOST_COMPILER`` variable may be set explicitly before CUDA
+or HIP is first enabled by a :command:`project` or :command:`enable_language`
+command.
+This can be done via ``-DCMAKE_<LANG>_HOST_COMPILER=...`` on the command line
+or in a :ref:`toolchain file <Cross Compiling Toolchain>`.  Or, one may set
+the :envvar:`CUDAHOSTCXX` or :envvar:`HIPHOSTCXX` environment variable to
+provide a default value.
+
+Once the CUDA or HIP language is enabled, the ``CMAKE_<LANG>_HOST_COMPILER``
+variable is read-only and changes to it are undefined behavior.
+
+.. note::
+
+  Since ``CMAKE_<LANG>_HOST_COMPILER`` is meaningful only when the
+  :variable:`CMAKE_<LANG>_COMPILER_ID` is ``NVIDIA``,
+  it does not make sense to set ``CMAKE_<LANG>_HOST_COMPILER`` without also
+  setting ``CMAKE_<LANG>_COMPILER`` to NVCC.
+
+.. note::
+
+  Projects should not try to set ``CMAKE_<LANG>_HOST_COMPILER`` to match
+  :variable:`CMAKE_CXX_COMPILER <CMAKE_<LANG>_COMPILER>` themselves.
+  It is the end-user's responsibility, not the project's, to ensure that
+  NVCC targets the same ABI as the C++ compiler.
+
+.. note::
+
+  Ignored when using :ref:`Visual Studio Generators`.
diff --git a/Help/variable/CMAKE_MACOSX_BUNDLE.rst b/Help/variable/CMAKE_MACOSX_BUNDLE.rst
index 43ddff5..483c5b3 100644
--- a/Help/variable/CMAKE_MACOSX_BUNDLE.rst
+++ b/Help/variable/CMAKE_MACOSX_BUNDLE.rst
@@ -7,4 +7,4 @@
 all the targets.  See that target property for additional information.
 
 This variable is set to ``ON`` by default if :variable:`CMAKE_SYSTEM_NAME`
-equals to :ref:`iOS, tvOS or watchOS <Cross Compiling for iOS, tvOS, or watchOS>`.
+equals to :ref:`iOS, tvOS, visionOS or watchOS <Cross Compiling for iOS, tvOS, visionOS, or watchOS>`.
diff --git a/Help/variable/CMAKE_OSX_VARIABLE.txt b/Help/variable/CMAKE_OSX_VARIABLE.txt
index 5670980..962fcd3 100644
--- a/Help/variable/CMAKE_OSX_VARIABLE.txt
+++ b/Help/variable/CMAKE_OSX_VARIABLE.txt
@@ -7,6 +7,6 @@
 policy :policy:`CMP0126` is set to ``NEW``.
 
 Despite the ``OSX`` part in the variable name(s) they apply also to
-other SDKs than macOS like iOS, tvOS, or watchOS.
+other SDKs than macOS like iOS, tvOS, visionOS, or watchOS.
 
 This variable is ignored on platforms other than Apple.
diff --git a/Help/variable/CMAKE_SIZEOF_VOID_P.rst b/Help/variable/CMAKE_SIZEOF_VOID_P.rst
index f5464d1..0c1fd7a 100644
--- a/Help/variable/CMAKE_SIZEOF_VOID_P.rst
+++ b/Help/variable/CMAKE_SIZEOF_VOID_P.rst
@@ -4,5 +4,5 @@
 Size of a ``void`` pointer.
 
 This is set to the size of a pointer on the target machine, and is determined
-by a try compile.  If a 64-bit size is found, then the library search
-path is modified to look for 64-bit libraries first.
+when a compiled language is enabled.  If a 64-bit size is found, then the
+library search path is modified to look for 64-bit libraries first.
diff --git a/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst b/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst
index c8b5815..8dfcdc5 100644
--- a/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst
+++ b/Help/variable/CMAKE_SYSTEM_PREFIX_PATH.rst
@@ -38,6 +38,12 @@
 Unix:
   * ``ENV{CONDA_PREFIX}`` when using a conda compiler
 
+MSYSTEM environment with MinGW toolchain:
+  .. versionadded:: 3.28
+
+  * ``ENV{MSYSTEM_PREFIX}/local``
+  * ``ENV{MSYSTEM_PREFIX}``
+
 Windows:
   * ``ENV{ProgramW6432}``
   * ``ENV{ProgramFiles}``
diff --git a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
index 2bb97c4..155931f 100644
--- a/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
+++ b/Help/variable/CMAKE_VS_DEVENV_COMMAND.rst
@@ -10,5 +10,5 @@
 is installed on the computer.
 
 The :variable:`CMAKE_VS_MSBUILD_COMMAND` is also provided for
-:generator:`Visual Studio 11 2012` and above.
+:generator:`Visual Studio 12 2013` and above.
 See also the :variable:`CMAKE_MAKE_PROGRAM` variable.
diff --git a/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst b/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst
index 8a521a3..96924d5 100644
--- a/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst
+++ b/Help/variable/CMAKE_VS_MSBUILD_COMMAND.rst
@@ -1,7 +1,7 @@
 CMAKE_VS_MSBUILD_COMMAND
 ------------------------
 
-The generators for :generator:`Visual Studio 11 2012` and above set this
+The generators for :generator:`Visual Studio 12 2013` and above set this
 variable to the ``MSBuild.exe`` command installed with the corresponding
 Visual Studio version.
 
diff --git a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst
index d153061..f3c213c 100644
--- a/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst
+++ b/Help/variable/CMAKE_XCODE_BUILD_SYSTEM.rst
@@ -12,7 +12,8 @@
 
 ``1``
   The original Xcode build system.
-  This is the default when using Xcode 11.x or below.
+  This is the default when using Xcode 11.x or below and supported
+  up to Xcode 13.x.
 
 ``12``
   The Xcode "new build system" introduced by Xcode 10.
diff --git a/Help/variable/ENV.rst b/Help/variable/ENV.rst
index 6791853..71df3dd 100644
--- a/Help/variable/ENV.rst
+++ b/Help/variable/ENV.rst
@@ -8,6 +8,17 @@
 To test whether an environment variable is defined, use the signature
 ``if(DEFINED ENV{<name>})`` of the :command:`if` command.
 
+.. note::
+
+  Environment variable names containing special characters like parentheses
+  may need to be escaped.  (Policy :policy:`CMP0053` must also be enabled.)
+  For example, to get the value of the Windows environment variable
+  ``ProgramFiles(x86)``, use:
+
+  .. code-block:: cmake
+
+      set(ProgramFiles_x86 "$ENV{ProgramFiles\(x86\)}")
+
 For general information on environment variables, see the
 :ref:`Environment Variables <CMake Language Environment Variables>`
 section in the :manual:`cmake-language(7)` manual.
diff --git a/Modules/CMakeCUDACompilerABI.cu b/Modules/CMakeCUDACompilerABI.cu
index 8463e86..b04d0ec 100644
--- a/Modules/CMakeCUDACompilerABI.cu
+++ b/Modules/CMakeCUDACompilerABI.cu
@@ -2,11 +2,8 @@
 #  error "A C or C++ compiler has been selected for CUDA"
 #endif
 
-#include <cstdio>
-
-#include <cuda_runtime.h>
-
 #include "CMakeCompilerABI.h"
+#include "CMakeCompilerCUDAArch.h"
 
 int main(int argc, char* argv[])
 {
@@ -19,25 +16,7 @@
 #endif
   static_cast<void>(argv);
 
-  int count = 0;
-  if (cudaGetDeviceCount(&count) != cudaSuccess || count == 0) {
-    std::fprintf(stderr, "No CUDA devices found.\n");
-    return -1;
-  }
-
-  int found = 0;
-  const char* sep = "";
-  for (int device = 0; device < count; ++device) {
-    cudaDeviceProp prop;
-    if (cudaGetDeviceProperties(&prop, device) == cudaSuccess) {
-      std::printf("%s%d%d", sep, prop.major, prop.minor);
-      sep = ";";
-      found = 1;
-    }
-  }
-
-  if (!found) {
-    std::fprintf(stderr, "No CUDA architecture detected from any devices.\n");
+  if (!cmakeCompilerCUDAArch()) {
     // Convince the compiler that the non-zero return value depends
     // on the info strings so they are not optimized out.
     return require ? -1 : 1;
diff --git a/Modules/CMakeCompilerCUDAArch.h b/Modules/CMakeCompilerCUDAArch.h
new file mode 100644
index 0000000..dcb030f
--- /dev/null
+++ b/Modules/CMakeCompilerCUDAArch.h
@@ -0,0 +1,29 @@
+#include <cstdio>
+
+#include <cuda_runtime.h>
+
+static bool cmakeCompilerCUDAArch()
+{
+  int count = 0;
+  if (cudaGetDeviceCount(&count) != cudaSuccess || count == 0) {
+    std::fprintf(stderr, "No CUDA devices found.\n");
+    return -1;
+  }
+
+  bool found = false;
+  const char* sep = "";
+  for (int device = 0; device < count; ++device) {
+    cudaDeviceProp prop;
+    if (cudaGetDeviceProperties(&prop, device) == cudaSuccess) {
+      std::printf("%s%d%d", sep, prop.major, prop.minor);
+      sep = ";";
+      found = true;
+    }
+  }
+
+  if (!found) {
+    std::fprintf(stderr, "No CUDA architecture detected from any devices.\n");
+  }
+
+  return found;
+}
diff --git a/Modules/CMakeCompilerIdDetection.cmake b/Modules/CMakeCompilerIdDetection.cmake
index 75d33e8..7eb93e2 100644
--- a/Modules/CMakeCompilerIdDetection.cmake
+++ b/Modules/CMakeCompilerIdDetection.cmake
@@ -65,12 +65,14 @@
       VisualAge
       NVHPC
       PGI
+      CrayClang
       Cray
       TI
       FujitsuClang
       Fujitsu
       GHS
       Tasking
+      OrangeC
     )
     if ("x${lang}" STREQUAL "xC")
       list(APPEND ordered_compilers
@@ -102,6 +104,10 @@
       set(ordered_compilers NVIDIA Clang)
     endif()
 
+    if("x${lang}" STREQUAL "xHIP")
+      set(ordered_compilers NVIDIA Clang)
+    endif()
+
     if(CID_ID_DEFINE)
       foreach(Id ${ordered_compilers})
         string(APPEND CMAKE_${lang}_COMPILER_ID_CONTENT "# define ${CID_PREFIX}COMPILER_IS_${Id} 0\n")
diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake
index 8ec6c66..6d7d17e 100644
--- a/Modules/CMakeDetermineASMCompiler.cmake
+++ b/Modules/CMakeDetermineASMCompiler.cmake
@@ -70,6 +70,10 @@
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_ARMClang "--version")
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_ARMClang "armclang")
 
+  list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS OrangeC )
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_OrangeC "--version")
+  set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_OrangeC "occ \\(OrangeC\\) Version")
+
   list(APPEND CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDORS HP )
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_FLAGS_HP "-V")
   set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_VENDOR_REGEX_HP "HP C")
diff --git a/Modules/CMakeDetermineCSharpCompiler.cmake b/Modules/CMakeDetermineCSharpCompiler.cmake
index fe98469..652eb63 100644
--- a/Modules/CMakeDetermineCSharpCompiler.cmake
+++ b/Modules/CMakeDetermineCSharpCompiler.cmake
@@ -3,7 +3,7 @@
 
 if(NOT ${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])")
   message(FATAL_ERROR
-    "C# is currently only supported for Microsoft Visual Studio 11 2012 and later.")
+    "C# is currently only supported for Microsoft Visual Studio 12 2013 and later.")
 endif()
 
 include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
diff --git a/Modules/CMakeDetermineCUDACompiler.cmake b/Modules/CMakeDetermineCUDACompiler.cmake
index 53c5f78..585b2aa 100644
--- a/Modules/CMakeDetermineCUDACompiler.cmake
+++ b/Modules/CMakeDetermineCUDACompiler.cmake
@@ -4,13 +4,13 @@
 include(${CMAKE_ROOT}/Modules/CMakeDetermineCompiler.cmake)
 include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake)
 
-if( NOT ( ("${CMAKE_GENERATOR}" MATCHES "Make") OR
-          ("${CMAKE_GENERATOR}" MATCHES "Ninja") OR
-          ("${CMAKE_GENERATOR}" MATCHES "Visual Studio (1|[9][0-9])") ) )
+if(NOT ((CMAKE_GENERATOR MATCHES "Make") OR
+        (CMAKE_GENERATOR MATCHES "Ninja") OR
+        (CMAKE_GENERATOR MATCHES "Visual Studio (1|[9][0-9])")))
   message(FATAL_ERROR "CUDA language not currently supported by \"${CMAKE_GENERATOR}\" generator")
 endif()
 
-if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
+if(CMAKE_GENERATOR MATCHES "Visual Studio")
   if(DEFINED ENV{CUDAHOSTCXX} OR DEFINED CMAKE_CUDA_HOST_COMPILER)
     message(WARNING "Visual Studio does not support specifying CUDAHOSTCXX or CMAKE_CUDA_HOST_COMPILER. Using the C++ compiler provided by Visual Studio.")
   endif()
@@ -62,7 +62,7 @@
 
   include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake)
 
-  if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
+  if(CMAKE_GENERATOR MATCHES "Visual Studio")
     # We will not know CMAKE_CUDA_COMPILER until the main compiler id step
     # below extracts it, but we do know that the compiler id will be NVIDIA.
     set(CMAKE_CUDA_COMPILER_ID "NVIDIA")
@@ -78,176 +78,18 @@
       message(FATAL_ERROR "Clang with CUDA is not yet supported on Windows. See CMake issue #20776.")
     endif()
 
-    # Find the CUDA toolkit. We store the CMAKE_CUDA_COMPILER_TOOLKIT_ROOT, CMAKE_CUDA_COMPILER_TOOLKIT_VERSION and
-    # CMAKE_CUDA_COMPILER_LIBRARY_ROOT in CMakeCUDACompiler.cmake so FindCUDAToolkit can avoid searching on future
-    # runs and the toolkit is the same.
-    # This is very similar to FindCUDAToolkit, but somewhat simplified since we can issue fatal errors
-    # if we fail and we don't need to account for searching the libraries.
-
-    # For NVCC we can easily deduce the SDK binary directory from the compiler path.
-    if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
-      set(_CUDA_NVCC_EXECUTABLE "${CMAKE_CUDA_COMPILER}")
-    else()
-      # Search using CUDAToolkit_ROOT and then CUDA_PATH for equivalence with FindCUDAToolkit.
-      # In FindCUDAToolkit CUDAToolkit_ROOT is searched automatically due to being in a find_package().
-      # First we search candidate non-default paths to give them priority.
-      find_program(_CUDA_NVCC_EXECUTABLE
-        NAMES nvcc nvcc.exe
-        PATHS ${CUDAToolkit_ROOT}
-        ENV CUDAToolkit_ROOT
-        ENV CUDA_PATH
-        PATH_SUFFIXES bin
-        NO_DEFAULT_PATH
-      )
-
-      # If we didn't find NVCC, then try the default paths.
-      find_program(_CUDA_NVCC_EXECUTABLE
-        NAMES nvcc nvcc.exe
-        PATH_SUFFIXES bin
-      )
-
-      # If the user specified CUDAToolkit_ROOT but nvcc could not be found, this is an error.
-      if(NOT _CUDA_NVCC_EXECUTABLE AND (DEFINED CUDAToolkit_ROOT OR DEFINED ENV{CUDAToolkit_ROOT}))
-        set(fail_base "Could not find nvcc executable in path specified by")
-
-        if(DEFINED CUDAToolkit_ROOT)
-          message(FATAL_ERROR "${fail_base} CUDAToolkit_ROOT=${CUDAToolkit_ROOT}")
-        elseif(DEFINED ENV{CUDAToolkit_ROOT})
-          message(FATAL_ERROR "${fail_base} environment variable CUDAToolkit_ROOT=$ENV{CUDAToolkit_ROOT}")
-        endif()
-      endif()
-
-      # CUDAToolkit_ROOT cmake/env variable not specified, try platform defaults.
-      #
-      # - Linux: /usr/local/cuda-X.Y
-      # - macOS: /Developer/NVIDIA/CUDA-X.Y
-      # - Windows: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\vX.Y
-      #
-      # We will also search the default symlink location /usr/local/cuda first since
-      # if CUDAToolkit_ROOT is not specified, it is assumed that the symlinked
-      # directory is the desired location.
-      if(NOT _CUDA_NVCC_EXECUTABLE)
-        if(UNIX)
-          if(NOT APPLE)
-            set(platform_base "/usr/local/cuda-")
-          else()
-            set(platform_base "/Developer/NVIDIA/CUDA-")
-          endif()
-        else()
-          set(platform_base "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v")
-        endif()
-
-        # Build out a descending list of possible cuda installations, e.g.
-        file(GLOB possible_paths "${platform_base}*")
-        # Iterate the glob results and create a descending list.
-        set(versions)
-        foreach(p ${possible_paths})
-          # Extract version number from end of string
-          string(REGEX MATCH "[0-9][0-9]?\\.[0-9]$" p_version ${p})
-          if(IS_DIRECTORY ${p} AND p_version)
-            list(APPEND versions ${p_version})
-          endif()
-        endforeach()
-
-        # Sort numerically in descending order, so we try the newest versions first.
-        list(SORT versions COMPARE NATURAL ORDER DESCENDING)
-
-        # With a descending list of versions, populate possible paths to search.
-        set(search_paths)
-        foreach(v ${versions})
-          list(APPEND search_paths "${platform_base}${v}")
-        endforeach()
-
-        # Force the global default /usr/local/cuda to the front on Unix.
-        if(UNIX)
-          list(INSERT search_paths 0 "/usr/local/cuda")
-        endif()
-
-        # Now search for nvcc again using the platform default search paths.
-        find_program(_CUDA_NVCC_EXECUTABLE
-          NAMES nvcc nvcc.exe
-          PATHS ${search_paths}
-          PATH_SUFFIXES bin
-        )
-
-        # We are done with these variables now, cleanup.
-        unset(platform_base)
-        unset(possible_paths)
-        unset(versions)
-        unset(search_paths)
-
-        if(NOT _CUDA_NVCC_EXECUTABLE)
-          message(FATAL_ERROR "Failed to find nvcc.\nCompiler ${CMAKE_CUDA_COMPILER_ID} requires the CUDA toolkit. Please set the CUDAToolkit_ROOT variable.")
-        endif()
-      endif()
-    endif()
-
-    # Given that NVCC can be provided by multiple different sources (NVIDIA HPC SDK, CUDA Toolkit, distro)
-    # each of which has a different layout, we need to extract the CUDA toolkit root from the compiler
-    # itself, allowing us to support numerous different scattered toolkit layouts
-    execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda"
-      OUTPUT_VARIABLE _CUDA_NVCC_OUT ERROR_VARIABLE _CUDA_NVCC_OUT)
-    if(_CUDA_NVCC_OUT MATCHES "\\#\\$ TOP=([^\r\n]*)")
-      get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_MATCH_1}" ABSOLUTE)
-    else()
-      get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY)
-      get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY)
-    endif()
-
-    if(_CUDA_NVCC_OUT MATCHES "\\#\\$ NVVMIR_LIBRARY_DIR=([^\r\n]*)")
-      get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${CMAKE_MATCH_1}" ABSOLUTE)
-
-      #We require the path to end in `/nvvm/libdevice'
-      if(_CUDA_NVVMIR_LIBRARY_DIR MATCHES "nvvm/libdevice$")
-        get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}/../.." ABSOLUTE)
-        set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}")
-      endif()
-
-      unset(_CUDA_NVVMIR_LIBRARY_DIR)
-      unset(_cuda_nvvmir_dir_name)
-    endif()
-    unset(_CUDA_NVCC_OUT)
+    # Find the CUDA toolkit to get:
+    # - CMAKE_CUDA_COMPILER_TOOLKIT_VERSION
+    # - CMAKE_CUDA_COMPILER_TOOLKIT_ROOT
+    # - CMAKE_CUDA_COMPILER_LIBRARY_ROOT
+    # We save them in CMakeCUDACompiler.cmake so FindCUDAToolkit can
+    # avoid searching on future runs and the toolkit is the same.
+    # Match arguments with cmake_cuda_architectures_all call.
+    include(Internal/CMakeCUDAFindToolkit)
+    cmake_cuda_find_toolkit(CUDA CMAKE_CUDA_COMPILER_)
 
     set(CMAKE_CUDA_DEVICE_LINKER "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/bin/nvlink${CMAKE_EXECUTABLE_SUFFIX}")
     set(CMAKE_CUDA_FATBINARY "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/bin/fatbinary${CMAKE_EXECUTABLE_SUFFIX}")
-
-    # In a non-scattered installation the following are equivalent to CMAKE_CUDA_COMPILER_TOOLKIT_ROOT.
-    # We first check for a non-scattered installation to prefer it over a scattered installation.
-
-    # CMAKE_CUDA_COMPILER_LIBRARY_ROOT contains the device library.
-    if(DEFINED CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR)
-      set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR}")
-    elseif(EXISTS "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}/nvvm/libdevice")
-      set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}")
-    elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/nvvm/libdevice")
-      set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/cuda")
-    elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/cuda/nvvm/libdevice")
-      set(CMAKE_CUDA_COMPILER_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/cuda")
-    else()
-      message(FATAL_ERROR "Couldn't find CUDA library root.")
-    endif()
-    unset(CMAKE_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR)
-
-    # CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT contains the linking stubs necessary for device linking and other low-level library files.
-    if(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub")
-      set(CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit")
-    elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub")
-      set(CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit")
-    else()
-      set(CMAKE_CUDA_COMPILER_TOOLKIT_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}")
-    endif()
-  endif()
-
-  # For regular nvcc we the toolkit version is the same as the compiler version and we can parse it from the vendor test output.
-  # For Clang we need to invoke nvcc to get version output.
-  if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
-    if(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")
-      execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE CMAKE_CUDA_COMPILER_ID_OUTPUT)
-    endif()
-
-    if(CMAKE_CUDA_COMPILER_ID_OUTPUT MATCHES [=[V([0-9]+\.[0-9]+\.[0-9]+)]=])
-      set(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION "${CMAKE_MATCH_1}")
-    endif()
   endif()
 
   set(CMAKE_CUDA_COMPILER_ID_FLAGS_ALWAYS "-v")
@@ -269,23 +111,9 @@
     endif()
   endif()
 
-  # Rest of the code treats an empty value as equivalent to "use the defaults".
-  # Error out early to prevent confusing errors as a result of this.
-  # Note that this also catches invalid non-numerical values such as "a".
-  if(DEFINED CMAKE_CUDA_ARCHITECTURES)
-    if(CMAKE_CUDA_ARCHITECTURES STREQUAL "")
-      message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES must be non-empty if set.")
-    elseif(CMAKE_CUDA_ARCHITECTURES AND NOT CMAKE_CUDA_ARCHITECTURES MATCHES "^([0-9]+a?(-real|-virtual)?(;[0-9]+a?(-real|-virtual)?|;)*|all|all-major|native)$")
-      message(FATAL_ERROR
-        "CMAKE_CUDA_ARCHITECTURES:\n"
-        "  ${CMAKE_CUDA_ARCHITECTURES}\n"
-        "is not one of the following:\n"
-        "  * a semicolon-separated list of integers, each optionally\n"
-        "    followed by '-real' or '-virtual'\n"
-        "  * a special value: all, all-major, native\n"
-        )
-    endif()
-  endif()
+  # If the user set CMAKE_CUDA_ARCHITECTURES, validate its value.
+  include(Internal/CMakeCUDAArchitecturesValidate)
+  cmake_cuda_architectures_validate(CUDA)
 
   if(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")
     # Clang doesn't automatically select an architecture supported by the SDK.
@@ -306,7 +134,7 @@
 
   CMAKE_DETERMINE_COMPILER_ID(CUDA CUDAFLAGS CMakeCUDACompilerId.cu)
 
-  if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
+  if(CMAKE_GENERATOR MATCHES "Visual Studio")
     # Now that we have the path to nvcc, we can compute the toolkit root.
     get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER}" DIRECTORY)
     get_filename_component(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}" DIRECTORY)
@@ -316,7 +144,12 @@
     set(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION ${CMAKE_CUDA_COMPILER_VERSION})
   endif()
 
-  include(${CMAKE_ROOT}/Modules/CUDA/architectures.cmake)
+  include(Internal/CMakeCUDAArchitecturesAll)
+  # From CMAKE_CUDA_COMPILER_TOOLKIT_VERSION and CMAKE_CUDA_COMPILER_{ID,VERSION}, get:
+  # - CMAKE_CUDA_ARCHITECTURES_ALL
+  # - CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR
+  # Match arguments with cmake_cuda_find_toolkit call.
+  cmake_cuda_architectures_all(CUDA CMAKE_CUDA_COMPILER_)
 
   _cmake_find_compiler_sysroot(CUDA)
 endif()
@@ -331,7 +164,7 @@
     "set(MSVC_CUDA_ARCHITECTURE_ID ${MSVC_CUDA_ARCHITECTURE_ID})")
 endif()
 
-if(${CMAKE_GENERATOR} MATCHES "Visual Studio")
+if(CMAKE_GENERATOR MATCHES "Visual Studio")
   set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${CMAKE_LINKER}")
   set(CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES "")
   set(CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES "")
@@ -351,11 +184,12 @@
   set(_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT
     "set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT \"${CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT}\")")
 elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "Clang")
-  string(REGEX MATCHALL "-target-cpu sm_([0-9]+)" target_cpus "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}")
+  string(REGEX MATCHALL "-target-cpu sm_([0-9]+)" _clang_target_cpus "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}")
 
-  foreach(cpu ${target_cpus})
-    string(REGEX MATCH "-target-cpu sm_([0-9]+)" dont_care "${cpu}")
-    list(APPEND architectures_detected "${CMAKE_MATCH_1}")
+  foreach(_clang_target_cpu ${_clang_target_cpus})
+    if(_clang_target_cpu MATCHES "-target-cpu sm_([0-9]+)")
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_DEFAULT "${CMAKE_MATCH_1}")
+    endif()
   endforeach()
 
   # Find target directory when crosscompiling.
@@ -411,141 +245,32 @@
   set(CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES "${_CUDA_LIBRARY_DIR}")
   set(CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES "")
   set(CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")
+
+  # Don't leak variables unnecessarily to user code.
+  unset(_CUDA_INCLUDE_DIR)
+  unset(_CUDA_LIBRARY_DIR)
+  unset(_CUDA_TARGET_DIR)
+  unset(_CUDA_TARGET_NAME)
 elseif(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
-  set(_nvcc_log "")
-  string(REPLACE "\r" "" _nvcc_output_orig "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}")
-  if(_nvcc_output_orig MATCHES "#\\\$ +PATH= *([^\n]*)\n")
-    set(_nvcc_path "${CMAKE_MATCH_1}")
-    string(APPEND _nvcc_log "  found 'PATH=' string: [${_nvcc_path}]\n")
-    string(REPLACE ":" ";" _nvcc_path "${_nvcc_path}")
-  else()
-    set(_nvcc_path "")
-    string(REPLACE "\n" "\n    " _nvcc_output_log "\n${_nvcc_output_orig}")
-    string(APPEND _nvcc_log "  no 'PATH=' string found in nvcc output:${_nvcc_output_log}\n")
-  endif()
-  if(_nvcc_output_orig MATCHES "#\\\$ +LIBRARIES= *([^\n]*)\n")
-    set(_nvcc_libraries "${CMAKE_MATCH_1}")
-    string(APPEND _nvcc_log "  found 'LIBRARIES=' string: [${_nvcc_libraries}]\n")
-  else()
-    set(_nvcc_libraries "")
-    string(REPLACE "\n" "\n    " _nvcc_output_log "\n${_nvcc_output_orig}")
-    string(APPEND _nvcc_log "  no 'LIBRARIES=' string found in nvcc output:${_nvcc_output_log}\n")
-  endif()
+  include(Internal/CMakeNVCCParseImplicitInfo)
+  # Parse CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT to get:
+  # - CMAKE_CUDA_ARCHITECTURES_DEFAULT
+  # - CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES
+  # - CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES
+  # - CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES
+  # - CMAKE_CUDA_HOST_LINK_LAUNCHER
+  # - CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT
+  # - CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES
+  # Match arguments with cmake_nvcc_filter_implicit_info call in CMakeTestCUDACompiler.
+  cmake_nvcc_parse_implicit_info(CUDA CMAKE_CUDA_)
 
-  set(_nvcc_link_line "")
-  if(_nvcc_libraries)
-    # Remove variable assignments.
-    string(REGEX REPLACE "#\\\$ *[^= ]+=[^\n]*\n" "" _nvcc_output "${_nvcc_output_orig}")
-    # Encode [] characters that break list expansion.
-    string(REPLACE "[" "{==={" _nvcc_output "${_nvcc_output}")
-    string(REPLACE "]" "}===}" _nvcc_output "${_nvcc_output}")
-    # Split lines.
-    string(REGEX REPLACE "\n+(#\\\$ )?" ";" _nvcc_output "${_nvcc_output}")
-    foreach(line IN LISTS _nvcc_output)
-      set(_nvcc_output_line "${line}")
-      string(REPLACE "{==={" "[" _nvcc_output_line "${_nvcc_output_line}")
-      string(REPLACE "}===}" "]" _nvcc_output_line "${_nvcc_output_line}")
-      string(APPEND _nvcc_log "  considering line: [${_nvcc_output_line}]\n")
-      if("${_nvcc_output_line}" MATCHES "^ *nvlink")
-        string(APPEND _nvcc_log "    ignoring nvlink line\n")
-      elseif(_nvcc_libraries)
-        if("${_nvcc_output_line}" MATCHES "(@\"?((tmp/)?a\\.exe\\.res)\"?)")
-          set(_nvcc_link_res_arg "${CMAKE_MATCH_1}")
-          set(_nvcc_link_res_file "${CMAKE_MATCH_2}")
-          set(_nvcc_link_res "${CMAKE_PLATFORM_INFO_DIR}/CompilerIdCUDA/${_nvcc_link_res_file}")
-          if(EXISTS "${_nvcc_link_res}")
-            file(READ "${_nvcc_link_res}" _nvcc_link_res_content)
-            string(REPLACE "${_nvcc_link_res_arg}" "${_nvcc_link_res_content}" _nvcc_output_line "${_nvcc_output_line}")
-          endif()
-        endif()
-        string(FIND "${_nvcc_output_line}" "${_nvcc_libraries}" _nvcc_libraries_pos)
-        if(NOT _nvcc_libraries_pos EQUAL -1)
-          set(_nvcc_link_line "${_nvcc_output_line}")
-          string(APPEND _nvcc_log "    extracted link line: [${_nvcc_link_line}]\n")
-        endif()
-      endif()
-    endforeach()
-  endif()
-
-  if(_nvcc_link_line)
-    if("x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC")
-      set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${CMAKE_LINKER}")
-    else()
-      #extract the compiler that is being used for linking
-      separate_arguments(_nvcc_link_line_args UNIX_COMMAND "${_nvcc_link_line}")
-      list(GET _nvcc_link_line_args 0 _nvcc_host_link_launcher)
-      if(IS_ABSOLUTE "${_nvcc_host_link_launcher}")
-        string(APPEND _nvcc_log "  extracted link launcher absolute path: [${_nvcc_host_link_launcher}]\n")
-        set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}")
-      else()
-        string(APPEND _nvcc_log "  extracted link launcher name: [${_nvcc_host_link_launcher}]\n")
-        find_program(_nvcc_find_host_link_launcher
-          NAMES ${_nvcc_host_link_launcher}
-          PATHS ${_nvcc_path} NO_DEFAULT_PATH)
-        find_program(_nvcc_find_host_link_launcher
-          NAMES ${_nvcc_host_link_launcher})
-        if(_nvcc_find_host_link_launcher)
-          string(APPEND _nvcc_log "  found link launcher absolute path: [${_nvcc_find_host_link_launcher}]\n")
-          set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${_nvcc_find_host_link_launcher}")
-        else()
-          string(APPEND _nvcc_log "  could not find link launcher absolute path\n")
-          set(CMAKE_CUDA_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}")
-        endif()
-        unset(_nvcc_find_host_link_launcher CACHE)
-      endif()
-    endif()
-
-    #prefix the line with cuda-fake-ld so that implicit link info believes it is
-    #a link line
-    set(_nvcc_link_line "cuda-fake-ld ${_nvcc_link_line}")
-    CMAKE_PARSE_IMPLICIT_LINK_INFO("${_nvcc_link_line}"
-                                   CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES
-                                   CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES
-                                   CMAKE_CUDA_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES
-                                   log
-                                   "${CMAKE_CUDA_IMPLICIT_OBJECT_REGEX}"
-                                   LANGUAGE CUDA)
-
-    # Detect CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT from the compiler by looking at which
-    # cudart library exists in the implicit link libraries passed to the host linker.
-    # This is required when a project sets the cuda runtime library as part of the
-    # initial flags.
-    if(";${CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart_static(\.lib)?;]])
-      set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "STATIC")
-    elseif(";${CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart(\.lib)?;]])
-      set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "SHARED")
-    else()
-      set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT "NONE")
-    endif()
-    set(_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT
-      "set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT \"${CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT}\")")
-
-    message(CONFIGURE_LOG
-      "Parsed CUDA nvcc implicit link information:\n${_nvcc_log}\n${log}\n\n")
-  else()
-    message(CONFIGURE_LOG
-      "Failed to parse CUDA nvcc implicit link information:\n${_nvcc_log}\n\n")
-    message(FATAL_ERROR "Failed to extract nvcc implicit link line.")
-  endif()
+  set(_SET_CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT
+    "set(CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT \"${CMAKE_CUDA_RUNTIME_LIBRARY_DEFAULT}\")")
 endif()
 
-# CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES is detected above as the list of
-# libraries that the CUDA compiler implicitly passes to the host linker.
-# CMake invokes the host linker directly and so needs to pass these libraries.
-# We filter out those that should not be passed unconditionally both here
-# and from CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES in CMakeTestCUDACompiler.
-set(CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE
-  # The CUDA runtime libraries are controlled by CMAKE_CUDA_RUNTIME_LIBRARY.
-  cudart        cudart.lib
-  cudart_static cudart_static.lib
-  cudadevrt     cudadevrt.lib
-
-  # Dependencies of the CUDA static runtime library on Linux hosts.
-  rt
-  pthread
-  dl
-  )
-list(REMOVE_ITEM CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES ${CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE})
+include(Internal/CMakeCUDAFilterImplicitLibs)
+# Filter out implicit link libraries that should not be passed unconditionally.
+cmake_cuda_filter_implicit_libs(CMAKE_CUDA_HOST_IMPLICIT_LINK_LIBRARIES)
 
 if(CMAKE_CUDA_COMPILER_SYSROOT)
   string(CONCAT _SET_CMAKE_CUDA_COMPILER_SYSROOT
@@ -555,55 +280,17 @@
   set(_SET_CMAKE_CUDA_COMPILER_SYSROOT "")
 endif()
 
-# Determine CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES
-if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
-  set(CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES)
-  string(REPLACE "\r" "" _nvcc_output_orig "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}")
-  if(_nvcc_output_orig MATCHES "#\\\$ +INCLUDES= *([^\n]*)\n")
-    set(_nvcc_includes "${CMAKE_MATCH_1}")
-    string(APPEND _nvcc_log "  found 'INCLUDES=' string: [${_nvcc_includes}]\n")
-  else()
-    set(_nvcc_includes "")
-    string(REPLACE "\n" "\n    " _nvcc_output_log "\n${_nvcc_output_orig}")
-    string(APPEND _nvcc_log "  no 'INCLUDES=' string found in nvcc output:${_nvcc_output_log}\n")
-  endif()
-  if(_nvcc_includes)
-    # across all operating system each include directory is prefixed with -I
-    separate_arguments(_nvcc_output NATIVE_COMMAND "${_nvcc_includes}")
-    foreach(line IN LISTS _nvcc_output)
-      string(REGEX REPLACE "^-I" "" line "${line}")
-      get_filename_component(line "${line}" ABSOLUTE)
-      list(APPEND CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES "${line}")
-    endforeach()
-
-    message(CONFIGURE_LOG
-      "Parsed CUDA nvcc include information:\n${_nvcc_log}\n${log}\n\n")
-  else()
-    message(CONFIGURE_LOG
-      "Failed to detect CUDA nvcc include information:\n${_nvcc_log}\n\n")
-  endif()
-
-  string(REGEX MATCHALL "-arch compute_([0-9]+)" target_cpus "${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}")
-
-  foreach(cpu ${target_cpus})
-    string(REGEX MATCH "-arch compute_([0-9]+)" dont_care "${cpu}")
-    list(APPEND architectures_detected "${CMAKE_MATCH_1}")
-  endforeach()
-endif()
-
-# If the user didn't set the architectures, then set them to a default.
-# If the user did, then make sure those architectures worked.
+# If the user did not set CMAKE_CUDA_ARCHITECTURES, use the compiler's default.
 if("${CMAKE_CUDA_ARCHITECTURES}" STREQUAL "")
   cmake_policy(GET CMP0104 _CUDA_CMP0104)
-
   if(NOT CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA" OR _CUDA_CMP0104 STREQUAL "NEW")
-    set(CMAKE_CUDA_ARCHITECTURES "${architectures_detected}" CACHE STRING "CUDA architectures")
-
+    set(CMAKE_CUDA_ARCHITECTURES "${CMAKE_CUDA_ARCHITECTURES_DEFAULT}" CACHE STRING "CUDA architectures")
     if(NOT CMAKE_CUDA_ARCHITECTURES)
       message(FATAL_ERROR "Failed to detect a default CUDA architecture.\n\nCompiler output:\n${CMAKE_CUDA_COMPILER_PRODUCED_OUTPUT}")
     endif()
   endif()
 endif()
+unset(CMAKE_CUDA_ARCHITECTURES_DEFAULT)
 
 # configure all variables set in this file
 configure_file(${CMAKE_ROOT}/Modules/CMakeCUDACompiler.cmake.in
@@ -611,14 +298,5 @@
   @ONLY
 )
 
-# Don't leak variables unnecessarily to user code.
-unset(_CUDA_INCLUDE_DIR CACHE)
-unset(_CUDA_NVCC_EXECUTABLE CACHE)
-unset(_CUDA_LIBRARY_DIR)
-unset(_CUDA_TARGET_DIR)
-unset(_CUDA_TARGET_NAME)
-
-unset(architectures_detected)
-
 set(CMAKE_CUDA_COMPILER_ENV_VAR "CUDACXX")
 set(CMAKE_CUDA_HOST_COMPILER_ENV_VAR "CUDAHOSTCXX")
diff --git a/Modules/CMakeDetermineCompilerABI.cmake b/Modules/CMakeDetermineCompilerABI.cmake
index 13bfeec..efc18f9 100644
--- a/Modules/CMakeDetermineCompilerABI.cmake
+++ b/Modules/CMakeDetermineCompilerABI.cmake
@@ -26,13 +26,13 @@
     if(DEFINED CMAKE_${lang}_VERBOSE_COMPILE_FLAG)
       set(COMPILE_DEFINITIONS "${CMAKE_${lang}_VERBOSE_COMPILE_FLAG}")
     endif()
-    if(lang STREQUAL "CUDA")
-      if(CMAKE_CUDA_ARCHITECTURES STREQUAL "native")
+    if(lang MATCHES "^(CUDA|HIP)$")
+      if(CMAKE_${lang}_ARCHITECTURES STREQUAL "native")
         # We are about to detect the native architectures, so we do
         # not yet know them.  Use all architectures during detection.
-        set(CMAKE_CUDA_ARCHITECTURES "all")
+        set(CMAKE_${lang}_ARCHITECTURES "all")
       endif()
-      set(CMAKE_CUDA_RUNTIME_LIBRARY "Static")
+      set(CMAKE_${lang}_RUNTIME_LIBRARY "Static")
     endif()
     if(NOT "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xMSVC")
       # Avoid adding our own platform standard libraries for compilers
@@ -139,38 +139,46 @@
       endif()
       set(CMAKE_${lang}_IMPLICIT_INCLUDE_DIRECTORIES "${_CMAKE_${lang}_IMPLICIT_INCLUDE_DIRECTORIES_INIT}" PARENT_SCOPE)
 
-      # Parse implicit linker information for this language, if available.
-      set(implicit_dirs "")
-      set(implicit_objs "")
-      set(implicit_libs "")
-      set(implicit_fwks "")
-      if(CMAKE_${lang}_VERBOSE_FLAG)
-        CMAKE_PARSE_IMPLICIT_LINK_INFO("${OUTPUT}" implicit_libs implicit_dirs implicit_fwks log
-          "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
-          COMPUTE_IMPLICIT_OBJECTS implicit_objs
-          LANGUAGE ${lang})
-        message(CONFIGURE_LOG
-          "Parsed ${lang} implicit link information:\n${log}\n\n")
-      endif()
-      # for VS IDE Intel Fortran we have to figure out the
-      # implicit link path for the fortran run time using
-      # a try-compile
-      if("${lang}" MATCHES "Fortran"
-          AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
-        message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path")
-        # Build a sample project which reports symbols.
-        try_compile(IFORT_LIB_PATH_COMPILED
-          PROJECT IntelFortranImplicit
-          SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath
-          BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath
-          CMAKE_FLAGS
-          "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
-          OUTPUT_VARIABLE _output)
-        file(WRITE
-          "${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.txt"
-          "${_output}")
-        include(${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.cmake OPTIONAL)
-        message(CHECK_PASS "done")
+      if(_CMAKE_${lang}_IMPLICIT_LINK_INFORMATION_DETERMINED_EARLY)
+        # Use implicit linker information detected during compiler id step.
+        set(implicit_dirs "${CMAKE_${lang}_IMPLICIT_LINK_DIRECTORIES}")
+        set(implicit_objs "")
+        set(implicit_libs "${CMAKE_${lang}_IMPLICIT_LINK_LIBRARIES}")
+        set(implicit_fwks "${CMAKE_${lang}_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES}")
+      else()
+        # Parse implicit linker information for this language, if available.
+        set(implicit_dirs "")
+        set(implicit_objs "")
+        set(implicit_libs "")
+        set(implicit_fwks "")
+        if(CMAKE_${lang}_VERBOSE_FLAG)
+          CMAKE_PARSE_IMPLICIT_LINK_INFO("${OUTPUT}" implicit_libs implicit_dirs implicit_fwks log
+            "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
+            COMPUTE_IMPLICIT_OBJECTS implicit_objs
+            LANGUAGE ${lang})
+          message(CONFIGURE_LOG
+            "Parsed ${lang} implicit link information:\n${log}\n\n")
+        endif()
+        # for VS IDE Intel Fortran we have to figure out the
+        # implicit link path for the fortran run time using
+        # a try-compile
+        if("${lang}" MATCHES "Fortran"
+            AND "${CMAKE_GENERATOR}" MATCHES "Visual Studio")
+          message(CHECK_START "Determine Intel Fortran Compiler Implicit Link Path")
+          # Build a sample project which reports symbols.
+          try_compile(IFORT_LIB_PATH_COMPILED
+            PROJECT IntelFortranImplicit
+            SOURCE_DIR ${CMAKE_ROOT}/Modules/IntelVSImplicitPath
+            BINARY_DIR ${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath
+            CMAKE_FLAGS
+            "-DCMAKE_Fortran_FLAGS:STRING=${CMAKE_Fortran_FLAGS}"
+            OUTPUT_VARIABLE _output)
+          file(WRITE
+            "${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.txt"
+            "${_output}")
+          include(${CMAKE_BINARY_DIR}/CMakeFiles/IntelVSImplicitPath/output.cmake OPTIONAL)
+          message(CHECK_PASS "done")
+        endif()
       endif()
 
       # Implicit link libraries cannot be used explicitly for multiple
diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake
index 2cf9853..4f1eaba 100644
--- a/Modules/CMakeDetermineCompilerId.cmake
+++ b/Modules/CMakeDetermineCompilerId.cmake
@@ -174,6 +174,32 @@
     endif()
   endif()
 
+  # FIXME(LLVMFlang): It does not provide predefines identifying the MSVC ABI or architecture.
+  # It should be taught to define _MSC_VER and its _M_* architecture flags.
+  if("x${lang}" STREQUAL "xFortran" AND "x${CMAKE_${lang}_COMPILER_ID}" STREQUAL "xLLVMFlang")
+    # Parse the target triple to detect information we should later be able
+    # to get during preprocessing above, once LLVMFlang provides it.
+    if(COMPILER_${lang}_PRODUCED_OUTPUT MATCHES "-triple ([0-9a-z_]*)-.*windows-msvc([0-9]+)\\.([0-9]+)")
+      set(CMAKE_${lang}_SIMULATE_ID "MSVC")
+      set(CMAKE_${lang}_SIMULATE_VERSION "${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
+      set(arch ${CMAKE_MATCH_1})
+      if(arch STREQUAL "x86_64")
+        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "x64")
+      elseif(arch STREQUAL "aarch64")
+        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64")
+      elseif(arch STREQUAL "arm64ec")
+        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "ARM64EC")
+      elseif(arch MATCHES "^i[3-9]86$")
+        set(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID "X86")
+      else()
+        message(FATAL_ERROR "LLVMFlang target architecture unrecognized: ${arch}")
+      endif()
+      set(MSVC_${lang}_ARCHITECTURE_ID "${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}")
+    elseif(COMPILER_${lang}_PRODUCED_OUTPUT MATCHES "-triple ([0-9a-z_]*)-.*windows-gnu")
+      set(CMAKE_${lang}_SIMULATE_ID "GNU")
+    endif()
+  endif()
+
   if (COMPILER_QNXNTO AND (CMAKE_${lang}_COMPILER_ID STREQUAL "GNU" OR CMAKE_${lang}_COMPILER_ID STREQUAL "LCC"))
     execute_process(
       COMMAND "${CMAKE_${lang}_COMPILER}"
@@ -605,6 +631,7 @@
     if(CMAKE_OSX_SYSROOT)
       set(id_sdkroot "SDKROOT = \"${CMAKE_OSX_SYSROOT}\";")
       if(CMAKE_OSX_SYSROOT MATCHES "(^|/)[Ii][Pp][Hh][Oo][Nn][Ee]" OR
+        CMAKE_OSX_SYSROOT MATCHES "(^|/)[Xx][Rr]" OR
         CMAKE_OSX_SYSROOT MATCHES "(^|/)[Aa][Pp][Pp][Ll][Ee][Tt][Vv]")
         set(id_product_type "com.apple.product-type.bundle.unit-test")
       elseif(CMAKE_OSX_SYSROOT MATCHES "(^|/)[Ww][Aa][Tt][Cc][Hh]")
diff --git a/Modules/CMakeDetermineFortranCompiler.cmake b/Modules/CMakeDetermineFortranCompiler.cmake
index 8cbaf70..392f0f1 100644
--- a/Modules/CMakeDetermineFortranCompiler.cmake
+++ b/Modules/CMakeDetermineFortranCompiler.cmake
@@ -98,6 +98,9 @@
   set(CMAKE_Fortran_COMPILER_ID_TEST_FLAGS_FIRST
     # Get verbose output to help distinguish compilers.
     "-v"
+
+    # Try compiling to an object file only, with verbose output.
+    "-v -c"
     )
   set(CMAKE_Fortran_COMPILER_ID_TEST_FLAGS
     # Try compiling to an object file only.
@@ -108,6 +111,10 @@
     )
 endif()
 
+if(CMAKE_Fortran_COMPILER_TARGET)
+  set(CMAKE_Fortran_COMPILER_ID_TEST_FLAGS_FIRST "-v -c --target=${CMAKE_Fortran_COMPILER_TARGET}")
+endif()
+
 # Build a small source file to identify the compiler.
 if(NOT CMAKE_Fortran_COMPILER_ID_RUN)
   set(CMAKE_Fortran_COMPILER_ID_RUN 1)
@@ -227,6 +234,49 @@
   endif()
 endif()
 
+if("${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "LLVMFlang;MSVC")
+  # With LLVMFlang targeting the MSVC ABI we link using lld-link.
+  # Detect the implicit link information from the compiler driver
+  # so we can explicitly pass it to the linker.
+  include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake)
+  set(_LLVMFlang_COMMAND "${CMAKE_Fortran_COMPILER}" "-###" ${CMAKE_CURRENT_LIST_DIR}/CMakeFortranCompilerABI.F)
+  if(CMAKE_Fortran_COMPILER_TARGET)
+    list(APPEND _LLVMFlang_COMMAND --target=${CMAKE_Fortran_COMPILER_TARGET})
+  endif()
+  execute_process(COMMAND ${_LLVMFlang_COMMAND}
+    OUTPUT_VARIABLE _LLVMFlang_OUTPUT
+    ERROR_VARIABLE _LLVMFlang_OUTPUT
+    RESULT_VARIABLE _LLVMFlang_RESULT)
+  string(JOIN "\" \"" _LLVMFlang_COMMAND ${_LLVMFlang_COMMAND})
+  message(CONFIGURE_LOG
+    "Running the Fortran compiler: \"${_LLVMFlang_COMMAND}\"\n"
+    "${_LLVMFlang_OUTPUT}"
+    )
+  if(_LLVMFlang_RESULT EQUAL 0)
+    cmake_parse_implicit_link_info("${_LLVMFlang_OUTPUT}"
+                                   CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES
+                                   CMAKE_Fortran_IMPLICIT_LINK_DIRECTORIES
+                                   CMAKE_Fortran_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES
+                                   log
+                                   "${CMAKE_Fortran_IMPLICIT_OBJECT_REGEX}"
+                                   LANGUAGE Fortran)
+    message(CONFIGURE_LOG
+      "Parsed Fortran implicit link information:\n"
+      "${log}\n"
+      )
+    set(_CMAKE_Fortran_IMPLICIT_LINK_INFORMATION_DETERMINED_EARLY 1)
+    if("x${CMAKE_Fortran_COMPILER_ARCHITECTURE_ID}" STREQUAL "xARM64")
+      # FIXME(LLVMFlang): It does not add `-defaultlib:` fields to object
+      # files to specify link dependencies on its runtime libraries.
+      # For now, we add them ourselves.
+      list(APPEND CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES "clang_rt.builtins-aarch64.lib")
+    endif()
+  endif()
+  unset(_LLVMFlang_COMMAND)
+  unset(_LLVMFlang_OUTPUT)
+  unset(_LLVMFlang_RESULT)
+endif()
+
 if (NOT _CMAKE_TOOLCHAIN_LOCATION)
   get_filename_component(_CMAKE_TOOLCHAIN_LOCATION "${CMAKE_Fortran_COMPILER}" PATH)
 endif ()
diff --git a/Modules/CMakeDetermineHIPCompiler.cmake b/Modules/CMakeDetermineHIPCompiler.cmake
index 9a40e82..e667099 100644
--- a/Modules/CMakeDetermineHIPCompiler.cmake
+++ b/Modules/CMakeDetermineHIPCompiler.cmake
@@ -5,11 +5,29 @@
 include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake)
 include(${CMAKE_ROOT}/Modules/CMakeParseLibraryArchitecture.cmake)
 
-if( NOT ( ("${CMAKE_GENERATOR}" MATCHES "Make") OR
-          ("${CMAKE_GENERATOR}" MATCHES "Ninja") ) )
+if(NOT ((CMAKE_GENERATOR MATCHES "Make") OR
+        (CMAKE_GENERATOR MATCHES "Ninja")))
   message(FATAL_ERROR "HIP language not currently supported by \"${CMAKE_GENERATOR}\" generator")
 endif()
 
+if(NOT CMAKE_HIP_PLATFORM)
+  execute_process(COMMAND hipconfig --platform
+    OUTPUT_VARIABLE _CMAKE_HIPCONFIG_PLATFORM OUTPUT_STRIP_TRAILING_WHITESPACE
+    RESULT_VARIABLE _CMAKE_HIPCONFIG_RESULT
+    )
+  if(_CMAKE_HIPCONFIG_RESULT EQUAL 0 AND _CMAKE_HIPCONFIG_PLATFORM MATCHES "^(nvidia|nvcc)$")
+    set(CMAKE_HIP_PLATFORM "nvidia" CACHE STRING "HIP platform" FORCE)
+  else()
+    set(CMAKE_HIP_PLATFORM "amd" CACHE STRING "HIP platform" FORCE)
+  endif()
+endif()
+if(NOT CMAKE_HIP_PLATFORM MATCHES "^(amd|nvidia)$")
+  message(FATAL_ERROR
+    "The CMAKE_HIP_PLATFORM has unsupported value:\n"
+    " '${CMAKE_HIP_PLATFORM}'\n"
+    "It must be 'amd' or 'nvidia'."
+    )
+endif()
 
 if(NOT CMAKE_HIP_COMPILER)
   set(CMAKE_HIP_COMPILER_INIT NOTFOUND)
@@ -34,15 +52,19 @@
 
   # finally list compilers to try
   if(NOT CMAKE_HIP_COMPILER_INIT)
-    set(CMAKE_HIP_COMPILER_LIST clang++)
+    if(CMAKE_HIP_PLATFORM STREQUAL "nvidia")
+      set(CMAKE_HIP_COMPILER_LIST nvcc)
+    elseif(CMAKE_HIP_PLATFORM STREQUAL "amd")
+      set(CMAKE_HIP_COMPILER_LIST clang++)
 
-    # Look for the Clang coming with ROCm to support HIP.
-    execute_process(COMMAND hipconfig --hipclangpath
-      OUTPUT_VARIABLE _CMAKE_HIPCONFIG_CLANGPATH
-      RESULT_VARIABLE _CMAKE_HIPCONFIG_RESULT
-    )
-    if(_CMAKE_HIPCONFIG_RESULT EQUAL 0 AND EXISTS "${_CMAKE_HIPCONFIG_CLANGPATH}")
-      set(CMAKE_HIP_COMPILER_HINTS "${_CMAKE_HIPCONFIG_CLANGPATH}")
+      # Look for the Clang coming with ROCm to support HIP.
+      execute_process(COMMAND hipconfig --hipclangpath
+        OUTPUT_VARIABLE _CMAKE_HIPCONFIG_CLANGPATH
+        RESULT_VARIABLE _CMAKE_HIPCONFIG_RESULT
+      )
+      if(_CMAKE_HIPCONFIG_RESULT EQUAL 0 AND EXISTS "${_CMAKE_HIPCONFIG_CLANGPATH}")
+        set(CMAKE_HIP_COMPILER_HINTS "${_CMAKE_HIPCONFIG_CLANGPATH}")
+      endif()
     endif()
   endif()
 
@@ -63,17 +85,65 @@
 if(NOT CMAKE_HIP_COMPILER_ID_RUN)
   set(CMAKE_HIP_COMPILER_ID_RUN 1)
 
-  # Try to identify the compiler.
+  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake)
+
+  # We determine the vendor to use the right flags for detection right away.
+  # The main compiler identification is still needed below to extract other information.
+  list(APPEND CMAKE_HIP_COMPILER_ID_VENDORS NVIDIA Clang)
+  set(CMAKE_HIP_COMPILER_ID_VENDOR_REGEX_NVIDIA "nvcc: NVIDIA \\(R\\) Cuda compiler driver")
+  set(CMAKE_HIP_COMPILER_ID_VENDOR_REGEX_Clang "(clang version)")
+  CMAKE_DETERMINE_COMPILER_ID_VENDOR(HIP "--version")
+
+  if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+    # Find the CUDA toolkit to get:
+    # - CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION
+    # - CMAKE_HIP_COMPILER_CUDA_TOOLKIT_ROOT
+    # - CMAKE_HIP_COMPILER_CUDA_LIBRARY_ROOT
+    # We save them in CMakeHIPCompiler.cmake.
+    # Match arguments with cmake_cuda_architectures_all call.
+    include(Internal/CMakeCUDAFindToolkit)
+    cmake_cuda_find_toolkit(HIP CMAKE_HIP_COMPILER_CUDA_)
+
+    # If the user set CMAKE_HIP_ARCHITECTURES, validate its value.
+    include(Internal/CMakeCUDAArchitecturesValidate)
+    cmake_cuda_architectures_validate(HIP)
+
+    if(NOT CMAKE_HIP_HOST_COMPILER AND NOT $ENV{HIPHOSTCXX} STREQUAL "")
+      get_filename_component(CMAKE_HIP_HOST_COMPILER $ENV{HIPHOSTCXX} PROGRAM)
+      if(NOT EXISTS "${CMAKE_HIP_HOST_COMPILER}")
+        message(FATAL_ERROR "Could not find compiler set in environment variable HIPHOSTCXX:\n$ENV{HIPHOSTCXX}.\n${CMAKE_HIP_HOST_COMPILER}")
+      endif()
+    endif()
+  endif()
+
+  if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang")
+    list(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST "-v")
+  elseif(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+    # Tell nvcc to treat .hip files as CUDA sources.
+    list(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST "-x cu -v")
+    if(CMAKE_HIP_HOST_COMPILER)
+      string(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST " -ccbin=\"${CMAKE_HIP_HOST_COMPILER}\"")
+    endif()
+  endif()
+
+  # We perform compiler identification for a second time to extract implicit linking info.
+  # We need to unset the compiler ID otherwise CMAKE_DETERMINE_COMPILER_ID() doesn't work.
   set(CMAKE_HIP_COMPILER_ID)
   set(CMAKE_HIP_PLATFORM_ID)
   file(READ ${CMAKE_ROOT}/Modules/CMakePlatformId.h.in
     CMAKE_HIP_COMPILER_ID_PLATFORM_CONTENT)
 
-  list(APPEND CMAKE_HIP_COMPILER_ID_TEST_FLAGS_FIRST "-v")
-
-  include(${CMAKE_ROOT}/Modules/CMakeDetermineCompilerId.cmake)
   CMAKE_DETERMINE_COMPILER_ID(HIP HIPFLAGS CMakeHIPCompilerId.hip)
 
+  if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+    include(Internal/CMakeCUDAArchitecturesAll)
+    # From CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION and CMAKE_HIP_COMPILER_{ID,VERSION}, get:
+    # - CMAKE_HIP_ARCHITECTURES_ALL
+    # - CMAKE_HIP_ARCHITECTURES_ALL_MAJOR
+    # Match arguments with cmake_cuda_find_toolkit call.
+    cmake_cuda_architectures_all(HIP CMAKE_HIP_COMPILER_CUDA_)
+  endif()
+
   _cmake_find_compiler_sysroot(HIP)
 endif()
 
@@ -104,56 +174,61 @@
   message(FATAL_ERROR "Failed to find ROCm root directory.")
 endif()
 
-# Normally implicit link information is not detected until
-cmake_parse_implicit_link_info("${CMAKE_HIP_COMPILER_PRODUCED_OUTPUT}"
-  _CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS
-  _CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS
-  _CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS
-  _CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG
-  "" LANGUAGE HIP)
-message(CONFIGURE_LOG
-  "Parsed HIP implicit link information from compiler id output:\n${_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG}\n\n")
-cmake_parse_library_architecture(HIP "${_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS}" "" CMAKE_HIP_LIBRARY_ARCHITECTURE)
-if(CMAKE_HIP_LIBRARY_ARCHITECTURE)
-  message(CONFIGURE_LOG
-    "Parsed HIP library architecture from compiler id output: ${CMAKE_HIP_LIBRARY_ARCHITECTURE}\n")
-endif()
-unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS)
-unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS)
-unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS)
-unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG)
+if(CMAKE_HIP_PLATFORM STREQUAL "amd")
+  # For this platform we need the hip-lang cmake package.
 
-if(NOT CMAKE_HIP_COMPILER_ROCM_LIB)
-  set(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS
-    "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib"
-    "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib64"
-    )
+  # Normally implicit link information is not detected until ABI detection,
+  # but we need to populate CMAKE_HIP_LIBRARY_ARCHITECTURE to find hip-lang.
+  cmake_parse_implicit_link_info("${CMAKE_HIP_COMPILER_PRODUCED_OUTPUT}"
+    _CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS
+    _CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS
+    _CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS
+    _CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG
+    "" LANGUAGE HIP)
+  message(CONFIGURE_LOG
+    "Parsed HIP implicit link information from compiler id output:\n${_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG}\n\n")
+  cmake_parse_library_architecture(HIP "${_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS}" "" CMAKE_HIP_LIBRARY_ARCHITECTURE)
   if(CMAKE_HIP_LIBRARY_ARCHITECTURE)
-    list(APPEND _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib/${CMAKE_HIP_LIBRARY_ARCHITECTURE}")
+    message(CONFIGURE_LOG
+      "Parsed HIP library architecture from compiler id output: ${CMAKE_HIP_LIBRARY_ARCHITECTURE}\n")
   endif()
-  foreach(dir IN LISTS _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS)
-    if(EXISTS "${dir}/cmake/hip-lang/hip-lang-config.cmake")
-      set(CMAKE_HIP_COMPILER_ROCM_LIB "${dir}")
-      break()
-    endif()
-  endforeach()
+  unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LIBS)
+  unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_DIRS)
+  unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_FWKS)
+  unset(_CMAKE_HIP_COMPILER_ID_IMPLICIT_LOG)
+
   if(NOT CMAKE_HIP_COMPILER_ROCM_LIB)
-    list(TRANSFORM _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS APPEND "/cmake/hip-lang/hip-lang-config.cmake")
-    string(REPLACE ";" "\n " _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}")
-    message(FATAL_ERROR
-      "The ROCm root directory:\n"
-      " ${CMAKE_HIP_COMPILER_ROCM_ROOT}\n"
-      "does not contain the HIP runtime CMake package, expected at one of:\n"
-      " ${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}\n"
+    set(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS
+      "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib"
+      "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib64"
       )
+    if(CMAKE_HIP_LIBRARY_ARCHITECTURE)
+      list(APPEND _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${CMAKE_HIP_COMPILER_ROCM_ROOT}/lib/${CMAKE_HIP_LIBRARY_ARCHITECTURE}")
+    endif()
+    foreach(dir IN LISTS _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS)
+      if(EXISTS "${dir}/cmake/hip-lang/hip-lang-config.cmake")
+        set(CMAKE_HIP_COMPILER_ROCM_LIB "${dir}")
+        break()
+      endif()
+    endforeach()
+    if(NOT CMAKE_HIP_COMPILER_ROCM_LIB)
+      list(TRANSFORM _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS APPEND "/cmake/hip-lang/hip-lang-config.cmake")
+      string(REPLACE ";" "\n " _CMAKE_HIP_COMPILER_ROCM_LIB_DIRS "${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}")
+      message(FATAL_ERROR
+        "The ROCm root directory:\n"
+        " ${CMAKE_HIP_COMPILER_ROCM_ROOT}\n"
+        "does not contain the HIP runtime CMake package, expected at one of:\n"
+        " ${_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS}\n"
+        )
+    endif()
+    unset(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS)
   endif()
-  unset(_CMAKE_HIP_COMPILER_ROCM_LIB_DIRS)
-endif()
-if(CMAKE_HIP_COMPILER_ROCM_LIB MATCHES "/lib64$" AND NOT DEFINED CMAKE_SIZEOF_VOID_P)
-  # We have not yet determined the target ABI but we need 'find_package' to
-  # search lib64 directories to find hip-lang CMake package dependencies.
-  # This will be replaced by ABI detection later.
-  set(CMAKE_HIP_SIZEOF_DATA_PTR 8)
+  if(CMAKE_HIP_COMPILER_ROCM_LIB MATCHES "/lib64$" AND NOT DEFINED CMAKE_SIZEOF_VOID_P)
+    # We have not yet determined the target ABI but we need 'find_package' to
+    # search lib64 directories to find hip-lang CMake package dependencies.
+    # This will be replaced by ABI detection later.
+    set(CMAKE_HIP_SIZEOF_DATA_PTR 8)
+  endif()
 endif()
 
 if (NOT _CMAKE_TOOLCHAIN_LOCATION)
@@ -165,6 +240,26 @@
 include(Compiler/${CMAKE_HIP_COMPILER_ID}-FindBinUtils OPTIONAL)
 unset(_CMAKE_PROCESSING_LANGUAGE)
 
+if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang")
+  set(CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT "SHARED")
+elseif(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+  include(Internal/CMakeNVCCParseImplicitInfo)
+  # Parse CMAKE_HIP_COMPILER_PRODUCED_OUTPUT to get:
+  # - CMAKE_HIP_ARCHITECTURES_DEFAULT
+  # - CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES
+  # - CMAKE_HIP_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES
+  # - CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES
+  # - CMAKE_HIP_HOST_LINK_LAUNCHER
+  # - CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT
+  # - CMAKE_HIP_CUDA_TOOLKIT_INCLUDE_DIRECTORIES
+  # Match arguments with cmake_nvcc_filter_implicit_info call in CMakeTestHIPCompiler.
+  cmake_nvcc_parse_implicit_info(HIP CMAKE_HIP_CUDA_)
+
+  include(Internal/CMakeCUDAFilterImplicitLibs)
+  # Filter out implicit link libraries that should not be passed unconditionally.
+  cmake_cuda_filter_implicit_libs(CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES)
+endif()
+
 if(CMAKE_HIP_COMPILER_SYSROOT)
   string(CONCAT _SET_CMAKE_HIP_COMPILER_SYSROOT
     "set(CMAKE_HIP_COMPILER_SYSROOT \"${CMAKE_HIP_COMPILER_SYSROOT}\")\n"
@@ -185,7 +280,20 @@
     "set(MSVC_HIP_ARCHITECTURE_ID ${MSVC_HIP_ARCHITECTURE_ID})")
 endif()
 
-if(NOT DEFINED CMAKE_HIP_ARCHITECTURES)
+if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+  if(NOT "$ENV{CUDAARCHS}" STREQUAL "")
+    set(CMAKE_HIP_ARCHITECTURES "$ENV{CUDAARCHS}" CACHE STRING "CUDA architectures")
+  endif()
+
+  # If the user did not set CMAKE_HIP_ARCHITECTURES, use the compiler's default.
+  if("${CMAKE_HIP_ARCHITECTURES}" STREQUAL "")
+    set(CMAKE_HIP_ARCHITECTURES "${CMAKE_HIP_ARCHITECTURES_DEFAULT}" CACHE STRING "HIP architectures" FORCE)
+    if(NOT CMAKE_HIP_ARCHITECTURES)
+      message(FATAL_ERROR "Failed to detect a default HIP architecture.\n\nCompiler output:\n${CMAKE_HIP_COMPILER_PRODUCED_OUTPUT}")
+    endif()
+  endif()
+  unset(CMAKE_HIP_ARCHITECTURES_DEFAULT)
+elseif(NOT DEFINED CMAKE_HIP_ARCHITECTURES)
   # Use 'rocm_agent_enumerator' to get the current GPU architecture.
   set(_CMAKE_HIP_ARCHITECTURES)
   find_program(_CMAKE_HIP_ROCM_AGENT_ENUMERATOR
diff --git a/Modules/CMakeDetermineSystem.cmake b/Modules/CMakeDetermineSystem.cmake
index 386be73..fff4e9d 100644
--- a/Modules/CMakeDetermineSystem.cmake
+++ b/Modules/CMakeDetermineSystem.cmake
@@ -47,6 +47,28 @@
       set(CMAKE_HOST_SYSTEM_VERSION "${_CMAKE_HOST_SYSTEM_MAJOR_VERSION}.${_CMAKE_HOST_SYSTEM_MINOR_VERSION}")
       unset(_CMAKE_HOST_SYSTEM_MAJOR_VERSION)
       unset(_CMAKE_HOST_SYSTEM_MINOR_VERSION)
+    elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Android")
+      execute_process(COMMAND getprop ro.build.version.sdk
+        OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+        ERROR_QUIET)
+
+      if(NOT DEFINED CMAKE_SYSTEM_VERSION)
+        set(_ANDROID_API_LEVEL_H $ENV{PREFIX}/include/android/api-level.h)
+        set(_ANDROID_API_REGEX "#define __ANDROID_API__ ([0-9]+)")
+        file(READ ${_ANDROID_API_LEVEL_H} _ANDROID_API_LEVEL_H_CONTENT)
+        string(REGEX MATCH ${_ANDROID_API_REGEX} _ANDROID_API_LINE "${_ANDROID_API_LEVEL_H_CONTENT}")
+        string(REGEX REPLACE ${_ANDROID_API_REGEX} "\\1" _ANDROID_API "${_ANDROID_API_LINE}")
+        if(_ANDROID_API)
+          set(CMAKE_SYSTEM_VERSION "${_ANDROID_API}")
+        endif()
+
+        unset(_ANDROID_API_LEVEL_H)
+        unset(_ANDROID_API_LEVEL_H_CONTENT)
+        unset(_ANDROID_API_REGEX)
+        unset(_ANDROID_API_LINE)
+        unset(_ANDROID_API)
+      endif()
     else()
       execute_process(COMMAND ${CMAKE_UNAME} -r
         OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION
diff --git a/Modules/CMakeFindBinUtils.cmake b/Modules/CMakeFindBinUtils.cmake
index f778891..e12b175 100644
--- a/Modules/CMakeFindBinUtils.cmake
+++ b/Modules/CMakeFindBinUtils.cmake
@@ -79,10 +79,10 @@
   set(_CMAKE_MT_NAMES "mt")
 
   # Prepend toolchain-specific names.
-  if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" STREQUAL "xClang")
+  if("x${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ID}" MATCHES "^x(Clang|LLVMFlang)$")
     set(_CMAKE_NM_NAMES "llvm-nm" "nm")
     list(PREPEND _CMAKE_AR_NAMES "llvm-lib")
-    # llvm-mt does not support all flags we need in vs_link_exe
+    # llvm-mt is not ready to be used as a replacement for mt.exe
     # list(PREPEND _CMAKE_MT_NAMES "llvm-mt")
     list(PREPEND _CMAKE_LINKER_NAMES "lld-link")
     list(APPEND _CMAKE_TOOL_VARS NM)
diff --git a/Modules/CMakeFortranCompiler.cmake.in b/Modules/CMakeFortranCompiler.cmake.in
index 6a2be28..89a00ab 100644
--- a/Modules/CMakeFortranCompiler.cmake.in
+++ b/Modules/CMakeFortranCompiler.cmake.in
@@ -14,8 +14,9 @@
 set(CMAKE_AR "@CMAKE_AR@")
 set(CMAKE_Fortran_COMPILER_AR "@CMAKE_Fortran_COMPILER_AR@")
 set(CMAKE_RANLIB "@CMAKE_RANLIB@")
-set(CMAKE_TAPI "@CMAKE_TAPI@")
+set(CMAKE_LINKER "@CMAKE_LINKER@")
 set(CMAKE_Fortran_COMPILER_RANLIB "@CMAKE_Fortran_COMPILER_RANLIB@")
+set(CMAKE_TAPI "@CMAKE_TAPI@")
 set(CMAKE_COMPILER_IS_GNUG77 @CMAKE_COMPILER_IS_GNUG77@)
 set(CMAKE_Fortran_COMPILER_LOADED 1)
 set(CMAKE_Fortran_COMPILER_WORKS @CMAKE_Fortran_COMPILER_WORKS@)
diff --git a/Modules/CMakeHIPCompiler.cmake.in b/Modules/CMakeHIPCompiler.cmake.in
index 0fa5bf0..6d5e62a 100644
--- a/Modules/CMakeHIPCompiler.cmake.in
+++ b/Modules/CMakeHIPCompiler.cmake.in
@@ -1,4 +1,6 @@
 set(CMAKE_HIP_COMPILER "@CMAKE_HIP_COMPILER@")
+set(CMAKE_HIP_HOST_COMPILER "@CMAKE_HIP_HOST_COMPILER@")
+set(CMAKE_HIP_HOST_LINK_LAUNCHER "@CMAKE_HIP_HOST_LINK_LAUNCHER@")
 set(CMAKE_HIP_COMPILER_ID "@CMAKE_HIP_COMPILER_ID@")
 set(CMAKE_HIP_COMPILER_VERSION "@CMAKE_HIP_COMPILER_VERSION@")
 set(CMAKE_HIP_STANDARD_COMPUTED_DEFAULT "@CMAKE_HIP_STANDARD_COMPUTED_DEFAULT@")
@@ -45,14 +47,27 @@
   set(CMAKE_LIBRARY_ARCHITECTURE "@CMAKE_HIP_LIBRARY_ARCHITECTURE@")
 endif()
 
-set(CMAKE_HIP_TOOLKIT_INCLUDE_DIRECTORIES "@CMAKE_HIP_TOOLKIT_INCLUDE_DIRECTORIES@")
+set(CMAKE_HIP_COMPILER_CUDA_TOOLKIT_ROOT "@CMAKE_HIP_COMPILER_CUDA_TOOLKIT_ROOT@")
+set(CMAKE_HIP_COMPILER_CUDA_TOOLKIT_LIBRARY_ROOT "@CMAKE_HIP_COMPILER_CUDA_TOOLKIT_LIBRARY_ROOT@")
+set(CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION "@CMAKE_HIP_COMPILER_CUDA_TOOLKIT_VERSION@")
+set(CMAKE_HIP_COMPILER_CUDA_LIBRARY_ROOT "@CMAKE_HIP_COMPILER_CUDA_LIBRARY_ROOT@")
+
+set(CMAKE_HIP_ARCHITECTURES_ALL "@CMAKE_HIP_ARCHITECTURES_ALL@")
+set(CMAKE_HIP_ARCHITECTURES_ALL_MAJOR "@CMAKE_HIP_ARCHITECTURES_ALL_MAJOR@")
+set(CMAKE_HIP_ARCHITECTURES_NATIVE "@CMAKE_HIP_ARCHITECTURES_NATIVE@")
+
+set(CMAKE_HIP_CUDA_TOOLKIT_INCLUDE_DIRECTORIES "@CMAKE_HIP_CUDA_TOOLKIT_INCLUDE_DIRECTORIES@")
+
+set(CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES "@CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES@")
+set(CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES "@CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES@")
+set(CMAKE_HIP_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_HIP_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
 
 set(CMAKE_HIP_IMPLICIT_INCLUDE_DIRECTORIES "@CMAKE_HIP_IMPLICIT_INCLUDE_DIRECTORIES@")
 set(CMAKE_HIP_IMPLICIT_LINK_LIBRARIES "@CMAKE_HIP_IMPLICIT_LINK_LIBRARIES@")
 set(CMAKE_HIP_IMPLICIT_LINK_DIRECTORIES "@CMAKE_HIP_IMPLICIT_LINK_DIRECTORIES@")
 set(CMAKE_HIP_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "@CMAKE_HIP_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES@")
 
-set(CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT "SHARED")
+set(CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT "@CMAKE_HIP_RUNTIME_LIBRARY_DEFAULT@")
 
 set(CMAKE_AR "@CMAKE_AR@")
 set(CMAKE_HIP_COMPILER_AR "@CMAKE_HIP_COMPILER_AR@")
diff --git a/Modules/CMakeHIPCompilerABI.hip b/Modules/CMakeHIPCompilerABI.hip
index 6c912bd..7d8b815 100644
--- a/Modules/CMakeHIPCompilerABI.hip
+++ b/Modules/CMakeHIPCompilerABI.hip
@@ -1,9 +1,13 @@
-#ifndef __HIP__
+#if !defined(__HIP__) && !defined(__NVCC__)
 #  error "A C or C++ compiler has been selected for HIP"
 #endif
 
 #include "CMakeCompilerABI.h"
 
+#if defined(__NVCC__)
+#  include "CMakeCompilerCUDAArch.h"
+#endif
+
 int main(int argc, char* argv[])
 {
   int require = 0;
@@ -11,6 +15,16 @@
 #if defined(ABI_ID)
   require += info_abi[argc];
 #endif
-  (void)argv;
+  static_cast<void>(argv);
+
+#if defined(__NVCC__)
+  if (!cmakeCompilerCUDAArch()) {
+    // Convince the compiler that the non-zero return value depends
+    // on the info strings so they are not optimized out.
+    return require ? -1 : 1;
+  }
+  return 0;
+#else
   return require;
+#endif
 }
diff --git a/Modules/CMakeHIPCompilerId.hip.in b/Modules/CMakeHIPCompilerId.hip.in
index 3c4a1d4..4ac0f30 100644
--- a/Modules/CMakeHIPCompilerId.hip.in
+++ b/Modules/CMakeHIPCompilerId.hip.in
@@ -1,4 +1,4 @@
-#ifndef __HIP__
+#if !defined(__HIP__) && !defined(__NVCC__)
 # error "A C or C++ compiler has been selected for HIP"
 #endif
 
diff --git a/Modules/CMakeHIPInformation.cmake b/Modules/CMakeHIPInformation.cmake
index 41a98db..3995c36 100644
--- a/Modules/CMakeHIPInformation.cmake
+++ b/Modules/CMakeHIPInformation.cmake
@@ -8,6 +8,19 @@
 endif()
 set(CMAKE_INCLUDE_FLAG_HIP "-I")
 
+# Set implicit links early so compiler-specific modules can use them.
+set(__IMPLICIT_LINKS)
+foreach(dir ${CMAKE_HIP_HOST_IMPLICIT_LINK_DIRECTORIES})
+  string(APPEND __IMPLICIT_LINKS " -L\"${dir}\"")
+endforeach()
+foreach(lib ${CMAKE_HIP_HOST_IMPLICIT_LINK_LIBRARIES})
+  if(${lib} MATCHES "/")
+    string(APPEND __IMPLICIT_LINKS " \"${lib}\"")
+  else()
+    string(APPEND __IMPLICIT_LINKS " -l${lib}")
+  endif()
+endforeach()
+
 # Load compiler-specific information.
 if(CMAKE_HIP_COMPILER_ID)
   include(Compiler/${CMAKE_HIP_COMPILER_ID}-HIP OPTIONAL)
@@ -129,7 +142,7 @@
 # compile a HIP file into an object file
 if(NOT CMAKE_HIP_COMPILE_OBJECT)
   set(CMAKE_HIP_COMPILE_OBJECT
-    "<CMAKE_HIP_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> ${_CMAKE_COMPILE_AS_HIP_FLAG} -c <SOURCE>")
+    "<CMAKE_HIP_COMPILER> ${_CMAKE_HIP_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> ${_CMAKE_COMPILE_AS_HIP_FLAG} -c <SOURCE>")
 endif()
 
 # compile a cu file into an executable
diff --git a/Modules/CMakeParseImplicitLinkInfo.cmake b/Modules/CMakeParseImplicitLinkInfo.cmake
index 1773dc4..9213cc9 100644
--- a/Modules/CMakeParseImplicitLinkInfo.cmake
+++ b/Modules/CMakeParseImplicitLinkInfo.cmake
@@ -26,12 +26,22 @@
   set(multiValueArgs )
   cmake_parse_arguments(EXTRA_PARSE "${keywordArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
 
+  set(is_msvc 0)
+  if(EXTRA_PARSE_LANGUAGE AND
+    ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_ID}" STREQUAL "xMSVC" OR
+     "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC"))
+    set(is_msvc 1)
+  endif()
+
   # Parse implicit linker arguments.
   set(linker "CMAKE_LINKER-NOTFOUND")
   if(CMAKE_LINKER)
     get_filename_component(linker ${CMAKE_LINKER} NAME)
     string(REGEX REPLACE "([][+.*?()^$])" "\\\\\\1" linker "${linker}")
   endif()
+  if(is_msvc)
+    string(APPEND linker "|link\\.exe|lld-link")
+  endif()
   set(startfile "CMAKE_LINK_STARTFILE-NOTFOUND")
   if(CMAKE_LINK_STARTFILE)
     set(startfile "${CMAKE_LINK_STARTFILE}")
@@ -75,12 +85,6 @@
         endif()
       endif()
     endif()
-    set(is_msvc 0)
-    if(EXTRA_PARSE_LANGUAGE AND
-      ("x${CMAKE_${EXTRA_PARSE_LANGUAGE}_ID}" STREQUAL "xMSVC" OR
-       "x${CMAKE_${EXTRA_PARSE_LANGUAGE}_SIMULATE_ID}" STREQUAL "xMSVC"))
-      set(is_msvc 1)
-    endif()
     set(search_static 0)
     if("${cmd}" MATCHES "${linker_regex}")
       string(APPEND log "  link line: [${line}]\n")
diff --git a/Modules/CMakeSwiftInformation.cmake b/Modules/CMakeSwiftInformation.cmake
index 777c680..1c6f0df 100644
--- a/Modules/CMakeSwiftInformation.cmake
+++ b/Modules/CMakeSwiftInformation.cmake
@@ -21,7 +21,8 @@
 
 # FIXME: Move compiler- and platform-specific flags to the above-included modules.
 if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS"
-    OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
+    OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS"
+    OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
   set(CMAKE_SHARED_LIBRARY_SONAME_Swift_FLAG "-Xlinker -install_name -Xlinker ")
 elseif(NOT CMAKE_SYSTEM_NAME STREQUAL Windows)
   set(CMAKE_SHARED_LIBRARY_SONAME_Swift_FLAG "-Xlinker -soname -Xlinker ")
@@ -30,7 +31,8 @@
   set(CMAKE_EXECUTABLE_RUNTIME_Swift_FLAG "-Xlinker -rpath -Xlinker ")
   set(CMAKE_SHARED_LIBRARY_RUNTIME_Swift_FLAG "-Xlinker -rpath -Xlinker ")
   if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR CMAKE_SYSTEM_NAME STREQUAL "iOS"
-      OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
+      OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS"
+      OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
     set(CMAKE_EXECUTABLE_RUNTIME_Swift_FLAG_SEP "")
     set(CMAKE_SHARED_LIBRARY_RUNTIME_Swift_FLAG_SEP "")
   else()
diff --git a/Modules/CMakeSystemSpecificInitialize.cmake b/Modules/CMakeSystemSpecificInitialize.cmake
index e87d868..ee8cb86 100644
--- a/Modules/CMakeSystemSpecificInitialize.cmake
+++ b/Modules/CMakeSystemSpecificInitialize.cmake
@@ -25,7 +25,7 @@
 # It is useful to share the same aforementioned configuration files and
 # avoids duplicating them in case of tightly related platforms.
 #
-# An example are the platforms supported by Xcode (macOS, iOS, tvOS,
+# An example are the platforms supported by Xcode (macOS, iOS, tvOS, visionOS
 # and watchOS). For all of those the CMAKE_EFFECTIVE_SYSTEM_NAME is
 # set to Apple which results in using
 # Platform/Apple-AppleClang-CXX.cmake for the Apple C++ compiler.
diff --git a/Modules/CMakeTestCUDACompiler.cmake b/Modules/CMakeTestCUDACompiler.cmake
index 5779e4b..3057fe9 100644
--- a/Modules/CMakeTestCUDACompiler.cmake
+++ b/Modules/CMakeTestCUDACompiler.cmake
@@ -22,51 +22,10 @@
   set(CMAKE_CUDA_COMPILER_WORKS TRUE)
   message(STATUS "Check for working CUDA compiler: ${CMAKE_CUDA_COMPILER} - skipped")
 
-  # Run the test binary to detect the native architectures.
-  execute_process(COMMAND "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCompilerABI_CUDA.bin"
-    RESULT_VARIABLE _CUDA_ARCHS_RESULT
-    OUTPUT_VARIABLE _CUDA_ARCHS_OUTPUT
-    ERROR_VARIABLE  _CUDA_ARCHS_OUTPUT
-    OUTPUT_STRIP_TRAILING_WHITESPACE
-    )
-  if(_CUDA_ARCHS_RESULT EQUAL 0)
-    if("$ENV{CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP}")
-      # Undocumented hook used by CMake's CI.
-      # Clamp native architecture to version range supported by this CUDA.
-      list(GET CMAKE_CUDA_ARCHITECTURES_ALL 0  _CUDA_ARCH_MIN)
-      list(GET CMAKE_CUDA_ARCHITECTURES_ALL -1 _CUDA_ARCH_MAX)
-      set(CMAKE_CUDA_ARCHITECTURES_NATIVE "")
-      foreach(_CUDA_ARCH IN LISTS _CUDA_ARCHS_OUTPUT)
-        if(_CUDA_ARCH LESS _CUDA_ARCH_MIN)
-          set(_CUDA_ARCH "${_CUDA_ARCH_MIN}")
-        endif()
-        if(_CUDA_ARCH GREATER _CUDA_ARCH_MAX)
-          set(_CUDA_ARCH "${_CUDA_ARCH_MAX}")
-        endif()
-        list(APPEND CMAKE_CUDA_ARCHITECTURES_NATIVE ${_CUDA_ARCH})
-      endforeach()
-      unset(_CUDA_ARCH)
-      unset(_CUDA_ARCH_MIN)
-      unset(_CUDA_ARCH_MAX)
-    else()
-      set(CMAKE_CUDA_ARCHITECTURES_NATIVE "${_CUDA_ARCHS_OUTPUT}")
-    endif()
-    list(REMOVE_DUPLICATES CMAKE_CUDA_ARCHITECTURES_NATIVE)
-    list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_NATIVE APPEND "-real")
-  else()
-    if(NOT _CUDA_ARCHS_RESULT MATCHES "[0-9]+")
-      set(_CUDA_ARCHS_STATUS " (${_CUDA_ARCHS_RESULT})")
-    else()
-      set(_CUDA_ARCHS_STATUS "")
-    endif()
-    string(REPLACE "\n" "\n  " _CUDA_ARCHS_OUTPUT "  ${_CUDA_ARCHS_OUTPUT}")
-    message(CONFIGURE_LOG
-      "Detecting the CUDA native architecture(s) failed with "
-      "the following output:\n${_CUDA_ARCHS_OUTPUT}\n\n")
-  endif()
-  unset(_CUDA_ARCHS_EXE)
-  unset(_CUDA_ARCHS_RESULT)
-  unset(_CUDA_ARCHS_OUTPUT)
+  include(Internal/CMakeCUDAArchitecturesNative)
+  # Run the test binary to get:
+  # - CMAKE_CUDA_ARCHITECTURES_NATIVE
+  cmake_cuda_architectures_native(CUDA)
 endif()
 
 # This file is used by EnableLanguage in cmGlobalGenerator to
@@ -114,22 +73,14 @@
   set(CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES "${CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES}")
 endif()
 
+include(Internal/CMakeCUDAFilterImplicitLibs)
 # Filter out implicit link libraries that should not be passed unconditionally.
-# See CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE in CMakeDetermineCUDACompiler.
-list(REMOVE_ITEM CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES ${CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES_EXCLUDE})
+cmake_cuda_filter_implicit_libs(CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES)
 
 if(CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
-  # Remove the CUDA Toolkit include directories from the set of
-  # implicit system include directories.
-  # This resolves the issue that NVCC doesn't specify these
-  # includes as SYSTEM includes when compiling device code, and sometimes
-  # they contain headers that generate warnings, so let users mark them
-  # as SYSTEM explicitly
-  if(CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES)
-    list(REMOVE_ITEM CMAKE_CUDA_IMPLICIT_INCLUDE_DIRECTORIES
-      ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}
-      )
-  endif()
+  include(Internal/CMakeNVCCFilterImplicitInfo)
+  # Match arguments with cmake_nvcc_parse_implicit_info call in CMakeDetermineCUDACompiler.
+  cmake_nvcc_filter_implicit_info(CUDA CMAKE_CUDA_)
 endif()
 
 # Re-configure to save learned information.
diff --git a/Modules/CMakeTestHIPCompiler.cmake b/Modules/CMakeTestHIPCompiler.cmake
index 686f055..ec54d80 100644
--- a/Modules/CMakeTestHIPCompiler.cmake
+++ b/Modules/CMakeTestHIPCompiler.cmake
@@ -10,7 +10,10 @@
 endif()
 
 set(__CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS}")
-string(APPEND CMAKE_HIP_FLAGS " --cuda-host-only")
+
+if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang")
+  string(APPEND CMAKE_HIP_FLAGS " --cuda-host-only")
+endif()
 
 include(CMakeTestCompilerCommon)
 
@@ -31,6 +34,13 @@
   # The compiler worked so skip dedicated test below.
   set(CMAKE_HIP_COMPILER_WORKS TRUE)
   message(STATUS "Check for working HIP compiler: ${CMAKE_HIP_COMPILER} - skipped")
+
+  if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+    include(Internal/CMakeCUDAArchitecturesNative)
+    # Run the test binary to get:
+    # - CMAKE_HIP_ARCHITECTURES_NATIVE
+    cmake_cuda_architectures_native(HIP)
+  endif()
 endif()
 
 # This file is used by EnableLanguage in cmGlobalGenerator to
@@ -42,7 +52,7 @@
   PrintTestCompilerStatus("HIP")
   __TestCompiler_setTryCompileTargetType()
   string(CONCAT __TestCompiler_testHIPCompilerSource
-    "#ifndef __HIP__\n"
+    "#if !defined(__HIP__) && !defined(__NVCC__)\n"
     "# error \"The CMAKE_HIP_COMPILER is set to a C/CXX compiler\"\n"
     "#endif\n"
     "int main(){return 0;}\n")
@@ -76,6 +86,16 @@
 include(${CMAKE_ROOT}/Modules/CMakeDetermineCompileFeatures.cmake)
 CMAKE_DETERMINE_COMPILE_FEATURES(HIP)
 
+if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+  include(Internal/CMakeNVCCFilterImplicitInfo)
+  # Match arguments with cmake_nvcc_parse_implicit_info call in CMakeDetermineHIPCompiler.
+  cmake_nvcc_filter_implicit_info(HIP CMAKE_HIP_CUDA_)
+
+  include(Internal/CMakeCUDAFilterImplicitLibs)
+  # Filter out implicit link libraries that should not be passed unconditionally.
+  cmake_cuda_filter_implicit_libs(CMAKE_HIP_IMPLICIT_LINK_LIBRARIES)
+endif()
+
 # Re-configure to save learned information.
 configure_file(
   ${CMAKE_ROOT}/Modules/CMakeHIPCompiler.cmake.in
diff --git a/Modules/CUDA/architectures.cmake b/Modules/CUDA/architectures.cmake
deleted file mode 100644
index 7d6a6e0..0000000
--- a/Modules/CUDA/architectures.cmake
+++ /dev/null
@@ -1,69 +0,0 @@
-# See supported GPUs on Wikipedia
-# https://en.wikipedia.org/wiki/CUDA#GPUs_supported
-
-# Initial set based on CUDA 7.0.
-set(CMAKE_CUDA_ARCHITECTURES_ALL 20 21 30 35 37 50 52 53)
-set(CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20 30 35 50)
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 8.0)
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 60 61 62)
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 60)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 9.0)
-  if(NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)
-    list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 70 72)
-    list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 70)
-  endif()
-
-  list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 20 21)
-  list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 10.0
-   AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0))
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 75)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.0)
-  if(NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0)
-    list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 80)
-    list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 80)
-  endif()
-
-  list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 30)
-  list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 30)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.1
-   AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang" OR CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0))
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 86)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4
-   AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang"))
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.8
-   AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang"))
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 89 90)
-  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 90)
-endif()
-
-if(CMAKE_CUDA_COMPILER_TOOLKIT_VERSION VERSION_GREATER_EQUAL 12.0
-   AND (NOT CMAKE_CUDA_COMPILER_ID STREQUAL "Clang"))
-  list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 35 37)
-  list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 35)
-endif()
-
-# only generate jit code for the newest arch for all/all-major
-list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL _latest_arch)
-list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL APPEND "-real")
-list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL ${_latest_arch})
-
-list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR _latest_arch)
-list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR APPEND "-real")
-list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR ${_latest_arch})
-
-unset(_latest_arch)
diff --git a/Modules/CheckCXXSymbolExists.cmake b/Modules/CheckCXXSymbolExists.cmake
index 569dafd..d807c17 100644
--- a/Modules/CheckCXXSymbolExists.cmake
+++ b/Modules/CheckCXXSymbolExists.cmake
@@ -33,7 +33,7 @@
   function. Since there is no reliable way to predict whether a given function
   in the system environment may be defined as an overloaded function or may be
   an overloaded function on other systems or will become so in the future, it
-  is generally advised to use the :module:`CheckCXXSourceCompiles` module for
+  is generally advised to use the :module:`CheckSourceCompiles` module for
   checking any function symbol (unless somehow you surely know the checked
   function is not overloaded on other systems or will not be so in the
   future).
diff --git a/Modules/CheckLanguage.cmake b/Modules/CheckLanguage.cmake
index 69913a3..94948b9 100644
--- a/Modules/CheckLanguage.cmake
+++ b/Modules/CheckLanguage.cmake
@@ -5,26 +5,45 @@
 CheckLanguage
 -------------
 
-Check if a language can be enabled
+Check whether a language can be enabled by the :command:`enable_language`
+or :command:`project` commands:
 
-Usage:
+.. command:: check_language
 
-::
+  .. code-block:: cmake
 
-  check_language(<lang>)
+    check_language(<lang>)
 
-where ``<lang>`` is a language that may be passed to :command:`enable_language`
-such as ``Fortran``.  If :variable:`CMAKE_<LANG>_COMPILER` is already defined
-the check does nothing.  Otherwise it tries enabling the language in a
-test project.  The result is cached in :variable:`CMAKE_<LANG>_COMPILER`
-as the compiler that was found, or ``NOTFOUND`` if the language cannot be
-enabled. For CUDA which can have an explicit host compiler, the cache
-:variable:`CMAKE_CUDA_HOST_COMPILER` variable will be set if it was required
-for compilation (and cleared if it was not).
+  Try enabling language ``<lang>`` in a test project and record results
+  in the cache:
 
-Example:
+  :variable:`CMAKE_<LANG>_COMPILER`
+    If the language can be enabled, this variable is set to the compiler
+    that was found.  If the language cannot be enabled, this variable is
+    set to ``NOTFOUND``.
 
-::
+    If this variable is already set, either explicitly or cached by
+    a previous call, the check is skipped.
+
+  :variable:`CMAKE_<LANG>_HOST_COMPILER`
+    This variable is set when ``<lang>`` is ``CUDA`` or ``HIP``.
+
+    If the check detects an explicit host compiler that is required for
+    compilation, this variable will be set to that compiler.
+    If the check detects that no explicit host compiler is needed,
+    this variable will be cleared.
+
+    If this variable is already set, its value is preserved only if
+    :variable:`CMAKE_<LANG>_COMPILER` is also set.
+    Otherwise, the check runs and overwrites
+    :variable:`CMAKE_<LANG>_HOST_COMPILER` with a new result.
+    Note that :variable:`CMAKE_<LANG>_HOST_COMPILER` documents it should
+    not be set without also setting
+    :variable:`CMAKE_<LANG>_COMPILER` to a NVCC compiler.
+
+For example:
+
+.. code-block:: cmake
 
   check_language(Fortran)
   if(CMAKE_Fortran_COMPILER)
@@ -46,7 +65,7 @@
     file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Check${lang})
 
     set(extra_compiler_variables)
-    if(${lang} STREQUAL CUDA AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+    if("${lang}" MATCHES "^(CUDA|HIP)$" AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
       set(extra_compiler_variables "set(CMAKE_CUDA_HOST_COMPILER \\\"\${CMAKE_CUDA_HOST_COMPILER}\\\")")
     endif()
 
diff --git a/Modules/Compiler/Clang-CXX.cmake b/Modules/Compiler/Clang-CXX.cmake
index a74e90b..c0d2356 100644
--- a/Modules/Compiler/Clang-CXX.cmake
+++ b/Modules/Compiler/Clang-CXX.cmake
@@ -32,7 +32,7 @@
 
 if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0)
   if("x${CMAKE_CXX_COMPILER_FRONTEND_VARIANT}" STREQUAL "xGNU")
-    string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+    string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
       "\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\""
       " -format=p1689"
       " --"
@@ -41,7 +41,8 @@
       " -MT <DYNDEP_FILE>"
       " -MD -MF <DEP_FILE>"
       " > <DYNDEP_FILE>")
-    set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang")
-    set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
+    set(CMAKE_CXX_MODULE_MAP_FORMAT "clang")
+    set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
+    set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "--precompile")
   endif()
 endif()
diff --git a/Modules/Compiler/CrayClang-C.cmake b/Modules/Compiler/CrayClang-C.cmake
new file mode 100644
index 0000000..bf878fc
--- /dev/null
+++ b/Modules/Compiler/CrayClang-C.cmake
@@ -0,0 +1,30 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Compiler/CrayClang)
+__compiler_cray_clang(C)
+
+set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
+
+string(APPEND CMAKE_C_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+string(APPEND CMAKE_C_FLAGS_RELEASE_INIT " -DNDEBUG")
+
+set(CMAKE_C90_STANDARD_COMPILE_OPTION  -std=c90)
+set(CMAKE_C90_EXTENSION_COMPILE_OPTION -std=gnu90)
+set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_C99_STANDARD_COMPILE_OPTION  -std=c99)
+set(CMAKE_C99_EXTENSION_COMPILE_OPTION -std=gnu99)
+set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_C11_STANDARD_COMPILE_OPTION  -std=c11)
+set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=gnu11)
+set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_C17_STANDARD_COMPILE_OPTION  -std=c17)
+set(CMAKE_C17_EXTENSION_COMPILE_OPTION -std=gnu17)
+
+set(CMAKE_C23_STANDARD_COMPILE_OPTION  -std=c2x)
+set(CMAKE_C23_EXTENSION_COMPILE_OPTION -std=gnu2x)
+
+__compiler_check_default_language_standard(C 15.0.0 17)
diff --git a/Modules/Compiler/CrayClang-CXX.cmake b/Modules/Compiler/CrayClang-CXX.cmake
new file mode 100644
index 0000000..de6a53c
--- /dev/null
+++ b/Modules/Compiler/CrayClang-CXX.cmake
@@ -0,0 +1,35 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+include(Compiler/CrayClang)
+__compiler_cray_clang(CXX)
+
+
+set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
+set(CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN "-fvisibility-inlines-hidden")
+
+string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL_INIT " -DNDEBUG")
+string(APPEND CMAKE_CXX_FLAGS_RELEASE_INIT " -DNDEBUG")
+
+set(CMAKE_CXX98_STANDARD_COMPILE_OPTION  -std=c++98)
+set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -std=gnu++98)
+set(CMAKE_CXX98_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_CXX11_STANDARD_COMPILE_OPTION  -std=c++11)
+set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -std=gnu++11)
+set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_CXX14_STANDARD_COMPILE_OPTION  -std=c++14)
+set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -std=gnu++14)
+set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_CXX17_STANDARD_COMPILE_OPTION  -std=c++17)
+set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION -std=gnu++17)
+
+set(CMAKE_CXX20_STANDARD_COMPILE_OPTION  -std=c++20)
+set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION -std=gnu++20)
+
+set(CMAKE_CXX23_STANDARD_COMPILE_OPTION  -std=c++2b)
+set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION -std=gnu++2b)
+
+__compiler_check_default_language_standard(CXX 15.0.0 14)
diff --git a/Modules/Compiler/CrayClang-DetermineCompiler.cmake b/Modules/Compiler/CrayClang-DetermineCompiler.cmake
new file mode 100644
index 0000000..1828444
--- /dev/null
+++ b/Modules/Compiler/CrayClang-DetermineCompiler.cmake
@@ -0,0 +1,8 @@
+set(_compiler_id_pp_test "defined(__clang__) && defined(__cray__)")
+
+set(_compiler_id_version_compute "
+# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__cray_major__)
+# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__cray_minor__)
+# define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__cray_patchlevel__)
+# define @PREFIX@COMPILER_VERSION_INTERNAL_STR __clang_version__
+")
diff --git a/Modules/Compiler/CrayClang.cmake b/Modules/Compiler/CrayClang.cmake
new file mode 100644
index 0000000..d2db9dd
--- /dev/null
+++ b/Modules/Compiler/CrayClang.cmake
@@ -0,0 +1,17 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# This module is shared by multiple languages; use include blocker.
+if(__COMPILER_CRAYCLANG)
+  return()
+endif()
+set(__COMPILER_CRAYCLANG 1)
+
+include(Compiler/Clang)
+
+macro (__compiler_cray_clang lang)
+  set(__crayclang_ver "${CMAKE_${lang}_COMPILER_VERSION}")
+  set("CMAKE_${lang}_COMPILER_VERSION" "${CMAKE_${lang}_COMPILER_VERSION_INTERNAL}")
+  __compiler_clang(${lang})
+  set("CMAKE_${lang}_COMPILER_VERSION" "${__crayclang_ver}")
+endmacro ()
diff --git a/Modules/Compiler/GNU-CXX.cmake b/Modules/Compiler/GNU-CXX.cmake
index b35f254..2e1b4ad 100644
--- a/Modules/Compiler/GNU-CXX.cmake
+++ b/Modules/Compiler/GNU-CXX.cmake
@@ -72,3 +72,24 @@
 endif()
 
 __compiler_check_default_language_standard(CXX 3.4 98 6.0 14 11.1 17)
+
+if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0)
+  string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
+    "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -E -x c++ <SOURCE>"
+    " -MT <DYNDEP_FILE> -MD -MF <DEP_FILE>"
+    " -fmodules-ts -fdeps-file=<DYNDEP_FILE> -fdeps-target=<OBJECT> -fdeps-format=p1689r5"
+    " -o <PREPROCESSED_SOURCE>")
+  set(CMAKE_CXX_MODULE_MAP_FORMAT "gcc")
+  string(CONCAT CMAKE_CXX_MODULE_MAP_FLAG
+    # Turn on modules.
+    "-fmodules-ts"
+    # Read the module mapper file.
+    " -fmodule-mapper=<MODULE_MAP_FILE>"
+    # Make sure dependency tracking is enabled (missing from `try_*`).
+    " -MD"
+    # Suppress `CXX_MODULES +=` from generated depfile snippets.
+    " -fdeps-format=p1689r5"
+    # Force C++ as a language.
+    " -x c++")
+  set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "-fmodule-only")
+endif()
diff --git a/Modules/Compiler/IBMClang-CXX.cmake b/Modules/Compiler/IBMClang-CXX.cmake
index 5431b17..be9b525 100644
--- a/Modules/Compiler/IBMClang-CXX.cmake
+++ b/Modules/Compiler/IBMClang-CXX.cmake
@@ -31,6 +31,8 @@
   set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++17")
   set(CMAKE_CXX20_STANDARD_COMPILE_OPTION  "-std=c++20")
   set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++20")
+  set(CMAKE_CXX23_STANDARD_COMPILE_OPTION  "-std=c++2b")
+  set(CMAKE_CXX23_EXTENSION_COMPILE_OPTION "-std=gnu++2b")
 endif()
 
 __compiler_check_default_language_standard(CXX 17.1.0 17)
diff --git a/Modules/Compiler/IntelLLVM.cmake b/Modules/Compiler/IntelLLVM.cmake
index e256c8f..f3c0bf4 100644
--- a/Modules/Compiler/IntelLLVM.cmake
+++ b/Modules/Compiler/IntelLLVM.cmake
@@ -44,6 +44,13 @@
 
     string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
     string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g")
+    if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 2023.0.0)
+      if("x${lang}" STREQUAL "xFortran")
+        string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -diag-disable:10440")
+      else()
+        string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -Rno-debug-disables-optimization")
+      endif()
+    endif()
     string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -Os")
     string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3")
     string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g")
diff --git a/Modules/Compiler/LLVMFlang-Fortran.cmake b/Modules/Compiler/LLVMFlang-Fortran.cmake
index 291413e..d27f094 100644
--- a/Modules/Compiler/LLVMFlang-Fortran.cmake
+++ b/Modules/Compiler/LLVMFlang-Fortran.cmake
@@ -1,5 +1,3 @@
-set(CMAKE_Fortran_VERBOSE_FLAG "-v")
-
 set(CMAKE_Fortran_SUBMODULE_SEP "-")
 set(CMAKE_Fortran_SUBMODULE_EXT ".mod")
 
@@ -15,6 +13,12 @@
 set(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF "-nocpp")
 set(CMAKE_Fortran_POSTPROCESS_FLAG "-ffixed-line-length-72")
 
-string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " -O0 -g")
-string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -O2 -g")
-string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3")
+set(CMAKE_Fortran_COMPILE_OPTIONS_TARGET "--target=")
+
+if(NOT "x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xMSVC")
+  set(CMAKE_Fortran_VERBOSE_FLAG "-v")
+
+  string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " -O0 -g")
+  string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " -O2 -g")
+  string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " -O3")
+endif()
diff --git a/Modules/Compiler/MSVC-CXX.cmake b/Modules/Compiler/MSVC-CXX.cmake
index 10a9073..79cd2e0 100644
--- a/Modules/Compiler/MSVC-CXX.cmake
+++ b/Modules/Compiler/MSVC-CXX.cmake
@@ -79,12 +79,13 @@
 endif()
 
 if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "19.34")
-  string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+  string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
     "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> <SOURCE> -nologo -TP"
     " -showIncludes"
     " -scanDependencies <DYNDEP_FILE>"
     " -Fo<OBJECT>")
-  set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_DEPFILE_FORMAT "msvc")
-  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "msvc")
-  set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
+  set(CMAKE_CXX_SCANDEP_DEPFILE_FORMAT "msvc")
+  set(CMAKE_CXX_MODULE_MAP_FORMAT "msvc")
+  set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
+  set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "-ifcOnly;-ifcOutput;<OBJECT>")
 endif ()
diff --git a/Modules/Compiler/NVIDIA-CUDA.cmake b/Modules/Compiler/NVIDIA-CUDA.cmake
index c839d1c..93ad182 100644
--- a/Modules/Compiler/NVIDIA-CUDA.cmake
+++ b/Modules/Compiler/NVIDIA-CUDA.cmake
@@ -1,10 +1,9 @@
-include(Compiler/CMakeCommonCompilerMacros)
+include(Compiler/NVIDIA)
+__compiler_nvidia_cxx_standards(CUDA)
+__compiler_nvidia_cuda_flags(CUDA)
 
 set(CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE True)
-set(CMAKE_CUDA_VERBOSE_FLAG "-v")
-set(CMAKE_CUDA_VERBOSE_COMPILE_FLAG "-Xcompiler=-v")
 
-set(_CMAKE_COMPILE_AS_CUDA_FLAG "-x cu")
 set(_CMAKE_CUDA_WHOLE_FLAG "-c")
 set(_CMAKE_CUDA_RDC_FLAG "-rdc=true")
 set(_CMAKE_CUDA_PTX_FLAG "-ptx")
@@ -14,162 +13,11 @@
   set(_CMAKE_CUDA_OPTIX_FLAG "-optix-ir")
 endif()
 
-if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
-  # The -forward-unknown-to-host-compiler flag was only
-  # added to nvcc in 10.2 so before that we had no good
-  # way to invoke the CUDA compiler and propagate unknown
-  # flags such as -pthread to the host compiler
-  set(_CMAKE_CUDA_EXTRA_FLAGS "-forward-unknown-to-host-compiler")
-else()
-  set(_CMAKE_CUDA_EXTRA_FLAGS "")
-endif()
-
-if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "8.0.0")
-  set(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS "-Wno-deprecated-gpu-targets")
-else()
-  set(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS "")
-endif()
-
-if(CMAKE_CUDA_HOST_COMPILER AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
-  string(APPEND _CMAKE_CUDA_EXTRA_FLAGS " -ccbin=<CMAKE_CUDA_HOST_COMPILER>")
-endif()
-
-if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
-  # Starting in 10.2, nvcc supported treating all warnings as errors
-  set(CMAKE_CUDA_COMPILE_OPTIONS_WARNING_AS_ERROR "-Werror" "all-warnings")
-endif()
-
-if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
-  # The -MD flag was only added to nvcc in 10.2 so
-  # before that we had to invoke the compiler twice
-  # to get header dependency information
-  set(CMAKE_DEPFILE_FLAGS_CUDA "-MD -MT <DEP_TARGET> -MF <DEP_FILE>")
-else()
-  set(CMAKE_CUDA_DEPENDS_EXTRA_COMMANDS "<CMAKE_CUDA_COMPILER> ${_CMAKE_CUDA_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> ${_CMAKE_COMPILE_AS_CUDA_FLAG} -M <SOURCE> -MT <OBJECT> -o <DEP_FILE>")
-endif()
-set(CMAKE_CUDA_DEPFILE_FORMAT gcc)
-if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
-    AND CMAKE_GENERATOR MATCHES "Makefiles|WMake")
-  set(CMAKE_CUDA_DEPENDS_USE_COMPILER TRUE)
-endif()
-
 if(CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL 11.2)
-  set(_CMAKE_CUDA_IPO_SUPPORTED_BY_CMAKE YES)
-  set(_CMAKE_CUDA_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
-
   set(CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO " -dlto")
 endif()
 
-if(NOT "x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC")
-  set(CMAKE_CUDA_COMPILE_OPTIONS_PIE -Xcompiler=-fPIE)
-  set(CMAKE_CUDA_COMPILE_OPTIONS_PIC -Xcompiler=-fPIC)
-  set(CMAKE_CUDA_COMPILE_OPTIONS_VISIBILITY -Xcompiler=-fvisibility=)
-  # CMAKE_SHARED_LIBRARY_CUDA_FLAGS is sent to the host linker so we
-  # don't need to forward it through nvcc.
-  set(CMAKE_SHARED_LIBRARY_CUDA_FLAGS -fPIC)
-  string(APPEND CMAKE_CUDA_FLAGS_INIT " ")
-  string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -g")
-  string(APPEND CMAKE_CUDA_FLAGS_RELEASE_INIT " -O3 -DNDEBUG")
-  string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG")
-  string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG")
-endif()
-
-set(CMAKE_SHARED_LIBRARY_CREATE_CUDA_FLAGS -shared)
-set(CMAKE_INCLUDE_SYSTEM_FLAG_CUDA "-isystem ")
-
-if (CMAKE_CUDA_SIMULATE_ID STREQUAL "GNU")
-  set(CMAKE_CUDA_LINKER_WRAPPER_FLAG "-Wl,")
-  set(CMAKE_CUDA_LINKER_WRAPPER_FLAG_SEP ",")
-elseif(CMAKE_CUDA_SIMULATE_ID STREQUAL "Clang")
-  set(CMAKE_CUDA_LINKER_WRAPPER_FLAG "-Xlinker" " ")
-  set(CMAKE_CUDA_LINKER_WRAPPER_FLAG_SEP)
-endif()
-
 set(CMAKE_CUDA_DEVICE_COMPILER_WRAPPER_FLAG "-Xcompiler=")
 set(CMAKE_CUDA_DEVICE_COMPILER_WRAPPER_FLAG_SEP ",")
 set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG "-Xlinker=")
 set(CMAKE_CUDA_DEVICE_LINKER_WRAPPER_FLAG_SEP ",")
-
-set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC  "cudadevrt;cudart_static")
-set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_SHARED  "cudadevrt;cudart")
-set(CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_NONE    "")
-
-if(UNIX AND NOT (CMAKE_SYSTEM_NAME STREQUAL "QNX"))
-  list(APPEND CMAKE_CUDA_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "rt" "pthread" "dl")
-endif()
-
-if("x${CMAKE_CUDA_SIMULATE_ID}" STREQUAL "xMSVC")
-  # MSVC requires c++14 as the minimum level
-  set(CMAKE_CUDA03_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CUDA03_EXTENSION_COMPILE_OPTION "")
-
-  # MSVC requires c++14 as the minimum level
-  set(CMAKE_CUDA11_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CUDA11_EXTENSION_COMPILE_OPTION "")
-
-  if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 9.0)
-    if(CMAKE_CUDA_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.10.25017)
-      set(CMAKE_CUDA14_STANDARD_COMPILE_OPTION "-std=c++14")
-      set(CMAKE_CUDA14_EXTENSION_COMPILE_OPTION "-std=c++14")
-    else()
-      set(CMAKE_CUDA14_STANDARD_COMPILE_OPTION "")
-      set(CMAKE_CUDA14_EXTENSION_COMPILE_OPTION "")
-    endif()
-  endif()
-
-  if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 11.0)
-    if(CMAKE_CUDA_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505)
-      set(CMAKE_CUDA17_STANDARD_COMPILE_OPTION "-std=c++17")
-      set(CMAKE_CUDA17_EXTENSION_COMPILE_OPTION "-std=c++17")
-    endif()
-  endif()
-
-  if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 12.0)
-    if(CMAKE_CUDA_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505)
-      set(CMAKE_CUDA20_STANDARD_COMPILE_OPTION "-std=c++20")
-      set(CMAKE_CUDA20_EXTENSION_COMPILE_OPTION "-std=c++20")
-    endif()
-  endif()
-
-else()
-  set(CMAKE_CUDA03_STANDARD_COMPILE_OPTION "")
-  set(CMAKE_CUDA03_EXTENSION_COMPILE_OPTION "")
-
-  set(CMAKE_CUDA11_STANDARD_COMPILE_OPTION "-std=c++11")
-  set(CMAKE_CUDA11_EXTENSION_COMPILE_OPTION "-std=c++11")
-
-  if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 9.0)
-    set(CMAKE_CUDA03_STANDARD_COMPILE_OPTION "-std=c++03")
-    set(CMAKE_CUDA03_EXTENSION_COMPILE_OPTION "-std=c++03")
-    set(CMAKE_CUDA14_STANDARD_COMPILE_OPTION "-std=c++14")
-    set(CMAKE_CUDA14_EXTENSION_COMPILE_OPTION "-std=c++14")
-  endif()
-
-  if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 11.0)
-    set(CMAKE_CUDA17_STANDARD_COMPILE_OPTION "-std=c++17")
-    set(CMAKE_CUDA17_EXTENSION_COMPILE_OPTION "-std=c++17")
-  endif()
-
-  if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 12.0)
-    set(CMAKE_CUDA20_STANDARD_COMPILE_OPTION "-std=c++20")
-    set(CMAKE_CUDA20_EXTENSION_COMPILE_OPTION "-std=c++20")
-  endif()
-
-endif()
-
-if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0")
-  set(CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG "--options-file ")
-  set(CMAKE_CUDA_RESPONSE_FILE_FLAG "--options-file ")
-endif()
-
-if (CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0")
-  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 1)
-  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
-  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 1)
-else()
-  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES 0)
-  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
-  set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS 0)
-endif()
-
-__compiler_check_default_language_standard(CUDA 6.0 03)
diff --git a/Modules/Compiler/NVIDIA-HIP.cmake b/Modules/Compiler/NVIDIA-HIP.cmake
new file mode 100644
index 0000000..c888bc7
--- /dev/null
+++ b/Modules/Compiler/NVIDIA-HIP.cmake
@@ -0,0 +1,14 @@
+include(Compiler/NVIDIA)
+__compiler_nvidia_cxx_standards(HIP)
+__compiler_nvidia_cuda_flags(HIP)
+
+# The ROCm hip-lang cmake package's device runtime library is not needed for NVIDIA GPUs.
+set(_CMAKE_HIP_DEVICE_RUNTIME_TARGET "")
+
+set(CMAKE_HIP_STANDARD_INCLUDE_DIRECTORIES "${CMAKE_HIP_COMPILER_ROCM_ROOT}/include")
+
+set(CMAKE_HIP_LINK_EXECUTABLE
+  "<CMAKE_HIP_HOST_LINK_LAUNCHER> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>${__IMPLICIT_LINKS}")
+set(CMAKE_HIP_CREATE_SHARED_LIBRARY
+  "<CMAKE_HIP_HOST_LINK_LAUNCHER> <CMAKE_SHARED_LIBRARY_HIP_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_HIP_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>${__IMPLICIT_LINKS}")
+set(CMAKE_HIP_CREATE_SHARED_MODULE "${CMAKE_HIP_CREATE_SHARED_LIBRARY}")
diff --git a/Modules/Compiler/NVIDIA.cmake b/Modules/Compiler/NVIDIA.cmake
new file mode 100644
index 0000000..a126c57
--- /dev/null
+++ b/Modules/Compiler/NVIDIA.cmake
@@ -0,0 +1,168 @@
+
+# This module is shared by multiple languages; use include blocker.
+if(__COMPILER_NVIDIA)
+  return()
+endif()
+set(__COMPILER_NVIDIA 1)
+
+include(Compiler/CMakeCommonCompilerMacros)
+
+macro(__compiler_nvidia_cxx_standards lang)
+  if("x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC")
+    # MSVC requires c++14 as the minimum level
+    set(CMAKE_${lang}03_STANDARD_COMPILE_OPTION "")
+    set(CMAKE_${lang}03_EXTENSION_COMPILE_OPTION "")
+
+    # MSVC requires c++14 as the minimum level
+    set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "")
+    set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "")
+
+    if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 9.0)
+      if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.10.25017)
+        set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++14")
+        set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=c++14")
+      else()
+        set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "")
+        set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "")
+      endif()
+    endif()
+
+    if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 11.0)
+      if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505)
+        set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++17")
+        set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=c++17")
+      endif()
+    endif()
+
+    if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 12.0)
+      if(CMAKE_${lang}_SIMULATE_VERSION VERSION_GREATER_EQUAL 19.11.25505)
+        set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++20")
+        set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=c++20")
+      endif()
+    endif()
+  else()
+    set(CMAKE_${lang}03_STANDARD_COMPILE_OPTION "")
+    set(CMAKE_${lang}03_EXTENSION_COMPILE_OPTION "")
+
+    set(CMAKE_${lang}11_STANDARD_COMPILE_OPTION "-std=c++11")
+    set(CMAKE_${lang}11_EXTENSION_COMPILE_OPTION "-std=c++11")
+
+    if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 9.0)
+      set(CMAKE_${lang}03_STANDARD_COMPILE_OPTION "-std=c++03")
+      set(CMAKE_${lang}03_EXTENSION_COMPILE_OPTION "-std=c++03")
+      set(CMAKE_${lang}14_STANDARD_COMPILE_OPTION "-std=c++14")
+      set(CMAKE_${lang}14_EXTENSION_COMPILE_OPTION "-std=c++14")
+    endif()
+
+    if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 11.0)
+      set(CMAKE_${lang}17_STANDARD_COMPILE_OPTION "-std=c++17")
+      set(CMAKE_${lang}17_EXTENSION_COMPILE_OPTION "-std=c++17")
+    endif()
+
+    if (NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 12.0)
+      set(CMAKE_${lang}20_STANDARD_COMPILE_OPTION "-std=c++20")
+      set(CMAKE_${lang}20_EXTENSION_COMPILE_OPTION "-std=c++20")
+    endif()
+  endif()
+
+  __compiler_check_default_language_standard(${lang} 6.0 03)
+endmacro()
+
+macro(__compiler_nvidia_cuda_flags lang)
+  set(CMAKE_${lang}_VERBOSE_FLAG "-v")
+  set(CMAKE_${lang}_VERBOSE_COMPILE_FLAG "-Xcompiler=-v")
+  set(_CMAKE_COMPILE_AS_${lang}_FLAG "-x cu")
+
+  if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
+    # The -forward-unknown-to-host-compiler flag was only
+    # added to nvcc in 10.2 so before that we had no good
+    # way to invoke the NVCC compiler and propagate unknown
+    # flags such as -pthread to the host compiler
+    set(_CMAKE_${lang}_EXTRA_FLAGS "-forward-unknown-to-host-compiler")
+  else()
+    set(_CMAKE_${lang}_EXTRA_FLAGS "")
+  endif()
+
+  if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "8.0.0")
+    set(_CMAKE_${lang}_EXTRA_DEVICE_LINK_FLAGS "-Wno-deprecated-gpu-targets")
+  else()
+    set(_CMAKE_${lang}_EXTRA_DEVICE_LINK_FLAGS "")
+  endif()
+
+  if(CMAKE_${lang}_HOST_COMPILER AND NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+    string(APPEND _CMAKE_${lang}_EXTRA_FLAGS " -ccbin=<CMAKE_${lang}_HOST_COMPILER>")
+  endif()
+
+  if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
+    # Starting in 10.2, nvcc supported treating all warnings as errors
+    set(CMAKE_${lang}_COMPILE_OPTIONS_WARNING_AS_ERROR "-Werror" "all-warnings")
+  endif()
+
+  set(CMAKE_${lang}_DEPFILE_FORMAT gcc)
+  if((NOT DEFINED CMAKE_DEPENDS_USE_COMPILER OR CMAKE_DEPENDS_USE_COMPILER)
+      AND CMAKE_GENERATOR MATCHES "Makefiles|WMake")
+    set(CMAKE_${lang}_DEPENDS_USE_COMPILER TRUE)
+  endif()
+
+  if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 10.2.89)
+    # The -MD flag was only added to nvcc in 10.2 so
+    # before that we had to invoke the compiler twice
+    # to get header dependency information
+    set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT <DEP_TARGET> -MF <DEP_FILE>")
+  else()
+    set(CMAKE_${lang}_DEPENDS_EXTRA_COMMANDS "<CMAKE_${lang}_COMPILER> ${_CMAKE_${lang}_EXTRA_FLAGS} <DEFINES> <INCLUDES> <FLAGS> ${_CMAKE_COMPILE_AS_${lang}_FLAG} -M <SOURCE> -MT <OBJECT> -o <DEP_FILE>")
+  endif()
+
+  if(CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 11.2)
+    set(_CMAKE_${lang}_IPO_SUPPORTED_BY_CMAKE YES)
+    set(_CMAKE_${lang}_IPO_MAY_BE_SUPPORTED_BY_COMPILER YES)
+  endif()
+
+  if(NOT "x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_PIE -Xcompiler=-fPIE)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_PIC -Xcompiler=-fPIC)
+    set(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY -Xcompiler=-fvisibility=)
+    # CMAKE_SHARED_LIBRARY_${lang}_FLAGS is sent to the host linker so we
+    # don't need to forward it through nvcc.
+    set(CMAKE_SHARED_LIBRARY_${lang}_FLAGS -fPIC)
+    string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
+    string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g")
+    string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG")
+    string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG")
+  endif()
+
+  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS -shared)
+  set(CMAKE_INCLUDE_SYSTEM_FLAG_${lang} "-isystem ")
+
+  if (CMAKE_${lang}_SIMULATE_ID STREQUAL "GNU")
+    set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Wl,")
+    set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP ",")
+  elseif(CMAKE_${lang}_SIMULATE_ID STREQUAL "Clang")
+    set(CMAKE_${lang}_LINKER_WRAPPER_FLAG "-Xlinker" " ")
+    set(CMAKE_${lang}_LINKER_WRAPPER_FLAG_SEP)
+  endif()
+
+  set(CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC  "cudadevrt;cudart_static")
+  set(CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_SHARED  "cudadevrt;cudart")
+  set(CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_NONE    "")
+
+  if(UNIX AND NOT (CMAKE_SYSTEM_NAME STREQUAL "QNX"))
+    list(APPEND CMAKE_${lang}_RUNTIME_LIBRARY_LINK_OPTIONS_STATIC "rt" "pthread" "dl")
+  endif()
+
+  if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "9.0")
+    set(CMAKE_${lang}_RESPONSE_FILE_DEVICE_LINK_FLAG "--options-file ")
+    set(CMAKE_${lang}_RESPONSE_FILE_FLAG "--options-file ")
+  endif()
+
+  if (CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0")
+    set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 1)
+    set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 1)
+    set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 1)
+  else()
+    set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_INCLUDES 0)
+    set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_LIBRARIES 0)
+    set(CMAKE_${lang}_USE_RESPONSE_FILE_FOR_OBJECTS 0)
+  endif()
+endmacro()
diff --git a/Modules/Compiler/OrangeC-ASM.cmake b/Modules/Compiler/OrangeC-ASM.cmake
new file mode 100644
index 0000000..fe78911
--- /dev/null
+++ b/Modules/Compiler/OrangeC-ASM.cmake
@@ -0,0 +1,7 @@
+include(Compiler/OrangeC)
+__compiler_orangec(ASM)
+
+set(CMAKE_ASM_OUTPUT_EXTENSION ".o")
+set(CMAKE_ASM_VERBOSE_FLAG "-yyyyy")
+
+set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;S;asm;nas)
diff --git a/Modules/Compiler/OrangeC-C.cmake b/Modules/Compiler/OrangeC-C.cmake
new file mode 100644
index 0000000..15a6476
--- /dev/null
+++ b/Modules/Compiler/OrangeC-C.cmake
@@ -0,0 +1,20 @@
+include(Compiler/OrangeC)
+include(Compiler/CMakeCommonCompilerMacros)
+
+set(CMAKE_C_OUTPUT_EXTENSION ".o")
+set(CMAKE_C_VERBOSE_FLAG "-yyyyy")
+set(CMAKE_C_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c)
+
+set(CMAKE_C90_STANDARD_COMPILE_OPTION -std=c89)
+set(CMAKE_C90_EXTENSION_COMPILE_OPTION -std=c89)
+set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
+set(CMAKE_C99_STANDARD_COMPILE_OPTION -std=c99)
+set(CMAKE_C99_EXTENSION_COMPILE_OPTION -std=c99)
+set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
+set(CMAKE_C11_STANDARD_COMPILE_OPTION -std=c11)
+set(CMAKE_C11_EXTENSION_COMPILE_OPTION -std=c11)
+set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
+
+__compiler_orangec(C)
+#- 6.38 is the earliest version which version info is available in the preprocessor
+__compiler_check_default_language_standard(C 6.38 11)
diff --git a/Modules/Compiler/OrangeC-CXX.cmake b/Modules/Compiler/OrangeC-CXX.cmake
new file mode 100644
index 0000000..3f9d59c
--- /dev/null
+++ b/Modules/Compiler/OrangeC-CXX.cmake
@@ -0,0 +1,25 @@
+include(Compiler/OrangeC)
+include(Compiler/CMakeCommonCompilerMacros)
+
+set(_ORANGEC_COMPILE_CXX " -x c++")
+set(CMAKE_CXX_COMPILE_OPTIONS_EXPLICIT_LANGUAGE -x c++)
+
+set(CMAKE_CXX_OUTPUT_EXTENSION ".o")
+set(CMAKE_CXX_VERBOSE_FLAG "-yyyyy")
+
+
+
+#- OrangeC is a little lax when accepting compiler version specifications.
+#  Usually changing the version only changes the value of __cplusplus.
+#  Also we don't support CXX98
+set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "-std=c++11")
+set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION "-std=c++11")
+set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON)
+
+set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14")
+set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=c++14")
+set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON)
+
+__compiler_orangec(CXX)
+#- 6.38 is the earliest version which version info is available in the preprocessor
+__compiler_check_default_language_standard(CXX 6.38 14)
diff --git a/Modules/Compiler/OrangeC-DetermineCompiler.cmake b/Modules/Compiler/OrangeC-DetermineCompiler.cmake
new file mode 100644
index 0000000..2ecc140
--- /dev/null
+++ b/Modules/Compiler/OrangeC-DetermineCompiler.cmake
@@ -0,0 +1,7 @@
+
+set(_compiler_id_pp_test "defined(__ORANGEC__)")
+
+set(_compiler_id_version_compute "
+# define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@(__ORANGEC_MAJOR__)
+# define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(__ORANGEC_MINOR__)
+# define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__ORANGEC_PATCHLEVEL__)")
diff --git a/Modules/Compiler/OrangeC.cmake b/Modules/Compiler/OrangeC.cmake
new file mode 100644
index 0000000..fbb245b
--- /dev/null
+++ b/Modules/Compiler/OrangeC.cmake
@@ -0,0 +1,33 @@
+include_guard()
+
+macro(__compiler_orangec lang)
+  if ("x${lang}" MATCHES "^x(C|CXX)$")
+    set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${_ORANGEC_COMPILE_${lang}} -! <SOURCE> <DEFINES> <INCLUDES> <FLAGS> +i -o <PREPROCESSED_SOURCE>")
+    set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE     "<CMAKE_${lang}_COMPILER> ${_ORANGEC_COMPILE_${lang}} -! <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -S -o <ASSEMBLY_SOURCE>")
+  endif()
+  set(CMAKE_${lang}_COMPILE_OBJECT             "<CMAKE_${lang}_COMPILER> ${_ORANGEC_COMPILE_${lang}} -! -c <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>")
+  unset(_ORANGEC_COMPILE_${lang})
+
+  set(CMAKE_DEPFILE_FLAGS_${lang} "-MD -MT <DEP_TARGET> -MF <DEP_FILE>")
+  set(CMAKE_${lang}_DEPFILE_FORMAT gcc)
+  set(CMAKE_${lang}_DEPENDS_USE_COMPILER TRUE)
+
+  string(APPEND CMAKE_${lang}_FLAGS_INIT " ")
+  string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " -g")
+  string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O2 -DNDEBUG")
+  string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " -O1 -DNDEBUG")
+  string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -g -DNDEBUG")
+
+  set(CMAKE_${lang}_CREATE_STATIC_LIBRARY
+    "<CMAKE_${lang}_COMPILER> -! -static -o <TARGET> <LINK_FLAGS> <OBJECTS> ")
+  set(CMAKE_${lang}_LINK_EXECUTABLE "<CMAKE_${lang}_COMPILER> -! <FLAGS> -o <TARGET> --out-implib <TARGET_IMPLIB> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> <LINK_LIBRARIES>")
+  set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
+    "<CMAKE_${lang}_COMPILER> -! <FLAGS> -o <TARGET> --out-implib <TARGET_IMPLIB> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <OBJECTS> <LINK_LIBRARIES>")
+  set(CMAKE_${lang}_CREATE_SHARED_MODULE "${CMAKE_${lang}_CREATE_SHARED_LIBRARY}")
+
+  set(CMAKE_LIBRARY_PATH_FLAG "-L")
+  set(CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS "-! -shared")
+
+  set(CMAKE_${lang}_RESPONSE_FILE_FLAG "@")
+  set(CMAKE_${lang}_RESPONSE_FILE_LINK_FLAG "@")
+endmacro()
diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake
index 605908d..f43f48d 100644
--- a/Modules/ExternalProject.cmake
+++ b/Modules/ExternalProject.cmake
@@ -11,11 +11,8 @@
 
    .. contents::
 
-Commands
-^^^^^^^^
-
 External Project Definition
-"""""""""""""""""""""""""""
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. command:: ExternalProject_Add
 
@@ -34,866 +31,908 @@
   customized. The function supports a large number of options which can be used
   to tailor the external project behavior.
 
-  **Directory Options:**
-    Most of the time, the default directory layout is sufficient. It is largely
-    an implementation detail that the main project usually doesn't need to
-    change. In some circumstances, however, control over the directory layout
-    can be useful or necessary. The directory options are potentially more
-    useful from the point of view that the main build can use the
-    :command:`ExternalProject_Get_Property` command to retrieve their values,
-    thereby allowing the main project to refer to build artifacts of the
-    external project.
+Directory Options
+"""""""""""""""""
 
-    ``PREFIX <dir>``
-      Root directory for the external project. Unless otherwise noted below,
-      all other directories associated with the external project will be
-      created under here.
+Most of the time, the default directory layout is sufficient. It is largely
+an implementation detail that the main project usually doesn't need to
+change. In some circumstances, however, control over the directory layout
+can be useful or necessary. The directory options are potentially more
+useful from the point of view that the main build can use the
+:command:`ExternalProject_Get_Property` command to retrieve their values,
+thereby allowing the main project to refer to build artifacts of the
+external project.
 
-    ``TMP_DIR <dir>``
-      Directory in which to store temporary files.
+``PREFIX <dir>``
+  Root directory for the external project. Unless otherwise noted below,
+  all other directories associated with the external project will be
+  created under here.
 
-    ``STAMP_DIR <dir>``
-      Directory in which to store the timestamps of each step. Log files from
-      individual steps are also created in here unless overridden by LOG_DIR
-      (see *Logging Options* below).
+``TMP_DIR <dir>``
+  Directory in which to store temporary files.
 
-    ``LOG_DIR <dir>``
-      .. versionadded:: 3.14
+``STAMP_DIR <dir>``
+  Directory in which to store the timestamps of each step. Log files from
+  individual steps are also created in here unless overridden by LOG_DIR
+  (see *Logging Options* below).
 
-      Directory in which to store the logs of each step.
+``LOG_DIR <dir>``
+  .. versionadded:: 3.14
 
-    ``DOWNLOAD_DIR <dir>``
-      Directory in which to store downloaded files before unpacking them. This
-      directory is only used by the URL download method, all other download
-      methods use ``SOURCE_DIR`` directly instead.
+  Directory in which to store the logs of each step.
 
-    ``SOURCE_DIR <dir>``
-      Source directory into which downloaded contents will be unpacked, or for
-      non-URL download methods, the directory in which the repository should be
-      checked out, cloned, etc. If no download method is specified, this must
-      point to an existing directory where the external project has already
-      been unpacked or cloned/checked out.
+``DOWNLOAD_DIR <dir>``
+  Directory in which to store downloaded files before unpacking them. This
+  directory is only used by the URL download method, all other download
+  methods use ``SOURCE_DIR`` directly instead.
 
-      .. note::
-         If a download method is specified, any existing contents of the source
-         directory may be deleted. Only the URL download method checks whether
-         this directory is either missing or empty before initiating the
-         download, stopping with an error if it is not empty. All other
-         download methods silently discard any previous contents of the source
-         directory.
+``SOURCE_DIR <dir>``
+  Source directory into which downloaded contents will be unpacked, or for
+  non-URL download methods, the directory in which the repository should be
+  checked out, cloned, etc. If no download method is specified, this must
+  point to an existing directory where the external project has already
+  been unpacked or cloned/checked out.
 
-    ``BINARY_DIR <dir>``
-      Specify the build directory location. This option is ignored if
-      ``BUILD_IN_SOURCE`` is enabled.
+  .. note::
+      If a download method is specified, any existing contents of the source
+      directory may be deleted. Only the URL download method checks whether
+      this directory is either missing or empty before initiating the
+      download, stopping with an error if it is not empty. All other
+      download methods silently discard any previous contents of the source
+      directory.
 
-    ``INSTALL_DIR <dir>``
-      Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder.
-      This does not actually configure the external project to install to
-      the given prefix. That must be done by passing appropriate arguments
-      to the external project configuration step, e.g. using ``<INSTALL_DIR>``.
+``BINARY_DIR <dir>``
+  Specify the build directory location. This option is ignored if
+  ``BUILD_IN_SOURCE`` is enabled.
 
-    If any of the above ``..._DIR`` options are not specified, their defaults
-    are computed as follows. If the ``PREFIX`` option is given or the
-    ``EP_PREFIX`` directory property is set, then an external project is built
-    and installed under the specified prefix::
+``INSTALL_DIR <dir>``
+  Installation prefix to be placed in the ``<INSTALL_DIR>`` placeholder.
+  This does not actually configure the external project to install to
+  the given prefix. That must be done by passing appropriate arguments
+  to the external project configuration step, e.g. using ``<INSTALL_DIR>``.
 
-      TMP_DIR      = <prefix>/tmp
-      STAMP_DIR    = <prefix>/src/<name>-stamp
-      DOWNLOAD_DIR = <prefix>/src
-      SOURCE_DIR   = <prefix>/src/<name>
-      BINARY_DIR   = <prefix>/src/<name>-build
-      INSTALL_DIR  = <prefix>
-      LOG_DIR      = <STAMP_DIR>
+If any of the above ``..._DIR`` options are not specified, their defaults
+are computed as follows. If the ``PREFIX`` option is given or the
+``EP_PREFIX`` directory property is set, then an external project is built
+and installed under the specified prefix::
 
-    Otherwise, if the ``EP_BASE`` directory property is set then components
-    of an external project are stored under the specified base::
+  TMP_DIR      = <prefix>/tmp
+  STAMP_DIR    = <prefix>/src/<name>-stamp
+  DOWNLOAD_DIR = <prefix>/src
+  SOURCE_DIR   = <prefix>/src/<name>
+  BINARY_DIR   = <prefix>/src/<name>-build
+  INSTALL_DIR  = <prefix>
+  LOG_DIR      = <STAMP_DIR>
 
-      TMP_DIR      = <base>/tmp/<name>
-      STAMP_DIR    = <base>/Stamp/<name>
-      DOWNLOAD_DIR = <base>/Download/<name>
-      SOURCE_DIR   = <base>/Source/<name>
-      BINARY_DIR   = <base>/Build/<name>
-      INSTALL_DIR  = <base>/Install/<name>
-      LOG_DIR      = <STAMP_DIR>
+Otherwise, if the ``EP_BASE`` directory property is set then components
+of an external project are stored under the specified base::
 
-    If no ``PREFIX``, ``EP_PREFIX``, or ``EP_BASE`` is specified, then the
-    default is to set ``PREFIX`` to ``<name>-prefix``. Relative paths are
-    interpreted with respect to :variable:`CMAKE_CURRENT_BINARY_DIR` at the
-    point where ``ExternalProject_Add()`` is called.
+  TMP_DIR      = <base>/tmp/<name>
+  STAMP_DIR    = <base>/Stamp/<name>
+  DOWNLOAD_DIR = <base>/Download/<name>
+  SOURCE_DIR   = <base>/Source/<name>
+  BINARY_DIR   = <base>/Build/<name>
+  INSTALL_DIR  = <base>/Install/<name>
+  LOG_DIR      = <STAMP_DIR>
 
-  **Download Step Options:**
-    A download method can be omitted if the ``SOURCE_DIR`` option is used to
-    point to an existing non-empty directory. Otherwise, one of the download
-    methods below must be specified (multiple download methods should not be
-    given) or a custom ``DOWNLOAD_COMMAND`` provided.
+If no ``PREFIX``, ``EP_PREFIX``, or ``EP_BASE`` is specified, then the
+default is to set ``PREFIX`` to ``<name>-prefix``. Relative paths are
+interpreted with respect to :variable:`CMAKE_CURRENT_BINARY_DIR` at the
+point where ``ExternalProject_Add()`` is called.
 
-    ``DOWNLOAD_COMMAND <cmd>...``
-      Overrides the command used for the download step
-      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
-      supported). If this option is specified, all other download options will
-      be ignored. Providing an empty string for ``<cmd>`` effectively disables
-      the download step.
+Download Step Options
+"""""""""""""""""""""
 
-    *URL Download*
-      ``URL <url1> [<url2>...]``
-        List of paths and/or URL(s) of the external project's source. When more
-        than one URL is given, they are tried in turn until one succeeds. A URL
-        may be an ordinary path in the local file system (in which case it
-        must be the only URL provided) or any downloadable URL supported by the
-        :command:`file(DOWNLOAD)` command. A local filesystem path may refer to
-        either an existing directory or to an archive file, whereas a URL is
-        expected to point to a file which can be treated as an archive. When an
-        archive is used, it will be unpacked automatically unless the
-        ``DOWNLOAD_NO_EXTRACT`` option is set to prevent it. The archive type
-        is determined by inspecting the actual content rather than using logic
-        based on the file extension.
+A download method can be omitted if the ``SOURCE_DIR`` option is used to
+point to an existing non-empty directory. Otherwise, one of the download
+methods below must be specified (multiple download methods should not be
+given) or a custom ``DOWNLOAD_COMMAND`` provided.
 
-        .. versionchanged:: 3.7
-          Multiple URLs are allowed.
+``DOWNLOAD_COMMAND <cmd>...``
+  Overrides the command used for the download step
+  (:manual:`generator expressions <cmake-generator-expressions(7)>` are
+  supported). If this option is specified, all other download options will
+  be ignored. Providing an empty string for ``<cmd>`` effectively disables
+  the download step.
 
-      ``URL_HASH <algo>=<hashValue>``
-        Hash of the archive file to be downloaded. The argument should be of
-        the form ``<algo>=<hashValue>`` where ``algo`` can be any of the hashing
-        algorithms supported by the :command:`file()` command. Specifying this
-        option is strongly recommended for URL downloads, as it ensures the
-        integrity of the downloaded content. It is also used as a check for a
-        previously downloaded file, allowing connection to the remote location
-        to be avoided altogether if the local directory already has a file from
-        an earlier download that matches the specified hash.
+URL
+~~~
 
-      ``URL_MD5 <md5>``
-        Equivalent to ``URL_HASH MD5=<md5>``.
+``URL <url1> [<url2>...]``
+  List of paths and/or URL(s) of the external project's source. When more
+  than one URL is given, they are tried in turn until one succeeds. A URL
+  may be an ordinary path in the local file system (in which case it
+  must be the only URL provided) or any downloadable URL supported by the
+  :command:`file(DOWNLOAD)` command. A local filesystem path may refer to
+  either an existing directory or to an archive file, whereas a URL is
+  expected to point to a file which can be treated as an archive. When an
+  archive is used, it will be unpacked automatically unless the
+  ``DOWNLOAD_NO_EXTRACT`` option is set to prevent it. The archive type
+  is determined by inspecting the actual content rather than using logic
+  based on the file extension.
 
-      ``DOWNLOAD_NAME <fname>``
-        File name to use for the downloaded file. If not given, the end of the
-        URL is used to determine the file name. This option is rarely needed,
-        the default name is generally suitable and is not normally used outside
-        of code internal to the ``ExternalProject`` module.
+  .. versionchanged:: 3.7
+    Multiple URLs are allowed.
 
-      ``DOWNLOAD_EXTRACT_TIMESTAMP <bool>``
-        .. versionadded:: 3.24
+``URL_HASH <algo>=<hashValue>``
+  Hash of the archive file to be downloaded. The argument should be of
+  the form ``<algo>=<hashValue>`` where ``algo`` can be any of the hashing
+  algorithms supported by the :command:`file()` command. Specifying this
+  option is strongly recommended for URL downloads, as it ensures the
+  integrity of the downloaded content. It is also used as a check for a
+  previously downloaded file, allowing connection to the remote location
+  to be avoided altogether if the local directory already has a file from
+  an earlier download that matches the specified hash.
 
-        When specified with a true value, the timestamps of the extracted
-        files will match those in the archive. When false, the timestamps of
-        the extracted files will reflect the time at which the extraction
-        was performed. If the download URL changes, timestamps based off
-        those in the archive can result in dependent targets not being rebuilt
-        when they potentially should have been. Therefore, unless the file
-        timestamps are significant to the project in some way, use a false
-        value for this option. If ``DOWNLOAD_EXTRACT_TIMESTAMP`` is not given,
-        the default is false. See policy :policy:`CMP0135`.
+``URL_MD5 <md5>``
+  Equivalent to ``URL_HASH MD5=<md5>``.
 
-      ``DOWNLOAD_NO_EXTRACT <bool>``
-        .. versionadded:: 3.6
+``DOWNLOAD_NAME <fname>``
+  File name to use for the downloaded file. If not given, the end of the
+  URL is used to determine the file name. This option is rarely needed,
+  the default name is generally suitable and is not normally used outside
+  of code internal to the ``ExternalProject`` module.
 
-        Allows the extraction part of the download step to be disabled by
-        passing a boolean true value for this option. If this option is not
-        given, the downloaded contents will be unpacked automatically if
-        required. If extraction has been disabled, the full path to the
-        downloaded file is available as ``<DOWNLOADED_FILE>`` in subsequent
-        steps or as the property ``DOWNLOADED_FILE`` with the
-        :command:`ExternalProject_Get_Property` command.
+``DOWNLOAD_EXTRACT_TIMESTAMP <bool>``
+  .. versionadded:: 3.24
 
-      ``DOWNLOAD_NO_PROGRESS <bool>``
-        Can be used to disable logging the download progress. If this option is
-        not given, download progress messages will be logged.
+  When specified with a true value, the timestamps of the extracted
+  files will match those in the archive. When false, the timestamps of
+  the extracted files will reflect the time at which the extraction
+  was performed. If the download URL changes, timestamps based off
+  those in the archive can result in dependent targets not being rebuilt
+  when they potentially should have been. Therefore, unless the file
+  timestamps are significant to the project in some way, use a false
+  value for this option. If ``DOWNLOAD_EXTRACT_TIMESTAMP`` is not given,
+  the default is false. See policy :policy:`CMP0135`.
 
-      ``TIMEOUT <seconds>``
-        Maximum time allowed for file download operations.
+``DOWNLOAD_NO_EXTRACT <bool>``
+  .. versionadded:: 3.6
 
-      ``INACTIVITY_TIMEOUT <seconds>``
-        .. versionadded:: 3.19
+  Allows the extraction part of the download step to be disabled by
+  passing a boolean true value for this option. If this option is not
+  given, the downloaded contents will be unpacked automatically if
+  required. If extraction has been disabled, the full path to the
+  downloaded file is available as ``<DOWNLOADED_FILE>`` in subsequent
+  steps or as the property ``DOWNLOADED_FILE`` with the
+  :command:`ExternalProject_Get_Property` command.
 
-        Terminate the operation after a period of inactivity.
+``DOWNLOAD_NO_PROGRESS <bool>``
+  Can be used to disable logging the download progress. If this option is
+  not given, download progress messages will be logged.
 
-      ``HTTP_USERNAME <username>``
-        .. versionadded:: 3.7
+``TIMEOUT <seconds>``
+  Maximum time allowed for file download operations.
 
-        Username for the download operation if authentication is required.
+``INACTIVITY_TIMEOUT <seconds>``
+  .. versionadded:: 3.19
 
-      ``HTTP_PASSWORD <password>``
-        .. versionadded:: 3.7
+  Terminate the operation after a period of inactivity.
 
-        Password for the download operation if authentication is required.
+``HTTP_USERNAME <username>``
+  .. versionadded:: 3.7
 
-      ``HTTP_HEADER <header1> [<header2>...]``
-        .. versionadded:: 3.7
+  Username for the download operation if authentication is required.
 
-        Provides an arbitrary list of HTTP headers for the download operation.
-        This can be useful for accessing content in systems like AWS, etc.
+``HTTP_PASSWORD <password>``
+  .. versionadded:: 3.7
 
-      ``TLS_VERIFY <bool>``
-        Specifies whether certificate verification should be performed for
-        https URLs. If this option is not provided, the default behavior is
-        determined by the :variable:`CMAKE_TLS_VERIFY` variable (see
-        :command:`file(DOWNLOAD)`). If that is also not set, certificate
-        verification will not be performed. In situations where ``URL_HASH``
-        cannot be provided, this option can be an alternative verification
-        measure.
+  Password for the download operation if authentication is required.
 
-        .. versionchanged:: 3.6
-          This option also applies to ``git clone`` invocations, although the
-          default behavior is different.  If ``TLS_VERIFY`` is not given and
-          :variable:`CMAKE_TLS_VERIFY` is not set, the behavior will be
-          determined by git's defaults.  Normally, the ``sslVerify`` git
-          config setting defaults to true, but the user may have overridden
-          this at a global level.
+``HTTP_HEADER <header1> [<header2>...]``
+  .. versionadded:: 3.7
 
-      ``TLS_CAINFO <file>``
-        Specify a custom certificate authority file to use if ``TLS_VERIFY``
-        is enabled. If this option is not specified, the value of the
-        :variable:`CMAKE_TLS_CAINFO` variable will be used instead (see
-        :command:`file(DOWNLOAD)`)
+  Provides an arbitrary list of HTTP headers for the download operation.
+  This can be useful for accessing content in systems like AWS, etc.
 
-      ``NETRC <level>``
-        .. versionadded:: 3.11
+``TLS_VERIFY <bool>``
+  Specifies whether certificate verification should be performed for
+  https URLs. If this option is not provided, the default behavior is
+  determined by the :variable:`CMAKE_TLS_VERIFY` variable (see
+  :command:`file(DOWNLOAD)`). If that is also not set, certificate
+  verification will not be performed. In situations where ``URL_HASH``
+  cannot be provided, this option can be an alternative verification
+  measure.
 
-        Specify whether the ``.netrc`` file is to be used for operation.
-        If this option is not specified, the value of the
-        :variable:`CMAKE_NETRC` variable will be used instead
-        (see :command:`file(DOWNLOAD)`).  Valid levels are:
+  .. versionchanged:: 3.6
+    This option also applies to ``git clone`` invocations, although the
+    default behavior is different.  If ``TLS_VERIFY`` is not given and
+    :variable:`CMAKE_TLS_VERIFY` is not set, the behavior will be
+    determined by git's defaults.  Normally, the ``sslVerify`` git
+    config setting defaults to true, but the user may have overridden
+    this at a global level.
 
-        ``IGNORED``
-          The ``.netrc`` file is ignored.
-          This is the default.
-        ``OPTIONAL``
-          The ``.netrc`` file is optional, and information in the URL
-          is preferred.  The file will be scanned to find which ever
-          information is not specified in the URL.
-        ``REQUIRED``
-          The ``.netrc`` file is required, and information in the URL
-          is ignored.
+``TLS_CAINFO <file>``
+  Specify a custom certificate authority file to use if ``TLS_VERIFY``
+  is enabled. If this option is not specified, the value of the
+  :variable:`CMAKE_TLS_CAINFO` variable will be used instead (see
+  :command:`file(DOWNLOAD)`)
 
-      ``NETRC_FILE <file>``
-        .. versionadded:: 3.11
+``NETRC <level>``
+  .. versionadded:: 3.11
 
-        Specify an alternative ``.netrc`` file to the one in your home directory
-        if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
-        is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable
-        will be used instead (see :command:`file(DOWNLOAD)`)
+  Specify whether the ``.netrc`` file is to be used for operation.
+  If this option is not specified, the value of the
+  :variable:`CMAKE_NETRC` variable will be used instead
+  (see :command:`file(DOWNLOAD)`).  Valid levels are:
 
-      .. versionadded:: 3.1
-        Added support for `tbz2`, `.tar.xz`, `.txz`, and `.7z` extensions.
+  ``IGNORED``
+    The ``.netrc`` file is ignored.
+    This is the default.
+  ``OPTIONAL``
+    The ``.netrc`` file is optional, and information in the URL
+    is preferred.  The file will be scanned to find which ever
+    information is not specified in the URL.
+  ``REQUIRED``
+    The ``.netrc`` file is required, and information in the URL
+    is ignored.
 
-    *Git*
-      NOTE: A git version of 1.6.5 or later is required if this download method
-      is used.
+``NETRC_FILE <file>``
+  .. versionadded:: 3.11
 
-      ``GIT_REPOSITORY <url>``
-        URL of the git repository. Any URL understood by the ``git`` command
-        may be used.
+  Specify an alternative ``.netrc`` file to the one in your home directory
+  if the ``NETRC`` level is ``OPTIONAL`` or ``REQUIRED``. If this option
+  is not specified, the value of the :variable:`CMAKE_NETRC_FILE` variable
+  will be used instead (see :command:`file(DOWNLOAD)`)
 
-        .. versionchanged:: 3.27
-          A relative URL will be resolved based on the parent project's
-          remote, subject to :policy:`CMP0150`.  See the policy documentation
-          for how the remote is selected, including conditions where the
-          remote selection can fail.  Local filesystem remotes should
-          always use absolute paths.
+.. versionadded:: 3.1
+  Added support for `tbz2`, `.tar.xz`, `.txz`, and `.7z` extensions.
 
-      ``GIT_TAG <tag>``
-        Git branch name, tag or commit hash. Note that branch names and tags
-        should generally be specified as remote names (i.e. ``origin/myBranch``
-        rather than simply ``myBranch``). This ensures that if the remote end
-        has its tag moved or branch rebased or history rewritten, the local
-        clone will still be updated correctly. In general, however, specifying
-        a commit hash should be preferred for a number of reasons:
+Git
+~~~
 
-        - If the local clone already has the commit corresponding to the hash,
-          no ``git fetch`` needs to be performed to check for changes each time
-          CMake is re-run. This can result in a significant speed up if many
-          external projects are being used.
-        - Using a specific git hash ensures that the main project's own history
-          is fully traceable to a specific point in the external project's
-          evolution. If a branch or tag name is used instead, then checking out
-          a specific commit of the main project doesn't necessarily pin the
-          whole build to a specific point in the life of the external project.
-          The lack of such deterministic behavior makes the main project lose
-          traceability and repeatability.
+NOTE: A git version of 1.6.5 or later is required if this download method
+is used.
 
-        If ``GIT_SHALLOW`` is enabled then ``GIT_TAG`` works only with
-        branch names and tags.  A commit hash is not allowed.
+``GIT_REPOSITORY <url>``
+  URL of the git repository. Any URL understood by the ``git`` command
+  may be used.
 
-        Note that if not provided, ``GIT_TAG`` defaults to ``master``, not the
-        default Git branch name.
+  .. versionchanged:: 3.27
+    A relative URL will be resolved based on the parent project's
+    remote, subject to :policy:`CMP0150`.  See the policy documentation
+    for how the remote is selected, including conditions where the
+    remote selection can fail.  Local filesystem remotes should
+    always use absolute paths.
 
-      ``GIT_REMOTE_NAME <name>``
-        The optional name of the remote. If this option is not specified, it
-        defaults to ``origin``.
+``GIT_TAG <tag>``
+  Git branch name, tag or commit hash. Note that branch names and tags
+  should generally be specified as remote names (i.e. ``origin/myBranch``
+  rather than simply ``myBranch``). This ensures that if the remote end
+  has its tag moved or branch rebased or history rewritten, the local
+  clone will still be updated correctly. In general, however, specifying
+  a commit hash should be preferred for a number of reasons:
 
-      ``GIT_SUBMODULES <module>...``
-        Specific git submodules that should also be updated. If this option is
-        not provided, all git submodules will be updated.
+  - If the local clone already has the commit corresponding to the hash,
+    no ``git fetch`` needs to be performed to check for changes each time
+    CMake is re-run. This can result in a significant speed up if many
+    external projects are being used.
+  - Using a specific git hash ensures that the main project's own history
+    is fully traceable to a specific point in the external project's
+    evolution. If a branch or tag name is used instead, then checking out
+    a specific commit of the main project doesn't necessarily pin the
+    whole build to a specific point in the life of the external project.
+    The lack of such deterministic behavior makes the main project lose
+    traceability and repeatability.
 
-        .. versionchanged:: 3.16
-          When :policy:`CMP0097` is set to ``NEW``, if this value is set
-          to an empty string then no submodules are initialized or updated.
+  If ``GIT_SHALLOW`` is enabled then ``GIT_TAG`` works only with
+  branch names and tags.  A commit hash is not allowed.
 
-      ``GIT_SUBMODULES_RECURSE <bool>``
-        .. versionadded:: 3.17
+  Note that if not provided, ``GIT_TAG`` defaults to ``master``, not the
+  default Git branch name.
 
-        Specify whether git submodules (if any) should update recursively by
-        passing the ``--recursive`` flag to ``git submodule update``.
-        If not specified, the default is on.
+``GIT_REMOTE_NAME <name>``
+  The optional name of the remote. If this option is not specified, it
+  defaults to ``origin``.
 
-      ``GIT_SHALLOW <bool>``
-        .. versionadded:: 3.6
+``GIT_SUBMODULES <module>...``
+  Specific git submodules that should also be updated. If this option is
+  not provided, all git submodules will be updated.
 
-        When this option is enabled, the ``git clone`` operation will be given
-        the ``--depth 1`` option. This performs a shallow clone, which avoids
-        downloading the whole history and instead retrieves just the commit
-        denoted by the ``GIT_TAG`` option.
+  .. versionchanged:: 3.16
+    When :policy:`CMP0097` is set to ``NEW``, if this value is set
+    to an empty string then no submodules are initialized or updated.
 
-      ``GIT_PROGRESS <bool>``
-        .. versionadded:: 3.8
+``GIT_SUBMODULES_RECURSE <bool>``
+  .. versionadded:: 3.17
 
-        When enabled, this option instructs the ``git clone`` operation to
-        report its progress by passing it the ``--progress`` option. Without
-        this option, the clone step for large projects may appear to make the
-        build stall, since nothing will be logged until the clone operation
-        finishes. While this option can be used to provide progress to prevent
-        the appearance of the build having stalled, it may also make the build
-        overly noisy if lots of external projects are used.
+  Specify whether git submodules (if any) should update recursively by
+  passing the ``--recursive`` flag to ``git submodule update``.
+  If not specified, the default is on.
 
-      ``GIT_CONFIG <option1> [<option2>...]``
-        .. versionadded:: 3.8
+``GIT_SHALLOW <bool>``
+  .. versionadded:: 3.6
 
-        Specify a list of config options to pass to ``git clone``. Each option
-        listed will be transformed into its own ``--config <option>`` on the
-        ``git clone`` command line, with each option required to be in the
-        form ``key=value``.
+  When this option is enabled, the ``git clone`` operation will be given
+  the ``--depth 1`` option. This performs a shallow clone, which avoids
+  downloading the whole history and instead retrieves just the commit
+  denoted by the ``GIT_TAG`` option.
 
-      ``GIT_REMOTE_UPDATE_STRATEGY <strategy>``
-        .. versionadded:: 3.18
+``GIT_PROGRESS <bool>``
+  .. versionadded:: 3.8
 
-        When ``GIT_TAG`` refers to a remote branch, this option can be used to
-        specify how the update step behaves.  The ``<strategy>`` must be one of
-        the following:
+  When enabled, this option instructs the ``git clone`` operation to
+  report its progress by passing it the ``--progress`` option. Without
+  this option, the clone step for large projects may appear to make the
+  build stall, since nothing will be logged until the clone operation
+  finishes. While this option can be used to provide progress to prevent
+  the appearance of the build having stalled, it may also make the build
+  overly noisy if lots of external projects are used.
 
-        ``CHECKOUT``
-          Ignore the local branch and always checkout the branch specified by
-          ``GIT_TAG``.
+``GIT_CONFIG <option1> [<option2>...]``
+  .. versionadded:: 3.8
 
-        ``REBASE``
-          Try to rebase the current branch to the one specified by ``GIT_TAG``.
-          If there are local uncommitted changes, they will be stashed first
-          and popped again after rebasing.  If rebasing or popping stashed
-          changes fail, abort the rebase and halt with an error.
-          When ``GIT_REMOTE_UPDATE_STRATEGY`` is not present, this is the
-          default strategy unless the default has been overridden with
-          ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` (see below).
-          Note that if the branch specified in ``GIT_TAG`` is different to
-          the upstream branch currently being tracked, it is not safe to
-          perform a rebase. In that situation, ``REBASE`` will silently be
-          treated as ``CHECKOUT`` instead.
+  Specify a list of config options to pass to ``git clone``. Each option
+  listed will be transformed into its own ``--config <option>`` on the
+  ``git clone`` command line, with each option required to be in the
+  form ``key=value``.
 
-        ``REBASE_CHECKOUT``
-          Same as ``REBASE`` except if the rebase fails, an annotated tag will
-          be created at the original ``HEAD`` position from before the rebase
-          and then checkout ``GIT_TAG`` just like the ``CHECKOUT`` strategy.
-          The message stored on the annotated tag will give information about
-          what was attempted and the tag name will include a timestamp so that
-          each failed run will add a new tag.  This strategy ensures no changes
-          will be lost, but updates should always succeed if ``GIT_TAG`` refers
-          to a valid ref unless there are uncommitted changes that cannot be
-          popped successfully.
+``GIT_REMOTE_UPDATE_STRATEGY <strategy>``
+  .. versionadded:: 3.18
 
-        The variable ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` can be set to
-        override the default strategy.  This variable should not be set by a
-        project, it is intended for the user to set.  It is primarily intended
-        for use in continuous integration scripts to ensure that when history
-        is rewritten on a remote branch, the build doesn't end up with
-        unintended changes or failed builds resulting from conflicts during
-        rebase operations.
+  When ``GIT_TAG`` refers to a remote branch, this option can be used to
+  specify how the update step behaves.  The ``<strategy>`` must be one of
+  the following:
 
-    *Subversion*
-      ``SVN_REPOSITORY <url>``
-        URL of the Subversion repository.
+  ``CHECKOUT``
+    Ignore the local branch and always checkout the branch specified by
+    ``GIT_TAG``.
 
-      ``SVN_REVISION -r<rev>``
-        Revision to checkout from the Subversion repository.
+  ``REBASE``
+    Try to rebase the current branch to the one specified by ``GIT_TAG``.
+    If there are local uncommitted changes, they will be stashed first
+    and popped again after rebasing.  If rebasing or popping stashed
+    changes fail, abort the rebase and halt with an error.
+    When ``GIT_REMOTE_UPDATE_STRATEGY`` is not present, this is the
+    default strategy unless the default has been overridden with
+    ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` (see below).
+    Note that if the branch specified in ``GIT_TAG`` is different to
+    the upstream branch currently being tracked, it is not safe to
+    perform a rebase. In that situation, ``REBASE`` will silently be
+    treated as ``CHECKOUT`` instead.
 
-      ``SVN_USERNAME <username>``
-        Username for the Subversion checkout and update.
+  ``REBASE_CHECKOUT``
+    Same as ``REBASE`` except if the rebase fails, an annotated tag will
+    be created at the original ``HEAD`` position from before the rebase
+    and then checkout ``GIT_TAG`` just like the ``CHECKOUT`` strategy.
+    The message stored on the annotated tag will give information about
+    what was attempted and the tag name will include a timestamp so that
+    each failed run will add a new tag.  This strategy ensures no changes
+    will be lost, but updates should always succeed if ``GIT_TAG`` refers
+    to a valid ref unless there are uncommitted changes that cannot be
+    popped successfully.
 
-      ``SVN_PASSWORD <password>``
-        Password for the Subversion checkout and update.
+  The variable ``CMAKE_EP_GIT_REMOTE_UPDATE_STRATEGY`` can be set to
+  override the default strategy.  This variable should not be set by a
+  project, it is intended for the user to set.  It is primarily intended
+  for use in continuous integration scripts to ensure that when history
+  is rewritten on a remote branch, the build doesn't end up with
+  unintended changes or failed builds resulting from conflicts during
+  rebase operations.
 
-      ``SVN_TRUST_CERT <bool>``
-        Specifies whether to trust the Subversion server site certificate. If
-        enabled, the ``--trust-server-cert`` option is passed to the ``svn``
-        checkout and update commands.
+Subversion
+~~~~~~~~~~
 
-    *Mercurial*
-      ``HG_REPOSITORY <url>``
-        URL of the mercurial repository.
+``SVN_REPOSITORY <url>``
+  URL of the Subversion repository.
 
-      ``HG_TAG <tag>``
-        Mercurial branch name, tag or commit id.
+``SVN_REVISION -r<rev>``
+  Revision to checkout from the Subversion repository.
 
-    *CVS*
-      ``CVS_REPOSITORY <cvsroot>``
-        CVSROOT of the CVS repository.
+``SVN_USERNAME <username>``
+  Username for the Subversion checkout and update.
 
-      ``CVS_MODULE <mod>``
-        Module to checkout from the CVS repository.
+``SVN_PASSWORD <password>``
+  Password for the Subversion checkout and update.
+
+``SVN_TRUST_CERT <bool>``
+  Specifies whether to trust the Subversion server site certificate. If
+  enabled, the ``--trust-server-cert`` option is passed to the ``svn``
+  checkout and update commands.
+
+Mercurial
+~~~~~~~~~
+
+``HG_REPOSITORY <url>``
+  URL of the mercurial repository.
+
+``HG_TAG <tag>``
+  Mercurial branch name, tag or commit id.
+
+CVS
+~~~
+
+``CVS_REPOSITORY <cvsroot>``
+  CVSROOT of the CVS repository.
+
+``CVS_MODULE <mod>``
+  Module to checkout from the CVS repository.
+
+``CVS_TAG <tag>``
+  Tag to checkout from the CVS repository.
+
+Update Step Options
+"""""""""""""""""""
+
+Whenever CMake is re-run, by default the external project's sources will be
+updated if the download method supports updates (e.g. a git repository
+would be checked if the ``GIT_TAG`` does not refer to a specific commit).
+
+``UPDATE_COMMAND <cmd>...``
+  Overrides the download method's update step with a custom command.
+  The command may use
+  :manual:`generator expressions <cmake-generator-expressions(7)>`.
+
+``UPDATE_DISCONNECTED <bool>``
+  .. versionadded:: 3.2
+
+  When enabled, this option causes the update step to be skipped (but see
+  below for changed behavior where this is not the case). It does not
+  prevent the download step. The update step can still be
+  added as a step target (see :command:`ExternalProject_Add_StepTargets`)
+  and called manually. This is useful if you want to allow developers to
+  build the project when disconnected from the network (the network may
+  still be needed for the download step though).
+
+  .. versionchanged:: 3.27
+
+    When ``UPDATE_DISCONNECTED`` is true, the update step will be executed
+    if any details about the update or download step are changed.
+    Furthermore, if using the git download/update method, the update
+    logic will be modified to skip attempts to contact the remote.
+    If the ``GIT_TAG`` mentions a ref that is not known locally, the
+    update step will halt with a fatal error.
+
+  When this option is present, it is generally advisable to make the value
+  a cache variable under the developer's control rather than hard-coding
+  it. If this option is not present, the default value is taken from the
+  ``EP_UPDATE_DISCONNECTED`` directory property. If that is also not
+  defined, updates are performed as normal. The ``EP_UPDATE_DISCONNECTED``
+  directory property is intended as a convenience for controlling the
+  ``UPDATE_DISCONNECTED`` behavior for an entire section of a project's
+  directory hierarchy and may be a more convenient method of giving
+  developers control over whether or not to perform updates (assuming the
+  project also provides a cache variable or some other convenient method
+  for setting the directory property).
+
+  This may cause a step target to be created automatically for the
+  ``download`` step.  See policy :policy:`CMP0114`.
+
+Patch Step Options
+""""""""""""""""""
+
+``PATCH_COMMAND <cmd>...``
+  Specifies a custom command to patch the sources after an update. By
+  default, no patch command is defined. Note that it can be quite difficult
+  to define an appropriate patch command that performs robustly, especially
+  for download methods such as git where changing the ``GIT_TAG`` will not
+  discard changes from a previous patch, but the patch command will be
+  called again after updating to the new tag.
 
-      ``CVS_TAG <tag>``
-        Tag to checkout from the CVS repository.
+Configure Step Options
+""""""""""""""""""""""
 
-  **Update Step Options:**
-    Whenever CMake is re-run, by default the external project's sources will be
-    updated if the download method supports updates (e.g. a git repository
-    would be checked if the ``GIT_TAG`` does not refer to a specific commit).
+The configure step is run after the download and update steps. By default,
+the external project is assumed to be a CMake project, but this can be
+overridden if required.
 
-    ``UPDATE_COMMAND <cmd>...``
-      Overrides the download method's update step with a custom command.
-      The command may use
-      :manual:`generator expressions <cmake-generator-expressions(7)>`.
+``CONFIGURE_COMMAND <cmd>...``
+  The default configure command runs CMake with a few options based on
+  the main project.  The options added are typically only those needed to
+  use the same generator as the main project, but the ``CMAKE_GENERATOR``
+  option can be given to override this.  The project is responsible for
+  adding any toolchain details, flags or other settings it wants to
+  re-use from the main project or otherwise specify (see ``CMAKE_ARGS``,
+  ``CMAKE_CACHE_ARGS`` and ``CMAKE_CACHE_DEFAULT_ARGS`` below).
 
-    ``UPDATE_DISCONNECTED <bool>``
-      .. versionadded:: 3.2
+  For non-CMake external projects, the ``CONFIGURE_COMMAND`` option must
+  be used to override the default configure command
+  (:manual:`generator expressions <cmake-generator-expressions(7)>` are
+  supported). For projects that require no configure step, specify this
+  option with an empty string as the command to execute.
 
-      When enabled, this option causes the update step to be skipped (but see
-      below for changed behavior where this is not the case). It does not
-      prevent the download step. The update step can still be
-      added as a step target (see :command:`ExternalProject_Add_StepTargets`)
-      and called manually. This is useful if you want to allow developers to
-      build the project when disconnected from the network (the network may
-      still be needed for the download step though).
+``CMAKE_COMMAND /.../cmake``
+  Specify an alternative cmake executable for the configure step (use an
+  absolute path). This is generally not recommended, since it is
+  usually desirable to use the same CMake version throughout the whole
+  build. This option is ignored if a custom configure command has been
+  specified with ``CONFIGURE_COMMAND``.
 
-      .. versionchanged:: 3.27
+``CMAKE_GENERATOR <gen>``
+  Override the CMake generator used for the configure step. Without this
+  option, the same generator as the main build will be used. This option is
+  ignored if a custom configure command has been specified with the
+  ``CONFIGURE_COMMAND`` option.
 
-        When ``UPDATE_DISCONNECTED`` is true, the update step will be executed
-        if any details about the update or download step are changed.
-        Furthermore, if using the git download/update method, the update
-        logic will be modified to skip attempts to contact the remote.
-        If the ``GIT_TAG`` mentions a ref that is not known locally, the
-        update step will halt with a fatal error.
+``CMAKE_GENERATOR_PLATFORM <platform>``
+  .. versionadded:: 3.1
 
-      When this option is present, it is generally advisable to make the value
-      a cache variable under the developer's control rather than hard-coding
-      it. If this option is not present, the default value is taken from the
-      ``EP_UPDATE_DISCONNECTED`` directory property. If that is also not
-      defined, updates are performed as normal. The ``EP_UPDATE_DISCONNECTED``
-      directory property is intended as a convenience for controlling the
-      ``UPDATE_DISCONNECTED`` behavior for an entire section of a project's
-      directory hierarchy and may be a more convenient method of giving
-      developers control over whether or not to perform updates (assuming the
-      project also provides a cache variable or some other convenient method
-      for setting the directory property).
+  Pass a generator-specific platform name to the CMake command (see
+  :variable:`CMAKE_GENERATOR_PLATFORM`). It is an error to provide this
+  option without the ``CMAKE_GENERATOR`` option.
 
-      This may cause a step target to be created automatically for the
-      ``download`` step.  See policy :policy:`CMP0114`.
+``CMAKE_GENERATOR_TOOLSET <toolset>``
+  Pass a generator-specific toolset name to the CMake command (see
+  :variable:`CMAKE_GENERATOR_TOOLSET`). It is an error to provide this
+  option without the ``CMAKE_GENERATOR`` option.
 
-  **Patch Step Options:**
-    ``PATCH_COMMAND <cmd>...``
-      Specifies a custom command to patch the sources after an update. By
-      default, no patch command is defined. Note that it can be quite difficult
-      to define an appropriate patch command that performs robustly, especially
-      for download methods such as git where changing the ``GIT_TAG`` will not
-      discard changes from a previous patch, but the patch command will be
-      called again after updating to the new tag.
+``CMAKE_GENERATOR_INSTANCE <instance>``
+  .. versionadded:: 3.11
 
-  **Configure Step Options:**
-    The configure step is run after the download and update steps. By default,
-    the external project is assumed to be a CMake project, but this can be
-    overridden if required.
+  Pass a generator-specific instance selection to the CMake command (see
+  :variable:`CMAKE_GENERATOR_INSTANCE`). It is an error to provide this
+  option without the ``CMAKE_GENERATOR`` option.
 
-    ``CONFIGURE_COMMAND <cmd>...``
-      The default configure command runs CMake with a few options based on
-      the main project.  The options added are typically only those needed to
-      use the same generator as the main project, but the ``CMAKE_GENERATOR``
-      option can be given to override this.  The project is responsible for
-      adding any toolchain details, flags or other settings it wants to
-      re-use from the main project or otherwise specify (see ``CMAKE_ARGS``,
-      ``CMAKE_CACHE_ARGS`` and ``CMAKE_CACHE_DEFAULT_ARGS`` below).
+``CMAKE_ARGS <arg>...``
+  The specified arguments are passed to the :program:`cmake` command line.
+  They can be any argument the :program:`cmake` command understands, not just
+  cache values defined by ``-D...`` arguments (see also
+  :manual:`CMake Options <cmake(1)>`).
 
-      For non-CMake external projects, the ``CONFIGURE_COMMAND`` option must
-      be used to override the default configure command
-      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
-      supported). For projects that require no configure step, specify this
-      option with an empty string as the command to execute.
+  .. versionadded:: 3.3
+    Arguments may use
+    :manual:`generator expressions <cmake-generator-expressions(7)>`.
 
-    ``CMAKE_COMMAND /.../cmake``
-      Specify an alternative cmake executable for the configure step (use an
-      absolute path). This is generally not recommended, since it is
-      usually desirable to use the same CMake version throughout the whole
-      build. This option is ignored if a custom configure command has been
-      specified with ``CONFIGURE_COMMAND``.
+``CMAKE_CACHE_ARGS <arg>...``
+  This is an alternate way of specifying cache variables where command line
+  length issues may become a problem. The arguments are expected to be in
+  the form ``-Dvar:STRING=value``, which are then transformed into
+  CMake :command:`set` commands with the ``FORCE`` option used. These
+  ``set()`` commands are written to a pre-load script which is then applied
+  using the :manual:`cmake -C <cmake(1)>` command line option.
 
-    ``CMAKE_GENERATOR <gen>``
-      Override the CMake generator used for the configure step. Without this
-      option, the same generator as the main build will be used. This option is
-      ignored if a custom configure command has been specified with the
-      ``CONFIGURE_COMMAND`` option.
+  .. versionadded:: 3.3
+    Arguments may use
+    :manual:`generator expressions <cmake-generator-expressions(7)>`.
 
-    ``CMAKE_GENERATOR_PLATFORM <platform>``
-      .. versionadded:: 3.1
+``CMAKE_CACHE_DEFAULT_ARGS <arg>...``
+  .. versionadded:: 3.2
 
-      Pass a generator-specific platform name to the CMake command (see
-      :variable:`CMAKE_GENERATOR_PLATFORM`). It is an error to provide this
-      option without the ``CMAKE_GENERATOR`` option.
+  This is the same as the ``CMAKE_CACHE_ARGS`` option except the ``set()``
+  commands do not include the ``FORCE`` keyword. This means the values act
+  as initial defaults only and will not override any variables already set
+  from a previous run. Use this option with care, as it can lead to
+  different behavior depending on whether the build starts from a fresh
+  build directory or re-uses previous build contents.
 
-    ``CMAKE_GENERATOR_TOOLSET <toolset>``
-      Pass a generator-specific toolset name to the CMake command (see
-      :variable:`CMAKE_GENERATOR_TOOLSET`). It is an error to provide this
-      option without the ``CMAKE_GENERATOR`` option.
+  .. versionadded:: 3.15
+    If the CMake generator is the ``Green Hills MULTI`` and not overridden,
+    the original project's settings for the GHS toolset and target system
+    customization cache variables are propagated into the external project.
 
-    ``CMAKE_GENERATOR_INSTANCE <instance>``
-      .. versionadded:: 3.11
+``SOURCE_SUBDIR <dir>``
+  .. versionadded:: 3.7
 
-      Pass a generator-specific instance selection to the CMake command (see
-      :variable:`CMAKE_GENERATOR_INSTANCE`). It is an error to provide this
-      option without the ``CMAKE_GENERATOR`` option.
+  When no ``CONFIGURE_COMMAND`` option is specified, the configure step
+  assumes the external project has a ``CMakeLists.txt`` file at the top of
+  its source tree (i.e. in ``SOURCE_DIR``). The ``SOURCE_SUBDIR`` option
+  can be used to point to an alternative directory within the source tree
+  to use as the top of the CMake source tree instead. This must be a
+  relative path and it will be interpreted as being relative to
+  ``SOURCE_DIR``.
 
-    ``CMAKE_ARGS <arg>...``
-      The specified arguments are passed to the :program:`cmake` command line.
-      They can be any argument the :program:`cmake` command understands, not just
-      cache values defined by ``-D...`` arguments (see also
-      :manual:`CMake Options <cmake(1)>`).
+  .. versionadded:: 3.14
+    When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND``
+    is used to point to an alternative directory within the source tree.
 
-      .. versionadded:: 3.3
-        Arguments may use
-        :manual:`generator expressions <cmake-generator-expressions(7)>`.
+``CONFIGURE_HANDLED_BY_BUILD <bool>``
+  .. versionadded:: 3.20
 
-    ``CMAKE_CACHE_ARGS <arg>...``
-      This is an alternate way of specifying cache variables where command line
-      length issues may become a problem. The arguments are expected to be in
-      the form ``-Dvar:STRING=value``, which are then transformed into
-      CMake :command:`set` commands with the ``FORCE`` option used. These
-      ``set()`` commands are written to a pre-load script which is then applied
-      using the :manual:`cmake -C <cmake(1)>` command line option.
+  Enabling this option relaxes the dependencies of the configure step on
+  other external projects to order-only. This means the configure step will
+  be executed after its external project dependencies are built but it will
+  not be marked dirty when one of its external project dependencies is
+  rebuilt. This option can be enabled when the build step is smart enough
+  to figure out if the configure step needs to be rerun. CMake and Meson are
+  examples of build systems whose build step is smart enough to know if the
+  configure step needs to be rerun.
 
-      .. versionadded:: 3.3
-        Arguments may use
-        :manual:`generator expressions <cmake-generator-expressions(7)>`.
+Build Step Options
+""""""""""""""""""
 
-    ``CMAKE_CACHE_DEFAULT_ARGS <arg>...``
-      .. versionadded:: 3.2
+If the configure step assumed the external project uses CMake as its build
+system, the build step will also. Otherwise, the build step will assume a
+Makefile-based build and simply run ``make`` with no arguments as the
+default build step. This can be overridden with custom build commands if
+required.
 
-      This is the same as the ``CMAKE_CACHE_ARGS`` option except the ``set()``
-      commands do not include the ``FORCE`` keyword. This means the values act
-      as initial defaults only and will not override any variables already set
-      from a previous run. Use this option with care, as it can lead to
-      different behavior depending on whether the build starts from a fresh
-      build directory or re-uses previous build contents.
+If both the main project and the external project use make as their build
+tool, the build step of the external project is invoked as a recursive
+make using ``$(MAKE)``.  This will communicate some build tool settings
+from the main project to the external project.  If either the main project
+or external project is not using make, no build tool settings will be
+passed to the external project other than those established by the
+configure step (i.e. running ``ninja -v`` in the main project will not
+pass ``-v`` to the external project's build step, even if it also uses
+``ninja`` as its build tool).
 
-      .. versionadded:: 3.15
-        If the CMake generator is the ``Green Hills MULTI`` and not overridden,
-        the original project's settings for the GHS toolset and target system
-        customization cache variables are propagated into the external project.
+``BUILD_COMMAND <cmd>...``
+  Overrides the default build command
+  (:manual:`generator expressions <cmake-generator-expressions(7)>` are
+  supported). If this option is not given, the default build command will
+  be chosen to integrate with the main build in the most appropriate way
+  (e.g. using recursive ``make`` for Makefile generators or
+  :option:`cmake --build` if the project uses a CMake build). This option
+  can be specified with an empty string as the command to make the build
+  step do nothing.
 
-    ``SOURCE_SUBDIR <dir>``
-      .. versionadded:: 3.7
+``BUILD_IN_SOURCE <bool>``
+  When this option is enabled, the build will be done directly within the
+  external project's source tree. This should generally be avoided, the use
+  of a separate build directory is usually preferred, but it can be useful
+  when the external project assumes an in-source build. The ``BINARY_DIR``
+  option should not be specified if building in-source.
 
-      When no ``CONFIGURE_COMMAND`` option is specified, the configure step
-      assumes the external project has a ``CMakeLists.txt`` file at the top of
-      its source tree (i.e. in ``SOURCE_DIR``). The ``SOURCE_SUBDIR`` option
-      can be used to point to an alternative directory within the source tree
-      to use as the top of the CMake source tree instead. This must be a
-      relative path and it will be interpreted as being relative to
-      ``SOURCE_DIR``.
+``BUILD_ALWAYS <bool>``
+  Enabling this option forces the build step to always be run. This can be
+  the easiest way to robustly ensure that the external project's own build
+  dependencies are evaluated rather than relying on the default
+  success timestamp-based method. This option is not normally needed unless
+  developers are expected to modify something the external project's build
+  depends on in a way that is not detectable via the step target
+  dependencies (e.g. ``SOURCE_DIR`` is used without a download method and
+  developers might modify the sources in ``SOURCE_DIR``).
 
-      .. versionadded:: 3.14
-        When ``BUILD_IN_SOURCE`` option is enabled, the ``BUILD_COMMAND``
-        is used to point to an alternative directory within the source tree.
+``BUILD_BYPRODUCTS <file>...``
+  .. versionadded:: 3.2
 
-    ``CONFIGURE_HANDLED_BY_BUILD <bool>``
-      .. versionadded:: 3.20
+  Specifies files that will be generated by the build command but which
+  might or might not have their modification time updated by subsequent
+  builds. This may also be required to explicitly declare dependencies
+  when using the :generator:`Ninja` generator.
+  These ultimately get passed through as ``BYPRODUCTS`` to the
+  build step's own underlying call to :command:`add_custom_command`, which
+  has additional documentation.
 
-      Enabling this option relaxes the dependencies of the configure step on
-      other external projects to order-only. This means the configure step will
-      be executed after its external project dependencies are built but it will
-      not be marked dirty when one of its external project dependencies is
-      rebuilt. This option can be enabled when the build step is smart enough
-      to figure out if the configure step needs to be rerun. CMake and Meson are
-      examples of build systems whose build step is smart enough to know if the
-      configure step needs to be rerun.
+``BUILD_JOB_SERVER_AWARE <bool>``
+  .. versionadded:: 3.28
 
-  **Build Step Options:**
-    If the configure step assumed the external project uses CMake as its build
-    system, the build step will also. Otherwise, the build step will assume a
-    Makefile-based build and simply run ``make`` with no arguments as the
-    default build step. This can be overridden with custom build commands if
-    required.
+  Specifies that the build step is aware of the GNU Make job server.
+  See the :command:`add_custom_command` documentation of its
+  ``JOB_SERVER_AWARE`` option for details.  This option is relevant
+  only when an explicit ``BUILD_COMMAND`` is specified.
 
-    If both the main project and the external project use make as their build
-    tool, the build step of the external project is invoked as a recursive
-    make using ``$(MAKE)``.  This will communicate some build tool settings
-    from the main project to the external project.  If either the main project
-    or external project is not using make, no build tool settings will be
-    passed to the external project other than those established by the
-    configure step (i.e. running ``ninja -v`` in the main project will not
-    pass ``-v`` to the external project's build step, even if it also uses
-    ``ninja`` as its build tool).
+Install Step Options
+""""""""""""""""""""
 
-    ``BUILD_COMMAND <cmd>...``
-      Overrides the default build command
-      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
-      supported). If this option is not given, the default build command will
-      be chosen to integrate with the main build in the most appropriate way
-      (e.g. using recursive ``make`` for Makefile generators or
-      :option:`cmake --build` if the project uses a CMake build). This option
-      can be specified with an empty string as the command to make the build
-      step do nothing.
+If the configure step assumed the external project uses CMake as its build
+system, the install step will also. Otherwise, the install step will assume
+a Makefile-based build and simply run ``make install`` as the default build
+step. This can be overridden with custom install commands if required.
 
-    ``BUILD_IN_SOURCE <bool>``
-      When this option is enabled, the build will be done directly within the
-      external project's source tree. This should generally be avoided, the use
-      of a separate build directory is usually preferred, but it can be useful
-      when the external project assumes an in-source build. The ``BINARY_DIR``
-      option should not be specified if building in-source.
+``INSTALL_COMMAND <cmd>...``
+  The external project's own install step is invoked as part of the main
+  project's *build*. It is done after the external project's build step
+  and may be before or after the external project's test step (see the
+  ``TEST_BEFORE_INSTALL`` option below). The external project's install
+  rules are not part of the main project's install rules, so if anything
+  from the external project should be installed as part of the main build,
+  these need to be specified in the main build as additional
+  :command:`install` commands. The default install step builds the
+  ``install`` target of the external project, but this can be overridden
+  with a custom command using this option
+  (:manual:`generator expressions <cmake-generator-expressions(7)>` are
+  supported). Passing an empty string as the ``<cmd>`` makes the install
+  step do nothing.
 
-    ``BUILD_ALWAYS <bool>``
-      Enabling this option forces the build step to always be run. This can be
-      the easiest way to robustly ensure that the external project's own build
-      dependencies are evaluated rather than relying on the default
-      success timestamp-based method. This option is not normally needed unless
-      developers are expected to modify something the external project's build
-      depends on in a way that is not detectable via the step target
-      dependencies (e.g. ``SOURCE_DIR`` is used without a download method and
-      developers might modify the sources in ``SOURCE_DIR``).
+``INSTALL_BYPRODUCTS <file>...``
+  .. versionadded:: 3.26
 
-    ``BUILD_BYPRODUCTS <file>...``
-      .. versionadded:: 3.2
+  Specifies files that will be generated by the install command but which
+  might or might not have their modification time updated by subsequent
+  installs. This may also be required to explicitly declare dependencies
+  when using the :generator:`Ninja` generator.
+  These ultimately get passed through as ``BYPRODUCTS`` to the
+  install step's own underlying call to :command:`add_custom_command`, which
+  has additional documentation.
 
-      Specifies files that will be generated by the build command but which
-      might or might not have their modification time updated by subsequent
-      builds. This may also be required to explicitly declare dependencies
-      when using the :generator:`Ninja` generator.
-      These ultimately get passed through as ``BYPRODUCTS`` to the
-      build step's own underlying call to :command:`add_custom_command`, which
-      has additional documentation.
+.. note::
+  If the :envvar:`CMAKE_INSTALL_MODE` environment variable is set when the
+  main project is built, it will only have an effect if the following
+  conditions are met:
 
-  **Install Step Options:**
-    If the configure step assumed the external project uses CMake as its build
-    system, the install step will also. Otherwise, the install step will assume
-    a Makefile-based build and simply run ``make install`` as the default build
-    step. This can be overridden with custom install commands if required.
+  * The main project's configure step assumed the external project uses
+    CMake as its build system.
+  * The external project's install command actually runs. Note that due
+    to the way ``ExternalProject`` may use timestamps internally, if
+    nothing the install step depends on needs to be re-executed, the
+    install command might also not need to run.
 
-    ``INSTALL_COMMAND <cmd>...``
-      The external project's own install step is invoked as part of the main
-      project's *build*. It is done after the external project's build step
-      and may be before or after the external project's test step (see the
-      ``TEST_BEFORE_INSTALL`` option below). The external project's install
-      rules are not part of the main project's install rules, so if anything
-      from the external project should be installed as part of the main build,
-      these need to be specified in the main build as additional
-      :command:`install` commands. The default install step builds the
-      ``install`` target of the external project, but this can be overridden
-      with a custom command using this option
-      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
-      supported). Passing an empty string as the ``<cmd>`` makes the install
-      step do nothing.
+  Note also that ``ExternalProject`` does not check whether the
+  :envvar:`CMAKE_INSTALL_MODE` environment variable changes from one run
+  to another.
 
-    ``INSTALL_BYPRODUCTS <file>...``
-      .. versionadded:: 3.26
+Test Step Options
+"""""""""""""""""
 
-      Specifies files that will be generated by the install command but which
-      might or might not have their modification time updated by subsequent
-      installs. This may also be required to explicitly declare dependencies
-      when using the :generator:`Ninja` generator.
-      These ultimately get passed through as ``BYPRODUCTS`` to the
-      install step's own underlying call to :command:`add_custom_command`, which
-      has additional documentation.
+The test step is only defined if at least one of the following ``TEST_...``
+options are provided.
 
-    .. note::
-      If the :envvar:`CMAKE_INSTALL_MODE` environment variable is set when the
-      main project is built, it will only have an effect if the following
-      conditions are met:
+``TEST_COMMAND <cmd>...``
+  Overrides the default test command
+  (:manual:`generator expressions <cmake-generator-expressions(7)>` are
+  supported). If this option is not given, the default behavior of the test
+  step is to build the external project's own ``test`` target. This option
+  can be specified with ``<cmd>`` as an empty string, which allows the test
+  step to still be defined, but it will do nothing. Do not specify any of
+  the other ``TEST_...`` options if providing an empty string as the test
+  command, but prefer to omit all ``TEST_...`` options altogether if the
+  test step target is not needed.
 
-      * The main project's configure step assumed the external project uses
-        CMake as its build system.
-      * The external project's install command actually runs. Note that due
-        to the way ``ExternalProject`` may use timestamps internally, if
-        nothing the install step depends on needs to be re-executed, the
-        install command might also not need to run.
+``TEST_BEFORE_INSTALL <bool>``
+  When this option is enabled, the test step will be executed before the
+  install step. The default behavior is for the test step to run after the
+  install step.
 
-      Note also that ``ExternalProject`` does not check whether the
-      :envvar:`CMAKE_INSTALL_MODE` environment variable changes from one run
-      to another.
+``TEST_AFTER_INSTALL <bool>``
+  This option is mainly useful as a way to indicate that the test step is
+  desired but all default behavior is sufficient. Specifying this option
+  with a boolean true value ensures the test step is defined and that it
+  comes after the install step. If both ``TEST_BEFORE_INSTALL`` and
+  ``TEST_AFTER_INSTALL`` are enabled, the latter is silently ignored.
 
-  **Test Step Options:**
-    The test step is only defined if at least one of the following ``TEST_...``
-    options are provided.
+``TEST_EXCLUDE_FROM_MAIN <bool>``
+  .. versionadded:: 3.2
 
-    ``TEST_COMMAND <cmd>...``
-      Overrides the default test command
-      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
-      supported). If this option is not given, the default behavior of the test
-      step is to build the external project's own ``test`` target. This option
-      can be specified with ``<cmd>`` as an empty string, which allows the test
-      step to still be defined, but it will do nothing. Do not specify any of
-      the other ``TEST_...`` options if providing an empty string as the test
-      command, but prefer to omit all ``TEST_...`` options altogether if the
-      test step target is not needed.
+  If enabled, the main build's default ALL target will not depend on the
+  test step. This can be a useful way of ensuring the test step is defined
+  but only gets invoked when manually requested.
+  This may cause a step target to be created automatically for either
+  the ``install`` or ``build`` step.  See policy :policy:`CMP0114`.
 
-    ``TEST_BEFORE_INSTALL <bool>``
-      When this option is enabled, the test step will be executed before the
-      install step. The default behavior is for the test step to run after the
-      install step.
+Output Logging Options
+""""""""""""""""""""""
 
-    ``TEST_AFTER_INSTALL <bool>``
-      This option is mainly useful as a way to indicate that the test step is
-      desired but all default behavior is sufficient. Specifying this option
-      with a boolean true value ensures the test step is defined and that it
-      comes after the install step. If both ``TEST_BEFORE_INSTALL`` and
-      ``TEST_AFTER_INSTALL`` are enabled, the latter is silently ignored.
+Each of the following ``LOG_...`` options can be used to wrap the relevant
+step in a script to capture its output to files. The log files will be
+created in ``LOG_DIR`` if supplied or otherwise the ``STAMP_DIR``
+directory with step-specific file names.
 
-    ``TEST_EXCLUDE_FROM_MAIN <bool>``
-      .. versionadded:: 3.2
+``LOG_DOWNLOAD <bool>``
+  When enabled, the output of the download step is logged to files.
 
-      If enabled, the main build's default ALL target will not depend on the
-      test step. This can be a useful way of ensuring the test step is defined
-      but only gets invoked when manually requested.
-      This may cause a step target to be created automatically for either
-      the ``install`` or ``build`` step.  See policy :policy:`CMP0114`.
+``LOG_UPDATE <bool>``
+  When enabled, the output of the update step is logged to files.
 
-  **Output Logging Options:**
-    Each of the following ``LOG_...`` options can be used to wrap the relevant
-    step in a script to capture its output to files. The log files will be
-    created in ``LOG_DIR`` if supplied or otherwise the ``STAMP_DIR``
-    directory with step-specific file names.
+``LOG_PATCH <bool>``
+  .. versionadded:: 3.14
 
-    ``LOG_DOWNLOAD <bool>``
-      When enabled, the output of the download step is logged to files.
+  When enabled, the output of the patch step is logged to files.
 
-    ``LOG_UPDATE <bool>``
-      When enabled, the output of the update step is logged to files.
+``LOG_CONFIGURE <bool>``
+  When enabled, the output of the configure step is logged to files.
 
-    ``LOG_PATCH <bool>``
-      .. versionadded:: 3.14
+``LOG_BUILD <bool>``
+  When enabled, the output of the build step is logged to files.
 
-      When enabled, the output of the patch step is logged to files.
+``LOG_INSTALL <bool>``
+  When enabled, the output of the install step is logged to files.
 
-    ``LOG_CONFIGURE <bool>``
-      When enabled, the output of the configure step is logged to files.
+``LOG_TEST <bool>``
+  When enabled, the output of the test step is logged to files.
 
-    ``LOG_BUILD <bool>``
-      When enabled, the output of the build step is logged to files.
+``LOG_MERGED_STDOUTERR <bool>``
+  .. versionadded:: 3.14
 
-    ``LOG_INSTALL <bool>``
-      When enabled, the output of the install step is logged to files.
+  When enabled, stdout and stderr will be merged for any step whose
+  output is being logged to files.
 
-    ``LOG_TEST <bool>``
-      When enabled, the output of the test step is logged to files.
+``LOG_OUTPUT_ON_FAILURE <bool>``
+  .. versionadded:: 3.14
 
-    ``LOG_MERGED_STDOUTERR <bool>``
-      .. versionadded:: 3.14
+  This option only has an effect if at least one of the other ``LOG_<step>``
+  options is enabled.  If an error occurs for a step which has logging to
+  file enabled, that step's output will be printed to the console if
+  ``LOG_OUTPUT_ON_FAILURE`` is set to true.  For cases where a large amount
+  of output is recorded, just the end of that output may be printed to the
+  console.
 
-      When enabled, stdout and stderr will be merged for any step whose
-      output is being logged to files.
+Terminal Access Options
+"""""""""""""""""""""""
 
-    ``LOG_OUTPUT_ON_FAILURE <bool>``
-      .. versionadded:: 3.14
+.. versionadded:: 3.4
 
-      This option only has an effect if at least one of the other ``LOG_<step>``
-      options is enabled.  If an error occurs for a step which has logging to
-      file enabled, that step's output will be printed to the console if
-      ``LOG_OUTPUT_ON_FAILURE`` is set to true.  For cases where a large amount
-      of output is recorded, just the end of that output may be printed to the
-      console.
+Steps can be given direct access to the terminal in some cases. Giving a
+step access to the terminal may allow it to receive terminal input if
+required, such as for authentication details not provided by other options.
+With the :generator:`Ninja` generator, these options place the steps in the
+``console`` :prop_gbl:`job pool <JOB_POOLS>`. Each step can be given access
+to the terminal individually via the following options:
 
-  **Terminal Access Options:**
-    .. versionadded:: 3.4
+``USES_TERMINAL_DOWNLOAD <bool>``
+  Give the download step access to the terminal.
 
-    Steps can be given direct access to the terminal in some cases. Giving a
-    step access to the terminal may allow it to receive terminal input if
-    required, such as for authentication details not provided by other options.
-    With the :generator:`Ninja` generator, these options place the steps in the
-    ``console`` :prop_gbl:`job pool <JOB_POOLS>`. Each step can be given access
-    to the terminal individually via the following options:
+``USES_TERMINAL_UPDATE <bool>``
+  Give the update step access to the terminal.
 
-    ``USES_TERMINAL_DOWNLOAD <bool>``
-      Give the download step access to the terminal.
+``USES_TERMINAL_PATCH <bool>``
+  .. versionadded:: 3.23
 
-    ``USES_TERMINAL_UPDATE <bool>``
-      Give the update step access to the terminal.
+  Give the patch step access to the terminal.
 
-    ``USES_TERMINAL_PATCH <bool>``
-      .. versionadded:: 3.23
+``USES_TERMINAL_CONFIGURE <bool>``
+  Give the configure step access to the terminal.
 
-      Give the patch step access to the terminal.
+``USES_TERMINAL_BUILD <bool>``
+  Give the build step access to the terminal.
 
-    ``USES_TERMINAL_CONFIGURE <bool>``
-      Give the configure step access to the terminal.
+``USES_TERMINAL_INSTALL <bool>``
+  Give the install step access to the terminal.
 
-    ``USES_TERMINAL_BUILD <bool>``
-      Give the build step access to the terminal.
+``USES_TERMINAL_TEST <bool>``
+  Give the test step access to the terminal.
 
-    ``USES_TERMINAL_INSTALL <bool>``
-      Give the install step access to the terminal.
+Target Options
+""""""""""""""
 
-    ``USES_TERMINAL_TEST <bool>``
-      Give the test step access to the terminal.
+``DEPENDS <targets>...``
+  Specify other targets on which the external project depends. The other
+  targets will be brought up to date before any of the external project's
+  steps are executed. Because the external project uses additional custom
+  targets internally for each step, the ``DEPENDS`` option is the most
+  convenient way to ensure all of those steps depend on the other targets.
+  Simply doing
+  :command:`add_dependencies(\<name\> \<targets\>) <add_dependencies>` will
+  not make any of the steps dependent on ``<targets>``.
 
-  **Target Options:**
-    ``DEPENDS <targets>...``
-      Specify other targets on which the external project depends. The other
-      targets will be brought up to date before any of the external project's
-      steps are executed. Because the external project uses additional custom
-      targets internally for each step, the ``DEPENDS`` option is the most
-      convenient way to ensure all of those steps depend on the other targets.
-      Simply doing
-      :command:`add_dependencies(\<name\> \<targets\>) <add_dependencies>` will
-      not make any of the steps dependent on ``<targets>``.
+``EXCLUDE_FROM_ALL <bool>``
+  When enabled, this option excludes the external project from the default
+  ALL target of the main build.
 
-    ``EXCLUDE_FROM_ALL <bool>``
-      When enabled, this option excludes the external project from the default
-      ALL target of the main build.
+``STEP_TARGETS <step-target>...``
+  Generate custom targets for the specified steps. This is required if the
+  steps need to be triggered manually or if they need to be used as
+  dependencies of other targets. If this option is not specified, the
+  default value is taken from the ``EP_STEP_TARGETS`` directory property.
+  See :command:`ExternalProject_Add_StepTargets` below for further
+  discussion of the effects of this option.
 
-    ``STEP_TARGETS <step-target>...``
-      Generate custom targets for the specified steps. This is required if the
-      steps need to be triggered manually or if they need to be used as
-      dependencies of other targets. If this option is not specified, the
-      default value is taken from the ``EP_STEP_TARGETS`` directory property.
-      See :command:`ExternalProject_Add_StepTargets` below for further
-      discussion of the effects of this option.
+``INDEPENDENT_STEP_TARGETS <step-target>...``
+  .. deprecated:: 3.19
+    This is allowed only if policy :policy:`CMP0114` is not set to ``NEW``.
 
-    ``INDEPENDENT_STEP_TARGETS <step-target>...``
-      .. deprecated:: 3.19
-        This is allowed only if policy :policy:`CMP0114` is not set to ``NEW``.
+  Generates custom targets for the specified steps and prevent these targets
+  from having the usual dependencies applied to them. If this option is not
+  specified, the default value is taken from the
+  ``EP_INDEPENDENT_STEP_TARGETS`` directory property. This option is mostly
+  useful for allowing individual steps to be driven independently, such as
+  for a CDash setup where each step should be initiated and reported
+  individually rather than as one whole build. See
+  :command:`ExternalProject_Add_StepTargets` below for further discussion
+  of the effects of this option.
 
-      Generates custom targets for the specified steps and prevent these targets
-      from having the usual dependencies applied to them. If this option is not
-      specified, the default value is taken from the
-      ``EP_INDEPENDENT_STEP_TARGETS`` directory property. This option is mostly
-      useful for allowing individual steps to be driven independently, such as
-      for a CDash setup where each step should be initiated and reported
-      individually rather than as one whole build. See
-      :command:`ExternalProject_Add_StepTargets` below for further discussion
-      of the effects of this option.
+Miscellaneous Options
+"""""""""""""""""""""
 
-  **Miscellaneous Options:**
-    ``LIST_SEPARATOR <sep>``
-      For any of the various ``..._COMMAND`` options, and ``CMAKE_ARGS``,
-      replace ``;`` with ``<sep>`` in the specified command lines.
-      This can be useful where list variables may be given in commands where
-      they should end up as space-separated arguments (``<sep>`` would be a
-      single space character string in this case).
+``LIST_SEPARATOR <sep>``
+  For any of the various ``..._COMMAND`` options, and ``CMAKE_ARGS``,
+  replace ``;`` with ``<sep>`` in the specified command lines.
+  This can be useful where list variables may be given in commands where
+  they should end up as space-separated arguments (``<sep>`` would be a
+  single space character string in this case).
 
-    ``COMMAND <cmd>...``
-      Any of the other ``..._COMMAND`` options can have additional commands
-      appended to them by following them with as many ``COMMAND ...`` options
-      as needed
-      (:manual:`generator expressions <cmake-generator-expressions(7)>` are
-      supported). For example:
+``COMMAND <cmd>...``
+  Any of the other ``..._COMMAND`` options can have additional commands
+  appended to them by following them with as many ``COMMAND ...`` options
+  as needed
+  (:manual:`generator expressions <cmake-generator-expressions(7)>` are
+  supported). For example:
 
-      .. code-block:: cmake
+  .. code-block:: cmake
 
-        ExternalProject_Add(example
-          ... # Download options, etc.
-          BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Starting $<CONFIG> build"
-          COMMAND       ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG>
-          COMMAND       ${CMAKE_COMMAND} -E echo "$<CONFIG> build complete"
-        )
+    ExternalProject_Add(example
+      ... # Download options, etc.
+      BUILD_COMMAND ${CMAKE_COMMAND} -E echo "Starting $<CONFIG> build"
+      COMMAND       ${CMAKE_COMMAND} --build <BINARY_DIR> --config $<CONFIG>
+      COMMAND       ${CMAKE_COMMAND} -E echo "$<CONFIG> build complete"
+    )
 
-  It should also be noted that each build step is created via a call to
-  :command:`ExternalProject_Add_Step`. See that command's documentation for the
-  automatic substitutions that are supported for some options.
+It should also be noted that each build step is created via a call to
+:command:`ExternalProject_Add_Step`. See that command's documentation for the
+automatic substitutions that are supported for some options.
 
 Obtaining Project Properties
-""""""""""""""""""""""""""""
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. command:: ExternalProject_Get_Property
 
@@ -914,7 +953,7 @@
     message("Source dir of myExtProj = ${SOURCE_DIR}")
 
 Explicit Step Management
-""""""""""""""""""""""""
+^^^^^^^^^^^^^^^^^^^^^^^^
 
 The ``ExternalProject_Add()`` function on its own is often sufficient for
 incorporating an external project into the main build. Certain scenarios
@@ -990,6 +1029,13 @@
     When enabled, this option specifies that the custom step should always be
     run (i.e. that it is always considered out of date).
 
+  ``JOB_SERVER_AWARE <bool>``
+    .. versionadded:: 3.28
+
+    Specifies that the custom step is aware of the GNU Make job server.
+    See the :command:`add_custom_command` documentation of its
+    ``JOB_SERVER_AWARE`` option for details.
+
   ``EXCLUDE_FROM_MAIN <bool>``
     When enabled, this option specifies that the external project's main target
     does not depend on the custom step.
@@ -2335,6 +2381,7 @@
     INDEPENDENT
     BYPRODUCTS
     ALWAYS
+    JOB_SERVER_AWARE
     EXCLUDE_FROM_MAIN
     WORKING_DIRECTORY
     LOG
@@ -2514,6 +2561,16 @@
     set(maybe_COMMAND_touch "COMMAND \${CMAKE_COMMAND} -E touch \${stamp_file}")
   endif()
 
+  get_property(job_server_aware
+    TARGET ${name}
+    PROPERTY _EP_${step}_JOB_SERVER_AWARE
+  )
+  if(job_server_aware)
+    set(maybe_JOB_SERVER_AWARE "JOB_SERVER_AWARE 1")
+  else()
+    set(maybe_JOB_SERVER_AWARE "")
+  endif()
+
   # Wrap with log script?
   get_property(log TARGET ${name} PROPERTY _EP_${step}_LOG)
   if(command AND log)
@@ -2540,6 +2597,7 @@
       COMMENT \${comment}
       COMMAND ${__cmdQuoted}
       ${maybe_COMMAND_touch}
+      ${maybe_JOB_SERVER_AWARE}
       DEPENDS \${depends}
       WORKING_DIRECTORY \${work_dir}
       VERBATIM
@@ -2767,6 +2825,7 @@
   set(comment)
   set(work_dir)
   set(extra_repo_info)
+  set(byproduct_file)
 
   if(cmd_set)
     set(work_dir ${download_dir})
@@ -2811,7 +2870,8 @@
       TARGET ${name}
       PROPERTY _EP_USES_TERMINAL_DOWNLOAD
     )
-    if(uses_terminal)
+    # The --trust-server-cert option requires --non-interactive
+    if(uses_terminal AND NOT svn_trust_cert)
       set(svn_interactive_args "")
     else()
       set(svn_interactive_args "--non-interactive")
@@ -3046,14 +3106,16 @@
           get_filename_component(fname "${fname}" NAME)
         else()
           # Fall back to a default file name.  The actual file name does not
-          # matter because it is used only internally and our extraction tool
-          # inspects the file content directly.  If it turns out the wrong URL
-          # was given that will be revealed during the build which is an easier
-          # place for users to diagnose than an error here anyway.
-          set(fname "archive.tar")
+          # matter as long as it doesn't conflict with other projects because
+          # it is used only internally and our extraction tool inspects the
+          # file content directly.  If it turns out the wrong URL was given
+          # that will be revealed during the build which is an easier place for
+          # users to diagnose than an error here anyway.
+          set(fname "${name}-archive.tar")
         endif()
         string(REPLACE ";" "-" fname "${fname}")
         set(file ${download_dir}/${fname})
+        set(byproduct_file "${download_dir}/${fname}")
         get_property(timeout TARGET ${name} PROPERTY _EP_TIMEOUT)
         get_property(inactivity_timeout
           TARGET ${name}
@@ -3230,6 +3292,7 @@
       COMMAND ${__cmdQuoted}
       WORKING_DIRECTORY \${work_dir}
       DEPENDS \${depends}
+      BYPRODUCTS \${byproduct_file}
       DEPENDEES mkdir
       ${log}
       ${uses_terminal}
@@ -3299,7 +3362,8 @@
     get_property(svn_password TARGET ${name} PROPERTY _EP_SVN_PASSWORD)
     get_property(svn_trust_cert TARGET ${name} PROPERTY _EP_SVN_TRUST_CERT)
     get_property(uses_terminal TARGET ${name} PROPERTY _EP_USES_TERMINAL_UPDATE)
-    if(uses_terminal)
+    # The --trust-server-cert option requires --non-interactive
+    if(uses_terminal AND NOT svn_trust_cert)
       set(svn_interactive_args "")
     else()
       set(svn_interactive_args "--non-interactive")
@@ -3914,6 +3978,17 @@
     PROPERTY _EP_BUILD_BYPRODUCTS
   )
 
+  get_property(build_job_server_aware
+    TARGET ${name}
+    PROPERTY _EP_BUILD_JOB_SERVER_AWARE
+  )
+  if(build_job_server_aware)
+    set(maybe_JOB_SERVER_AWARE "JOB_SERVER_AWARE 1")
+  else()
+    set(maybe_JOB_SERVER_AWARE "")
+  endif()
+
+
   set(__cmdQuoted)
   foreach(__item IN LISTS cmd)
     string(APPEND __cmdQuoted " [==[${__item}]==]")
@@ -3927,6 +4002,7 @@
       DEPENDEES configure
       DEPENDS \${file_deps}
       ALWAYS \${always}
+      ${maybe_JOB_SERVER_AWARE}
       ${log}
       ${uses_terminal}
     )"
@@ -4221,6 +4297,7 @@
     BUILD_IN_SOURCE
     BUILD_ALWAYS
     BUILD_BYPRODUCTS
+    BUILD_JOB_SERVER_AWARE
     #
     # Install step options
     #
diff --git a/Modules/FetchContent.cmake b/Modules/FetchContent.cmake
index 4ff43ed..f10684c 100644
--- a/Modules/FetchContent.cmake
+++ b/Modules/FetchContent.cmake
@@ -111,6 +111,7 @@
     FetchContent_Declare(
       <name>
       <contentOptions>...
+      [EXCLUDE_FROM_ALL]
       [SYSTEM]
       [OVERRIDE_FIND_PACKAGE |
        FIND_PACKAGE_ARGS args...]
@@ -240,6 +241,15 @@
       See the :prop_tgt:`SYSTEM` target property documentation for a more
       detailed discussion of the effects.
 
+  .. versionadded:: 3.28
+
+    ``EXCLUDE_FROM_ALL``
+      If the ``EXCLUDE_FROM_ALL`` argument is provided, then targets in the
+      subdirectory added by :command:`FetchContent_MakeAvailable` will not be
+      included in the ``ALL`` target by default, and may be excluded from IDE
+      project files. See the `:command:`add_subdirectory` `EXCLUDE_FROM_ALL``
+      argument documentation for a more detailed discussion of the effects.
+
 .. command:: FetchContent_MakeAvailable
 
   .. versionadded:: 3.14
@@ -358,6 +368,11 @@
       :command:`FetchContent_Declare`, the ``SYSTEM`` keyword will be
       added to the :command:`add_subdirectory` command as well.
 
+    .. versionadded:: 3.28
+      If the ``EXCLUDE_FROM_ALL`` keyword was included in the call to
+      :command:`FetchContent_Declare`, the ``EXCLUDE_FROM_ALL`` keyword will
+      be added to the :command:`add_subdirectory` command as well.
+
   Projects should aim to declare the details of all dependencies they might
   use before they call ``FetchContent_MakeAvailable()`` for any of them.
   This ensures that if any of the dependencies are also sub-dependencies of
@@ -1466,8 +1481,10 @@
 
   set(options
       QUIET
-      # SYSTEM has no meaning for ExternalProject, it is only used by us in
-      # FetchContent_MakeAvailable(). We need to parse and discard it here.
+      # EXCLUDE_FROM_ALL and SYSTEM have no meaning for ExternalProject, they
+      # are only used by us in FetchContent_MakeAvailable(). We need to parse
+      # and discard them here.
+      EXCLUDE_FROM_ALL
       SYSTEM
   )
   set(oneValueArgs
@@ -2030,22 +2047,28 @@
       if("${__cmake_contentDetails}" STREQUAL "")
         message(FATAL_ERROR "No details have been set for content: ${__cmake_contentName}")
       endif()
-      cmake_parse_arguments(__cmake_arg "SYSTEM" "SOURCE_SUBDIR" "" ${__cmake_contentDetails})
+      cmake_parse_arguments(__cmake_arg "EXCLUDE_FROM_ALL;SYSTEM" "SOURCE_SUBDIR" "" ${__cmake_contentDetails})
       if(NOT "${__cmake_arg_SOURCE_SUBDIR}" STREQUAL "")
         string(APPEND __cmake_srcdir "/${__cmake_arg_SOURCE_SUBDIR}")
       endif()
 
       if(EXISTS ${__cmake_srcdir}/CMakeLists.txt)
-        if (__cmake_arg_SYSTEM)
-          add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR} SYSTEM)
-        else()
-          add_subdirectory(${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR})
+        set(__cmake_add_subdirectory_args ${__cmake_srcdir} ${${__cmake_contentNameLower}_BINARY_DIR})
+        if(__cmake_arg_EXCLUDE_FROM_ALL)
+          list(APPEND __cmake_add_subdirectory_args EXCLUDE_FROM_ALL)
         endif()
+        if(__cmake_arg_SYSTEM)
+          list(APPEND __cmake_add_subdirectory_args SYSTEM)
+        endif()
+        add_subdirectory(${__cmake_add_subdirectory_args})
       endif()
 
       unset(__cmake_srcdir)
       unset(__cmake_contentDetails)
+      unset(__cmake_arg_EXCLUDE_FROM_ALL)
+      unset(__cmake_arg_SYSTEM)
       unset(__cmake_arg_SOURCE_SUBDIR)
+      unset(__cmake_add_subdirectory_args)
     endif()
   endforeach()
 
diff --git a/Modules/FindCUDAToolkit.cmake b/Modules/FindCUDAToolkit.cmake
index c2627e7..8f0f5bf 100644
--- a/Modules/FindCUDAToolkit.cmake
+++ b/Modules/FindCUDAToolkit.cmake
@@ -514,7 +514,7 @@
     executable ``nvcc``.
 
 ``CUDAToolkit_INCLUDE_DIRS``
-    The path to the CUDA Toolkit ``include`` folder containing the header files
+    List of paths to all the CUDA Toolkit folders containing header files
     required to compile a project linking against CUDA.
 
 ``CUDAToolkit_LIBRARY_DIR``
@@ -579,13 +579,28 @@
 #
 ###############################################################################
 
+function(_CUDAToolkit_build_include_dirs result_variable default_paths_variable)
+  set(content "${${default_paths_variable}}")
+  set(${result_variable} "${content}" PARENT_SCOPE)
+endfunction()
+
+function(_CUDAToolkit_build_library_dirs result_variable default_paths_variable)
+  set(content "${${default_paths_variable}}")
+  set(${result_variable} "${content}" PARENT_SCOPE)
+endfunction()
+
 # The toolkit is located during compiler detection for CUDA and stored in CMakeCUDACompiler.cmake as
-# CMAKE_CUDA_COMPILER_TOOLKIT_ROOT and CMAKE_CUDA_COMPILER_LIBRARY_ROOT.
+# - CMAKE_CUDA_COMPILER_TOOLKIT_ROOT
+# - CMAKE_CUDA_COMPILER_LIBRARY_ROOT
+# - CMAKE_CUDA_COMPILER_LIBRARY_DIRECTORIES_FROM_IMPLICIT_LIBRARIES
+# - CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES
 # We compute the rest based on those here to avoid re-searching and to avoid finding a possibly
 # different installation.
 if(CMAKE_CUDA_COMPILER_TOOLKIT_ROOT)
   set(CUDAToolkit_ROOT_DIR "${CMAKE_CUDA_COMPILER_TOOLKIT_ROOT}")
   set(CUDAToolkit_LIBRARY_ROOT "${CMAKE_CUDA_COMPILER_LIBRARY_ROOT}")
+  _CUDAToolkit_build_library_dirs(CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES CMAKE_CUDA_HOST_IMPLICIT_LINK_DIRECTORIES)
+  _CUDAToolkit_build_include_dirs(CUDAToolkit_INCLUDE_DIRECTORIES CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES)
   set(CUDAToolkit_BIN_DIR "${CUDAToolkit_ROOT_DIR}/bin")
   set(CUDAToolkit_NVCC_EXECUTABLE "${CUDAToolkit_BIN_DIR}/nvcc${CMAKE_EXECUTABLE_SUFFIX}")
   set(CUDAToolkit_VERSION "${CMAKE_CUDA_COMPILER_TOOLKIT_VERSION}")
@@ -622,11 +637,45 @@
         # NVIDIA HPC SDK, and distro's splayed layouts
         execute_process(COMMAND ${CUDAToolkit_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda"
           OUTPUT_VARIABLE _CUDA_NVCC_OUT ERROR_VARIABLE _CUDA_NVCC_OUT)
+        message(CONFIGURE_LOG
+          "Executed nvcc to extract CUDAToolkit information:\n${_CUDA_NVCC_OUT}\n\n")
         if(_CUDA_NVCC_OUT MATCHES "\\#\\$ TOP=([^\r\n]*)")
           get_filename_component(CUDAToolkit_BIN_DIR "${CMAKE_MATCH_1}/bin" ABSOLUTE)
+          message(CONFIGURE_LOG
+            "Parsed CUDAToolkit nvcc location:\n${CUDAToolkit_BIN_DIR}\n\n")
         else()
           get_filename_component(CUDAToolkit_BIN_DIR "${CUDAToolkit_NVCC_EXECUTABLE}" DIRECTORY)
         endif()
+        if(_CUDA_NVCC_OUT MATCHES "\\#\\$ INCLUDES=([^\r\n]*)")
+          separate_arguments(_nvcc_output NATIVE_COMMAND "${CMAKE_MATCH_1}")
+          foreach(line IN LISTS _nvcc_output)
+            string(REGEX REPLACE "^-I" "" line "${line}")
+            get_filename_component(line "${line}" ABSOLUTE)
+            list(APPEND _cmake_CUDAToolkit_include_directories "${line}")
+          endforeach()
+          message(CONFIGURE_LOG
+            "Parsed CUDAToolkit nvcc implicit include information:\n${_cmake_CUDAToolkit_include_directories}\n\n")
+
+          set(_cmake_CUDAToolkit_include_directories "${_cmake_CUDAToolkit_include_directories}" CACHE INTERNAL "CUDAToolkit internal list of include directories")
+        endif()
+        if(_CUDA_NVCC_OUT MATCHES "\\#\\$ LIBRARIES=([^\r\n]*)")
+          include(${CMAKE_ROOT}/Modules/CMakeParseImplicitLinkInfo.cmake)
+          set(_nvcc_link_line "cuda-fake-ld ${CMAKE_MATCH_1}")
+          CMAKE_PARSE_IMPLICIT_LINK_INFO("${_nvcc_link_line}"
+                                   _cmake_CUDAToolkit_implicit_link_libs
+                                   _cmake_CUDAToolkit_implicit_link_directories
+                                   _cmake_CUDAToolkit_implicit_frameworks
+                                   _nvcc_log
+                                   "${CMAKE_CUDA_IMPLICIT_OBJECT_REGEX}"
+                                   LANGUAGE CUDA)
+          message(CONFIGURE_LOG
+          "Parsed CUDAToolkit nvcc implicit link information:\n${_nvcc_log}\n${_cmake_CUDAToolkit_implicit_link_directories}\n\n")
+          unset(_nvcc_link_line)
+          unset(_cmake_CUDAToolkit_implicit_link_libs)
+          unset(_cmake_CUDAToolkit_implicit_frameworks)
+
+          set(_cmake_CUDAToolkit_implicit_link_directories "${_cmake_CUDAToolkit_implicit_link_directories}" CACHE INTERNAL "CUDAToolkit internal list of implicit link directories")
+        endif()
         unset(_CUDA_NVCC_OUT)
 
         set(CUDAToolkit_BIN_DIR "${CUDAToolkit_BIN_DIR}" CACHE PATH "" FORCE)
@@ -642,6 +691,15 @@
       endif()
     endif()
 
+    if(DEFINED _cmake_CUDAToolkit_include_directories)
+      _CUDAToolkit_build_include_dirs(_cmake_CUDAToolkit_contents _cmake_CUDAToolkit_include_directories)
+      set(CUDAToolkit_INCLUDE_DIRECTORIES "${_cmake_CUDAToolkit_contents}" PARENT_SCOPE)
+    endif()
+    if(DEFINED _cmake_CUDAToolkit_implicit_link_directories)
+      _CUDAToolkit_build_library_dirs(_cmake_CUDAToolkit_contents _cmake_CUDAToolkit_implicit_link_directories)
+      set(CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES "${_cmake_CUDAToolkit_contents}" PARENT_SCOPE)
+    endif()
+
     if(CUDAToolkit_BIN_DIR)
       get_filename_component(CUDAToolkit_ROOT_DIR ${CUDAToolkit_BIN_DIR} DIRECTORY ABSOLUTE)
       set(CUDAToolkit_ROOT_DIR "${CUDAToolkit_ROOT_DIR}" PARENT_SCOPE)
@@ -885,18 +943,27 @@
   set(_CUDAToolkit_Pop_Prefix True)
 endif()
 
-# CUDAToolkit_TARGET_DIR always points to the directory containing the include directory.
-# On a scattered installation /usr, on a non-scattered something like /usr/local/cuda or /usr/local/cuda-10.2/targets/aarch64-linux.
-if(EXISTS "${CUDAToolkit_TARGET_DIR}/include/cuda_runtime.h")
-  set(CUDAToolkit_INCLUDE_DIR "${CUDAToolkit_TARGET_DIR}/include")
-elseif(NOT CUDAToolkit_FIND_QUIETLY)
-  message(STATUS "Unable to find cuda_runtime.h in \"${CUDAToolkit_TARGET_DIR}/include\" for CUDAToolkit_INCLUDE_DIR.")
+
+# We don't need to verify the cuda_runtime header when we are using `nvcc` include paths
+# as the compiler being enabled means the header was found
+if(NOT CUDAToolkit_INCLUDE_DIRECTORIES)
+  # Otherwise use CUDAToolkit_TARGET_DIR to guess where the `cuda_runtime.h` is located
+  # On a scattered installation /usr, on a non-scattered something like /usr/local/cuda or /usr/local/cuda-10.2/targets/aarch64-linux.
+  if(EXISTS "${CUDAToolkit_TARGET_DIR}/include/cuda_runtime.h")
+    set(CUDAToolkit_INCLUDE_DIRECTORIES "${CUDAToolkit_TARGET_DIR}/include")
+  else()
+    message(STATUS "Unable to find cuda_runtime.h in \"${CUDAToolkit_TARGET_DIR}/include\" for CUDAToolkit_INCLUDE_DIRECTORIES.")
+  endif()
 endif()
 
 # The NVHPC layout moves math library headers and libraries to a sibling directory and it could be nested under
 # the version of the CUDA toolchain
 # Create a separate variable so this directory can be selectively added to math targets.
-if(NOT EXISTS "${CUDAToolkit_INCLUDE_DIR}/cublas_v2.h")
+find_path(CUDAToolkit_CUBLAS_INCLUDE_DIR cublas_v2.h PATHS
+  "${CUDAToolkit_INCLUDE_DIRECTORIES}"
+  NO_DEFAULT_PATH)
+
+if(NOT CUDAToolkit_CUBLAS_INCLUDE_DIR)
   file(REAL_PATH "${CUDAToolkit_TARGET_DIR}" CUDAToolkit_MATH_INCLUDE_DIR)
   cmake_path(APPEND CUDAToolkit_MATH_INCLUDE_DIR "../../math_libs/")
   if(EXISTS "${CUDAToolkit_MATH_INCLUDE_DIR}/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/")
@@ -905,22 +972,26 @@
   cmake_path(APPEND CUDAToolkit_MATH_INCLUDE_DIR "include")
   cmake_path(NORMAL_PATH CUDAToolkit_MATH_INCLUDE_DIR)
 
-  if(NOT EXISTS "${CUDAToolkit_MATH_INCLUDE_DIR}/cublas_v2.h")
-    if(NOT CUDAToolkit_FIND_QUIETLY)
-      message(STATUS "Unable to find cublas_v2.h in either \"${CUDAToolkit_INCLUDE_DIR}\" or \"${CUDAToolkit_MATH_INCLUDE_DIR}\"")
-    endif()
-    unset(CUDAToolkit_MATH_INCLUDE_DIR)
+  find_path(CUDAToolkit_CUBLAS_INCLUDE_DIR cublas_v2.h PATHS
+    "${CUDAToolkit_INCLUDE_DIRECTORIES}"
+    )
+  if(CUDAToolkit_CUBLAS_INCLUDE_DIR)
+    list(APPEND CUDAToolkit_INCLUDE_DIRECTORIES "${CUDAToolkit_CUBLAS_INCLUDE_DIR}")
   endif()
 endif()
+unset(CUDAToolkit_CUBLAS_INCLUDE_DIR CACHE)
+unset(CUDAToolkit_CUBLAS_INCLUDE_DIR)
 
 # Find the CUDA Runtime Library libcudart
 find_library(CUDA_CUDART
   NAMES cudart
+  PATHS ${CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES}
   PATH_SUFFIXES lib64 lib/x64
 )
 find_library(CUDA_CUDART
   NAMES cudart
-  PATH_SUFFIXES lib64/stubs lib/x64/stubs
+  PATHS ${CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES}
+  PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
 )
 
 if(NOT CUDA_CUDART AND NOT CUDAToolkit_FIND_QUIETLY)
@@ -937,7 +1008,7 @@
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 find_package_handle_standard_args(CUDAToolkit
   REQUIRED_VARS
-    CUDAToolkit_INCLUDE_DIR
+    CUDAToolkit_INCLUDE_DIRECTORIES
     CUDA_CUDART
     CUDAToolkit_BIN_DIR
   VERSION_VAR
@@ -946,7 +1017,6 @@
 
 unset(CUDAToolkit_ROOT_DIR)
 mark_as_advanced(CUDA_CUDART
-                 CUDAToolkit_INCLUDE_DIR
                  CUDAToolkit_NVCC_EXECUTABLE
                  CUDAToolkit_SENTINEL_FILE
                  )
@@ -954,7 +1024,7 @@
 #-----------------------------------------------------------------------------
 # Construct result variables
 if(CUDAToolkit_FOUND)
-  set(CUDAToolkit_INCLUDE_DIRS ${CUDAToolkit_INCLUDE_DIR})
+  set(CUDAToolkit_INCLUDE_DIRS "${CUDAToolkit_INCLUDE_DIRECTORIES}")
   get_filename_component(CUDAToolkit_LIBRARY_DIR ${CUDA_CUDART} DIRECTORY ABSOLUTE)
 
   # Build search paths without any symlinks
@@ -965,12 +1035,19 @@
   # search paths without symlinks
   if(CUDAToolkit_LIBRARY_DIR  MATCHES ".*/cuda/${CUDAToolkit_VERSION_MAJOR}.${CUDAToolkit_VERSION_MINOR}/lib64$")
     # Search location for math_libs/
-    file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}/../../../" _cmake_search_dir)
-    list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}")
+    block(SCOPE_FOR POLICIES)
+      cmake_policy(SET CMP0152 NEW)
+      file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}/../../../../../" _cmake_search_dir)
+      list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}")
 
-    # Search location for extras like cupti
-    file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}/../" _cmake_search_dir)
-    list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}")
+      # Search location for extras like cupti
+      file(REAL_PATH "${CUDAToolkit_LIBRARY_DIR}/../../../" _cmake_search_dir)
+      list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${_cmake_search_dir}")
+    endblock()
+  endif()
+
+  if(DEFINED CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES)
+    list(APPEND CUDAToolkit_LIBRARY_SEARCH_DIRS "${CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES}")
   endif()
 
   # If no `CUDAToolkit_LIBRARY_ROOT` exists set it based on CUDAToolkit_LIBRARY_DIR
@@ -986,7 +1063,8 @@
     unset(CUDAToolkit_possible_lib_root)
   endif()
 endif()
-
+unset(CUDAToolkit_IMPLICIT_LIBRARY_DIRECTORIES)
+unset(CUDAToolkit_INCLUDE_DIRECTORIES)
 
 #-----------------------------------------------------------------------------
 # Construct import targets
@@ -1009,17 +1087,26 @@
     )
     # Don't try any stub directories until we have exhausted all other
     # search locations.
-    find_library(CUDA_${lib_name}_LIBRARY
-      NAMES ${search_names}
-      HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS}
-            ENV CUDA_PATH
-      PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
-    )
+    set(CUDA_IMPORT_PROPERTY IMPORTED_LOCATION)
+    set(CUDA_IMPORT_TYPE     UNKNOWN)
+    if(NOT CUDA_${lib_name}_LIBRARY)
+      find_library(CUDA_${lib_name}_LIBRARY
+        NAMES ${search_names}
+        HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS}
+              ENV CUDA_PATH
+        PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
+      )
+      if(CUDA_${lib_name}_LIBRARY AND NOT WIN32)
+        # Use `IMPORTED_IMPLIB` so that we don't add a `-rpath` entry for stub directories
+        set(CUDA_IMPORT_PROPERTY IMPORTED_IMPLIB)
+        set(CUDA_IMPORT_TYPE     SHARED)
+      endif()
+    endif()
 
     mark_as_advanced(CUDA_${lib_name}_LIBRARY)
 
     if (NOT TARGET CUDA::${lib_name} AND CUDA_${lib_name}_LIBRARY)
-      add_library(CUDA::${lib_name} UNKNOWN IMPORTED)
+      add_library(CUDA::${lib_name} ${CUDA_IMPORT_TYPE} IMPORTED)
       target_include_directories(CUDA::${lib_name} SYSTEM INTERFACE "${CUDAToolkit_INCLUDE_DIRS}")
       if(DEFINED CUDAToolkit_MATH_INCLUDE_DIR)
         string(FIND ${CUDA_${lib_name}_LIBRARY} "math_libs" math_libs)
@@ -1027,7 +1114,7 @@
           target_include_directories(CUDA::${lib_name} SYSTEM INTERFACE "${CUDAToolkit_MATH_INCLUDE_DIR}")
         endif()
       endif()
-      set_property(TARGET CUDA::${lib_name} PROPERTY IMPORTED_LOCATION "${CUDA_${lib_name}_LIBRARY}")
+      set_property(TARGET CUDA::${lib_name} PROPERTY ${CUDA_IMPORT_PROPERTY} "${CUDA_${lib_name}_LIBRARY}")
       foreach(dep ${arg_DEPS})
         if(TARGET CUDA::${dep})
           target_link_libraries(CUDA::${lib_name} INTERFACE CUDA::${dep})
diff --git a/Modules/FindCURL.cmake b/Modules/FindCURL.cmake
index acb87dc..2f33dac 100644
--- a/Modules/FindCURL.cmake
+++ b/Modules/FindCURL.cmake
@@ -58,6 +58,18 @@
 
 Set ``CURL_NO_CURL_CMAKE`` to ``ON`` to disable this search.
 
+Hints
+^^^^^
+
+``CURL_USE_STATIC_LIBS``
+
+  .. versionadded:: 3.28
+
+  Set to ``TRUE`` to use static libraries.
+
+  This is meaningful only when CURL is not found via its
+  CMake Package Configuration file.
+
 #]=======================================================================]
 
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
@@ -193,6 +205,11 @@
     set_target_properties(CURL::libcurl PROPERTIES
       INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIRS}")
 
+    if(CURL_USE_STATIC_LIBS)
+      set_property(TARGET CURL::libcurl APPEND PROPERTY
+                   INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB")
+    endif()
+
     if(EXISTS "${CURL_LIBRARY}")
       set_target_properties(CURL::libcurl PROPERTIES
         IMPORTED_LINK_INTERFACE_LANGUAGES "C"
@@ -212,5 +229,11 @@
         IMPORTED_LINK_INTERFACE_LANGUAGES "C"
         IMPORTED_LOCATION_DEBUG "${CURL_LIBRARY_DEBUG}")
     endif()
+
+    if(CURL_USE_STATIC_LIBS AND MSVC)
+       set_target_properties(CURL::libcurl PROPERTIES
+           INTERFACE_LINK_LIBRARIES "normaliz.lib;ws2_32.lib;wldap32.lib")
+    endif()
+
   endif()
 endif()
diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake
index 76f4759..9903e37 100644
--- a/Modules/FindDoxygen.cmake
+++ b/Modules/FindDoxygen.cmake
@@ -463,10 +463,6 @@
     else()
         _Doxygen_get_version(candidate_version version_result "${doxy_path}")
 
-        if(version_result)
-            message(DEBUG "Unable to determine candidate doxygen version at ${doxy_path}: ${version_result}")
-        endif()
-
         find_package_check_version("${candidate_version}" valid_doxy_version
             HANDLE_VERSION_RANGE
         )
@@ -494,15 +490,18 @@
         _Doxygen_get_version(DOXYGEN_VERSION _Doxygen_version_result "${DOXYGEN_EXECUTABLE}")
 
         if(_Doxygen_version_result)
-            message(WARNING "Unable to determine doxygen version: ${_Doxygen_version_result}")
-        endif()
-
-        # Create an imported target for Doxygen
-        if(NOT TARGET Doxygen::doxygen)
-            add_executable(Doxygen::doxygen IMPORTED GLOBAL)
-            set_target_properties(Doxygen::doxygen PROPERTIES
-                IMPORTED_LOCATION "${DOXYGEN_EXECUTABLE}"
-            )
+            if(NOT Doxygen_FIND_QUIETLY)
+                message(WARNING "Doxygen executable failed unexpected while determining version (exit status: ${_Doxygen_version_result}). Disabling Doxygen.")
+            endif()
+            set(DOXYGEN_EXECUTABLE "${DOXYGEN_EXECUTABLE}-FAILED_EXECUTION-NOTFOUND")
+        else()
+            # Create an imported target for Doxygen
+            if(NOT TARGET Doxygen::doxygen)
+                add_executable(Doxygen::doxygen IMPORTED GLOBAL)
+                set_target_properties(Doxygen::doxygen PROPERTIES
+                    IMPORTED_LOCATION "${DOXYGEN_EXECUTABLE}"
+                )
+            endif()
         endif()
     endif()
 endmacro()
diff --git a/Modules/FindEXPAT.cmake b/Modules/FindEXPAT.cmake
index 3bedc73..762931e 100644
--- a/Modules/FindEXPAT.cmake
+++ b/Modules/FindEXPAT.cmake
@@ -30,6 +30,15 @@
 ``EXPAT_FOUND``
   true if the Expat headers and libraries were found.
 
+Hints
+^^^^^
+
+``EXPAT_USE_STATIC_LIBS``
+
+  .. versionadded:: 3.28
+
+  Set to ``TRUE`` to use static libraries.
+
 #]=======================================================================]
 
 find_package(PkgConfig QUIET)
@@ -43,8 +52,13 @@
 set(EXPAT_NAMES_DEBUG expatd expatwd)
 
 if(WIN32)
-  list(APPEND EXPAT_NAMES expatMT expatMD expatwMT expatwMD)
-  list(APPEND EXPAT_NAMES_DEBUG expatdMT expatdMD expatwdMT expatwdMD)
+  if(EXPAT_USE_STATIC_LIBS)
+    list(APPEND EXPAT_NAMES expatMT expatwMT)
+    list(APPEND EXPAT_NAMES_DEBUG expatdMT expatwdMT)
+  else()
+    list(APPEND EXPAT_NAMES expatMT expatMD expatwMT expatwMD)
+    list(APPEND EXPAT_NAMES_DEBUG expatdMT expatdMD expatwdMT expatwdMD)
+  endif()
 endif()
 
 # Allow EXPAT_LIBRARY to be set manually, as the location of the expat library
@@ -115,6 +129,11 @@
       IMPORTED_LINK_INTERFACE_LANGUAGES "C"
       INTERFACE_INCLUDE_DIRECTORIES "${EXPAT_INCLUDE_DIRS}")
 
+    if(EXPAT_USE_STATIC_LIBS)
+      set_property(TARGET EXPAT::EXPAT APPEND PROPERTY
+                   INTERFACE_COMPILE_DEFINITIONS "XML_STATIC")
+    endif()
+
     if(EXPAT_LIBRARY_RELEASE)
       set_property(TARGET EXPAT::EXPAT APPEND PROPERTY
         IMPORTED_CONFIGURATIONS RELEASE)
diff --git a/Modules/FindFLTK.cmake b/Modules/FindFLTK.cmake
index d54d2f6..a245c6c 100644
--- a/Modules/FindFLTK.cmake
+++ b/Modules/FindFLTK.cmake
@@ -232,7 +232,7 @@
     find_program(FLTK_CONFIG_SCRIPT fltk-config PATHS ${FLTK_BIN_DIR})
     if(FLTK_CONFIG_SCRIPT)
       if(NOT FLTK_INCLUDE_DIR)
-        exec_program(${FLTK_CONFIG_SCRIPT} ARGS --cxxflags OUTPUT_VARIABLE FLTK_CXXFLAGS)
+        execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --cxxflags OUTPUT_VARIABLE FLTK_CXXFLAGS)
         if(FLTK_CXXFLAGS)
           string(REGEX MATCHALL "-I[^ ]*" _fltk_temp_dirs ${FLTK_CXXFLAGS})
           string(REPLACE "-I" "" _fltk_temp_dirs "${_fltk_temp_dirs}")
@@ -256,7 +256,7 @@
   # Try to find FLTK library
   if(UNIX)
     if(FLTK_CONFIG_SCRIPT)
-      exec_program(${FLTK_CONFIG_SCRIPT} ARGS --libs OUTPUT_VARIABLE _FLTK_POSSIBLE_LIBS)
+      execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --libs OUTPUT_VARIABLE _FLTK_POSSIBLE_LIBS)
       if(_FLTK_POSSIBLE_LIBS)
         get_filename_component(_FLTK_POSSIBLE_LIBRARY_DIR ${_FLTK_POSSIBLE_LIBS} PATH)
       endif()
@@ -292,12 +292,12 @@
   # Find the extra libraries needed for the fltk_images library.
   if(UNIX)
     if(FLTK_CONFIG_SCRIPT)
-      exec_program(${FLTK_CONFIG_SCRIPT} ARGS --use-images --ldflags
+      execute_process(COMMAND ${FLTK_CONFIG_SCRIPT} --use-images --ldflags
         OUTPUT_VARIABLE FLTK_IMAGES_LDFLAGS)
       set(FLTK_LIBS_EXTRACT_REGEX ".*-lfltk_images (.*) -lfltk.*")
       if("${FLTK_IMAGES_LDFLAGS}" MATCHES "${FLTK_LIBS_EXTRACT_REGEX}")
         string(REGEX REPLACE " +" ";" FLTK_IMAGES_LIBS "${CMAKE_MATCH_1}")
-        # The EXEC_PROGRAM will not be inherited into subdirectories from
+        # The execute_process() will not be inherited into subdirectories from
         # the file that originally included this module.  Save the answer.
         set(FLTK_IMAGES_LIBS "${FLTK_IMAGES_LIBS}" CACHE INTERNAL
           "Extra libraries for fltk_images library.")
diff --git a/Modules/FindFLTK2.cmake b/Modules/FindFLTK2.cmake
index a43f7a4..2f6e41d 100644
--- a/Modules/FindFLTK2.cmake
+++ b/Modules/FindFLTK2.cmake
@@ -191,12 +191,12 @@
     if(UNIX)
       find_program(FLTK2_CONFIG_SCRIPT fltk2-config PATHS ${FLTK2_BIN_DIR})
       if(FLTK2_CONFIG_SCRIPT)
-        exec_program(${FLTK2_CONFIG_SCRIPT} ARGS --use-images --ldflags
+        execute_process(COMMAND ${FLTK2_CONFIG_SCRIPT} --use-images --ldflags
           OUTPUT_VARIABLE FLTK2_IMAGES_LDFLAGS)
         set(FLTK2_LIBS_EXTRACT_REGEX ".*-lfltk2_images (.*) -lfltk2.*")
         if("${FLTK2_IMAGES_LDFLAGS}" MATCHES "${FLTK2_LIBS_EXTRACT_REGEX}")
           string(REGEX REPLACE " +" ";" FLTK2_IMAGES_LIBS "${CMAKE_MATCH_1}")
-          # The EXEC_PROGRAM will not be inherited into subdirectories from
+          # The execute_process() will not be inherited into subdirectories from
           # the file that originally included this module.  Save the answer.
           set(FLTK2_IMAGES_LIBS "${FLTK2_IMAGES_LIBS}" CACHE INTERNAL
             "Extra libraries for fltk_images library.")
diff --git a/Modules/FindFreetype.cmake b/Modules/FindFreetype.cmake
index 82885cb..dcf271d 100644
--- a/Modules/FindFreetype.cmake
+++ b/Modules/FindFreetype.cmake
@@ -65,6 +65,64 @@
 # I'm going to attempt to cut out the middleman and hope
 # everything still works.
 
+set(_Freetype_args)
+if (Freetype_FIND_QUIETLY)
+  list(APPEND _Freetype_args
+    QUIET)
+endif ()
+if (Freetype_FIND_VERSION)
+  list(APPEND _Freetype_args
+    "${Freetype_FIND_VERSION}")
+  if (Freetype_FIND_VERSION_EXACT)
+    list(APPEND _Freetype_args
+      EXACT)
+  endif ()
+endif ()
+set(_Freetype_component_req)
+set(_Freetype_component_opt)
+foreach (_Freetype_component IN LISTS Freetype_FIND_COMPONENTS)
+  if (Freetype_FIND_REQUIRE_${_Freetype_component})
+    list(APPEND _Freetype_component_req
+      "${_Freetype_component}")
+  else ()
+    list(APPEND _Freetype_component_opt
+      "${_Freetype_component}")
+  endif ()
+endforeach ()
+unset(_Freetype_component)
+if (_Freetype_component_req)
+  list(APPEND _Freetype_args
+    COMPONENTS "${_Freetype_component_req}")
+endif ()
+unset(_Freetype_component_req)
+if (_Freetype_component_opt)
+  list(APPEND _Freetype_args
+    OPTIONAL_COMPONENTS "${_Freetype_component_opt}")
+endif ()
+unset(_Freetype_component_opt)
+find_package(freetype CONFIG ${_Freetype_args})
+unset(_Freetype_args)
+if (freetype_FOUND)
+  if (NOT TARGET Freetype::Freetype)
+    add_library(Freetype::Freetype IMPORTED INTERFACE)
+    set_target_properties(Freetype::Freetype PROPERTIES
+      INTERFACE_LINK_LIBRARIES freetype)
+  endif ()
+  get_property(FREETYPE_INCLUDE_DIRS TARGET freetype PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+  get_property(FREETYPE_LIBRARIES TARGET freetype PROPERTY INTERFACE_LINK_LIBRARIES)
+  get_property(_Freetype_location TARGET freetype PROPERTY LOCATION)
+  list(APPEND FREETYPE_LIBRARIES
+    "${_Freetype_location}")
+  unset(_Freetype_location)
+  set(Freetype_FOUND 1)
+  set(FREETYPE_VERSION_STRING "${freetype_VERSION}")
+  foreach (_Freetype_component IN LISTS Freetype_FIND_COMPONENTS)
+    set(Freetype_${_Freetype_component}_FOUND "${freetype_${_Freetype_component}_FOUND}")
+  endforeach ()
+  unset(_Freetype_component)
+  return ()
+endif ()
+
 set(FREETYPE_FIND_ARGS
   HINTS
     ENV FREETYPE_DIR
diff --git a/Modules/FindGLEW.cmake b/Modules/FindGLEW.cmake
index bfde40b..dff53e1 100644
--- a/Modules/FindGLEW.cmake
+++ b/Modules/FindGLEW.cmake
@@ -126,6 +126,10 @@
     set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib;.so" PARENT_SCOPE)
   elseif(APPLE AND "${shared_or_static}" MATCHES "STATIC")
     set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE)
+  elseif(WIN32 AND MINGW AND "${shared_or_static}" MATCHES "SHARED")
+    set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" PARENT_SCOPE)
+  elseif(WIN32 AND MINGW AND "${shared_or_static}" MATCHES "STATIC")
+    set(CMAKE_FIND_LIBRARY_SUFFIXES ".a" PARENT_SCOPE)
   elseif(WIN32 AND "${shared_or_static}" MATCHES "SHARED")
     set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" PARENT_SCOPE)
   elseif(WIN32 AND "${shared_or_static}" MATCHES "STATIC")
diff --git a/Modules/FindGLUT.cmake b/Modules/FindGLUT.cmake
index 09403bc..613d315 100644
--- a/Modules/FindGLUT.cmake
+++ b/Modules/FindGLUT.cmake
@@ -107,18 +107,8 @@
 
   if(GLUT_cocoa_LIBRARY AND NOT TARGET GLUT::Cocoa)
     add_library(GLUT::Cocoa UNKNOWN IMPORTED)
-    # Cocoa should always be a Framework, but we check to make sure.
-    if(GLUT_cocoa_LIBRARY MATCHES "/([^/]+)\\.framework$")
-      set(_glut_cocoa "${GLUT_cocoa_LIBRARY}/${CMAKE_MATCH_1}")
-      if(EXISTS "${_glut_cocoa}.tbd")
-        string(APPEND _glut_cocoa ".tbd")
-      endif()
-      set_target_properties(GLUT::Cocoa PROPERTIES
-        IMPORTED_LOCATION "${_glut_cocoa}")
-    else()
-      set_target_properties(GLUT::Cocoa PROPERTIES
-        IMPORTED_LOCATION "${GLUT_cocoa_LIBRARY}")
-    endif()
+    set_target_properties(GLUT::Cocoa PROPERTIES
+      IMPORTED_LOCATION "${GLUT_cocoa_LIBRARY}")
   endif()
 else()
   if(BEOS)
@@ -196,32 +186,23 @@
     add_library(GLUT::GLUT UNKNOWN IMPORTED)
     set_target_properties(GLUT::GLUT PROPERTIES
       INTERFACE_INCLUDE_DIRECTORIES "${GLUT_INCLUDE_DIRS}")
-    if(GLUT_glut_LIBRARY MATCHES "/([^/]+)\\.framework$")
-      set(_glut_glut "${GLUT_glut_LIBRARY}/${CMAKE_MATCH_1}")
-      if(EXISTS "${_glut_glut}.tbd")
-        string(APPEND _glut_glut ".tbd")
-      endif()
+    if(GLUT_glut_LIBRARY_RELEASE)
+      set_property(TARGET GLUT::GLUT APPEND PROPERTY
+        IMPORTED_CONFIGURATIONS RELEASE)
       set_target_properties(GLUT::GLUT PROPERTIES
-        IMPORTED_LOCATION "${_glut_glut}")
-    else()
-      if(GLUT_glut_LIBRARY_RELEASE)
-        set_property(TARGET GLUT::GLUT APPEND PROPERTY
-          IMPORTED_CONFIGURATIONS RELEASE)
-        set_target_properties(GLUT::GLUT PROPERTIES
-          IMPORTED_LOCATION_RELEASE "${GLUT_glut_LIBRARY_RELEASE}")
-      endif()
+        IMPORTED_LOCATION_RELEASE "${GLUT_glut_LIBRARY_RELEASE}")
+    endif()
 
-      if(GLUT_glut_LIBRARY_DEBUG)
-        set_property(TARGET GLUT::GLUT APPEND PROPERTY
-          IMPORTED_CONFIGURATIONS DEBUG)
-        set_target_properties(GLUT::GLUT PROPERTIES
-          IMPORTED_LOCATION_DEBUG "${GLUT_glut_LIBRARY_DEBUG}")
-      endif()
+    if(GLUT_glut_LIBRARY_DEBUG)
+      set_property(TARGET GLUT::GLUT APPEND PROPERTY
+        IMPORTED_CONFIGURATIONS DEBUG)
+      set_target_properties(GLUT::GLUT PROPERTIES
+        IMPORTED_LOCATION_DEBUG "${GLUT_glut_LIBRARY_DEBUG}")
+    endif()
 
-      if(NOT GLUT_glut_LIBRARY_RELEASE AND NOT GLUT_glut_LIBRARY_DEBUG)
-        set_property(TARGET GLUT::GLUT APPEND PROPERTY
-          IMPORTED_LOCATION "${GLUT_glut_LIBRARY}")
-      endif()
+    if(NOT GLUT_glut_LIBRARY_RELEASE AND NOT GLUT_glut_LIBRARY_DEBUG)
+      set_property(TARGET GLUT::GLUT APPEND PROPERTY
+        IMPORTED_LOCATION "${GLUT_glut_LIBRARY}")
     endif()
 
     if(TARGET GLUT::Xmu)
diff --git a/Modules/FindJNI.cmake b/Modules/FindJNI.cmake
index e7050a3..abc76cf 100644
--- a/Modules/FindJNI.cmake
+++ b/Modules/FindJNI.cmake
@@ -136,76 +136,76 @@
 
 # Expand {libarch} occurrences to java_libarch subdirectory(-ies) and set ${_var}
 macro(java_append_library_directories _var)
-    # Determine java arch-specific library subdir
-    # Mostly based on openjdk/jdk/make/common/shared/Platform.gmk as of openjdk
-    # 1.6.0_18 + icedtea patches. However, it would be much better to base the
-    # guess on the first part of the GNU config.guess platform triplet.
-    if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
-      if(CMAKE_LIBRARY_ARCHITECTURE STREQUAL "x86_64-linux-gnux32")
-        set(_java_libarch "x32" "amd64" "i386")
-      else()
-        set(_java_libarch "amd64" "i386")
-      endif()
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
-        set(_java_libarch "i386")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
-        set(_java_libarch "arm64" "aarch64")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^alpha")
-        set(_java_libarch "alpha")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
-        # Subdir is "arm" for both big-endian (arm) and little-endian (armel).
-        set(_java_libarch "arm" "aarch32")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
-        # mips* machines are bi-endian mostly so processor does not tell
-        # endianness of the underlying system.
-        set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}"
-            "mips" "mipsel" "mipseb" "mipsr6" "mipsr6el"
-            "mips64" "mips64el" "mips64r6" "mips64r6el"
-            "mipsn32" "mipsn32el" "mipsn32r6" "mipsn32r6el")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le")
-        set(_java_libarch "ppc64" "ppc64le")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
-        set(_java_libarch "ppc64" "ppc")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
-        set(_java_libarch "ppc" "ppc64")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc")
-        # Both flavors can run on the same processor
-        set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "sparc" "sparcv9")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)")
-        set(_java_libarch "parisc" "parisc64")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390")
-        # s390 binaries can run on s390x machines
-        set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "s390" "s390x")
-    elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sh")
-        set(_java_libarch "sh")
+  # Determine java arch-specific library subdir
+  # Mostly based on openjdk/jdk/make/common/shared/Platform.gmk as of openjdk
+  # 1.6.0_18 + icedtea patches. However, it would be much better to base the
+  # guess on the first part of the GNU config.guess platform triplet.
+  if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+    if(CMAKE_LIBRARY_ARCHITECTURE STREQUAL "x86_64-linux-gnux32")
+      set(_java_libarch "x32" "amd64" "i386")
     else()
-        set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}")
+      set(_java_libarch "amd64" "i386")
     endif()
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$")
+    set(_java_libarch "i386")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64")
+    set(_java_libarch "arm64" "aarch64")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^alpha")
+    set(_java_libarch "alpha")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm")
+    # Subdir is "arm" for both big-endian (arm) and little-endian (armel).
+    set(_java_libarch "arm" "aarch32")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^mips")
+    # mips* machines are bi-endian mostly so processor does not tell
+    # endianness of the underlying system.
+    set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}"
+        "mips" "mipsel" "mipseb" "mipsr6" "mipsr6el"
+        "mips64" "mips64el" "mips64r6" "mips64r6el"
+        "mipsn32" "mipsn32el" "mipsn32r6" "mipsn32r6el")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64le")
+    set(_java_libarch "ppc64" "ppc64le")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)64")
+    set(_java_libarch "ppc64" "ppc")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)")
+    set(_java_libarch "ppc" "ppc64")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sparc")
+    # Both flavors can run on the same processor
+    set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "sparc" "sparcv9")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)")
+    set(_java_libarch "parisc" "parisc64")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^s390")
+    # s390 binaries can run on s390x machines
+    set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}" "s390" "s390x")
+  elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^sh")
+    set(_java_libarch "sh")
+  else()
+    set(_java_libarch "${CMAKE_SYSTEM_PROCESSOR}")
+  endif()
 
-    # Append default list architectures if CMAKE_SYSTEM_PROCESSOR was empty or
-    # system is non-Linux (where the code above has not been well tested)
-    if(NOT _java_libarch OR NOT (CMAKE_SYSTEM_NAME MATCHES "Linux"))
-        list(APPEND _java_libarch "i386" "amd64" "ppc")
-    endif()
+  # Append default list architectures if CMAKE_SYSTEM_PROCESSOR was empty or
+  # system is non-Linux (where the code above has not been well tested)
+  if(NOT _java_libarch OR NOT (CMAKE_SYSTEM_NAME MATCHES "Linux"))
+    list(APPEND _java_libarch "i386" "amd64" "ppc")
+  endif()
 
-    # Sometimes ${CMAKE_SYSTEM_PROCESSOR} is added to the list to prefer
-    # current value to a hardcoded list. Remove possible duplicates.
-    list(REMOVE_DUPLICATES _java_libarch)
+  # Sometimes ${CMAKE_SYSTEM_PROCESSOR} is added to the list to prefer
+  # current value to a hardcoded list. Remove possible duplicates.
+  list(REMOVE_DUPLICATES _java_libarch)
 
-    foreach(_path ${ARGN})
-        if(_path MATCHES "{libarch}")
-            foreach(_libarch ${_java_libarch})
-                string(REPLACE "{libarch}" "${_libarch}" _newpath "${_path}")
-                if(EXISTS ${_newpath})
-                    list(APPEND ${_var} "${_newpath}")
-                endif()
-            endforeach()
-        else()
-            if(EXISTS ${_path})
-                list(APPEND ${_var} "${_path}")
-            endif()
+  foreach(_path ${ARGN})
+    if(_path MATCHES "{libarch}")
+      foreach(_libarch IN LISTS _java_libarch)
+        string(REPLACE "{libarch}" "${_libarch}" _newpath "${_path}")
+        if(EXISTS ${_newpath})
+          list(APPEND ${_var} "${_newpath}")
         endif()
-    endforeach()
+      endforeach()
+    else()
+      if(EXISTS ${_path})
+        list(APPEND ${_var} "${_path}")
+      endif()
+    endif()
+  endforeach()
 endmacro()
 
 include(${CMAKE_CURRENT_LIST_DIR}/CMakeFindJavaCommon.cmake)
@@ -235,13 +235,12 @@
 if (WIN32)
   set (_JNI_HINTS)
   macro (_JNI_GET_INSTALLED_VERSIONS _KIND)
-    execute_process(COMMAND REG QUERY "HKLM\\SOFTWARE\\JavaSoft\\${_KIND}"
-      RESULT_VARIABLE _JAVA_RESULT
-      OUTPUT_VARIABLE _JAVA_VERSIONS
-      ERROR_QUIET)
-    if (NOT  _JAVA_RESULT)
-      string (REGEX MATCHALL "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\[0-9._]+" _JNI_VERSIONS "${_JAVA_VERSIONS}")
-      string (REGEX REPLACE "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\([0-9._]+)" "\\1" _JNI_VERSIONS "${_JNI_VERSIONS}")
+  cmake_host_system_information(RESULT _JNI_VERSIONS
+    QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/JavaSoft/${_KIND}"
+    SUBKEYS)
+    if (_JNI_VERSIONS)
+      string (REGEX MATCHALL "[0-9._]+" _JNI_VERSIONS "${_JNI_VERSIONS}")
+      string (REGEX REPLACE "([0-9._]+)" "\\1" _JNI_VERSIONS "${_JNI_VERSIONS}")
       if (_JNI_VERSIONS)
         # sort versions. Most recent first
         list (SORT _JNI_VERSIONS COMPARE NATURAL ORDER DESCENDING)
@@ -340,7 +339,7 @@
   )
 
 set(JAVA_JVM_LIBRARY_DIRECTORIES)
-foreach(dir ${JAVA_AWT_LIBRARY_DIRECTORIES})
+foreach(dir IN LISTS JAVA_AWT_LIBRARY_DIRECTORIES)
   list(APPEND JAVA_JVM_LIBRARY_DIRECTORIES
     "${dir}"
     "${dir}/client"
@@ -365,14 +364,14 @@
   ${_JNI_JAVA_INCLUDE_TRIES}
   )
 
-foreach(JAVA_PROG "${JAVA_RUNTIME}" "${JAVA_COMPILE}" "${JAVA_ARCHIVE}")
+foreach(JAVA_PROG IN ITEMS "${JAVA_RUNTIME}" "${JAVA_COMPILE}" "${JAVA_ARCHIVE}")
   get_filename_component(jpath "${JAVA_PROG}" PATH)
-  foreach(JAVA_INC_PATH ../include ../java/include ../share/java/include)
+  foreach(JAVA_INC_PATH IN ITEMS ../include ../java/include ../share/java/include)
     if(EXISTS ${jpath}/${JAVA_INC_PATH})
       list(APPEND JAVA_AWT_INCLUDE_DIRECTORIES "${jpath}/${JAVA_INC_PATH}")
     endif()
   endforeach()
-  foreach(JAVA_LIB_PATH
+  foreach(JAVA_LIB_PATH IN ITEMS
     ../lib ../jre/lib ../jre/lib/i386
     ../java/lib ../java/jre/lib ../java/jre/lib/i386
     ../share/java/lib ../share/java/jre/lib ../share/java/jre/lib/i386)
@@ -429,7 +428,7 @@
   PATHS ${JAVA_AWT_LIBRARY_DIRECTORIES}
   )
 
-foreach(search ${_JNI_SEARCHES})
+foreach(search IN LISTS _JNI_SEARCHES)
   if(JVM IN_LIST JNI_FIND_COMPONENTS)
     find_library(JAVA_JVM_LIBRARY ${_JNI_${search}_JVM}
       DOC "Java Virtual Machine library"
diff --git a/Modules/FindJava.cmake b/Modules/FindJava.cmake
index e8fb120..74e424b 100644
--- a/Modules/FindJava.cmake
+++ b/Modules/FindJava.cmake
@@ -90,13 +90,12 @@
 endif()
 if (WIN32)
   macro (_JAVA_GET_INSTALLED_VERSIONS _KIND)
-    execute_process(COMMAND REG QUERY "HKLM\\SOFTWARE\\JavaSoft\\${_KIND}"
-      RESULT_VARIABLE _JAVA_RESULT
-      OUTPUT_VARIABLE _JAVA_VERSIONS
-      ERROR_QUIET)
-    if (NOT  _JAVA_RESULT)
-      string (REGEX MATCHALL "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\[0-9._]+" _JAVA_VERSIONS "${_JAVA_VERSIONS}")
-      string (REGEX REPLACE "HKEY_LOCAL_MACHINE\\\\SOFTWARE\\\\JavaSoft\\\\${_KIND}\\\\([0-9._]+)" "\\1" _JAVA_VERSIONS "${_JAVA_VERSIONS}")
+    cmake_host_system_information(RESULT _JAVA_VERSIONS
+    QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/JavaSoft/${_KIND}"
+    SUBKEYS)
+    if (_JAVA_VERSIONS)
+      string (REGEX MATCHALL "[0-9._]+" _JAVA_VERSIONS "${_JAVA_VERSIONS}")
+      string (REGEX REPLACE "([0-9._]+)" "\\1" _JAVA_VERSIONS "${_JAVA_VERSIONS}")
       if (_JAVA_VERSIONS)
         # sort versions. Most recent first
         list (SORT _JAVA_VERSIONS COMPARE NATURAL ORDER DESCENDING)
@@ -221,7 +220,7 @@
       unset(_java_var)
       set(Java_VERSION "${Java_VERSION_MAJOR}")
       if(NOT "x${Java_VERSION}" STREQUAL "x")
-        foreach(_java_c MINOR PATCH TWEAK)
+        foreach(_java_c IN ITEMS "MINOR" "PATCH" "TWEAK")
           if(NOT "x${Java_VERSION_${_java_c}}" STREQUAL "x")
             string(APPEND Java_VERSION ".${Java_VERSION_${_java_c}}")
           else()
@@ -274,7 +273,7 @@
 include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
 if(Java_FIND_COMPONENTS)
   set(_JAVA_REQUIRED_VARS)
-  foreach(component ${Java_FIND_COMPONENTS})
+  foreach(component IN LISTS Java_FIND_COMPONENTS)
     # User just want to execute some Java byte-compiled
     If(component STREQUAL "Runtime")
       list(APPEND _JAVA_REQUIRED_VARS Java_JAVA_EXECUTABLE)
@@ -316,7 +315,7 @@
     VERSION_VAR Java_VERSION
     )
   if(Java_FOUND)
-    foreach(component ${Java_FIND_COMPONENTS})
+    foreach(component IN LISTS Java_FIND_COMPONENTS)
       set(Java_${component}_FOUND TRUE)
     endforeach()
   endif()
diff --git a/Modules/FindLibXslt.cmake b/Modules/FindLibXslt.cmake
index 97943d6..a9920ee 100644
--- a/Modules/FindLibXslt.cmake
+++ b/Modules/FindLibXslt.cmake
@@ -110,7 +110,8 @@
                                   VERSION_VAR LIBXSLT_VERSION_STRING)
 
 mark_as_advanced(LIBXSLT_INCLUDE_DIR
-                 LIBXSLT_LIBRARIES
+                 LIBXSLT_LIBRARY
+                 LIBXSLT_EXSLT_INCLUDE_DIR
                  LIBXSLT_EXSLT_LIBRARY
                  LIBXSLT_XSLTPROC_EXECUTABLE)
 
diff --git a/Modules/FindLua50.cmake b/Modules/FindLua50.cmake
index 0575caa..6ba9008 100644
--- a/Modules/FindLua50.cmake
+++ b/Modules/FindLua50.cmake
@@ -5,28 +5,19 @@
 FindLua50
 ---------
 
-
-
 Locate Lua library.
-This module defines::
 
-::
+This module defines::
 
   LUA50_FOUND, if false, do not try to link to Lua
   LUA_LIBRARIES, both lua and lualib
   LUA_INCLUDE_DIR, where to find lua.h and lualib.h (and probably lauxlib.h)
 
-
-
-Note that the expected include convention is
-
-::
+Note that the expected include convention is::
 
   #include "lua.h"
 
-and not
-
-::
+and not::
 
   #include <lua/lua.h>
 
diff --git a/Modules/FindLua51.cmake b/Modules/FindLua51.cmake
index 283a3eb..405a7a7 100644
--- a/Modules/FindLua51.cmake
+++ b/Modules/FindLua51.cmake
@@ -5,29 +5,20 @@
 FindLua51
 ---------
 
-
-
 Locate Lua library.
 This module defines::
 
-::
-
   LUA51_FOUND, if false, do not try to link to Lua
   LUA_LIBRARIES
   LUA_INCLUDE_DIR, where to find lua.h
   LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8)
 
 
-
-Note that the expected include convention is
-
-::
+Note that the expected include convention is::
 
   #include "lua.h"
 
-and not
-
-::
+and not::
 
   #include <lua/lua.h>
 
diff --git a/Modules/FindMatlab.cmake b/Modules/FindMatlab.cmake
index 3ab6bc1..639cc5f 100644
--- a/Modules/FindMatlab.cmake
+++ b/Modules/FindMatlab.cmake
@@ -46,7 +46,7 @@
 
   The version given to the :command:`find_package` directive is the Matlab
   **version**, which should not be confused with the Matlab *release* name
-  (eg. `R2014`).
+  (e.g. `R2023b`).
   The :command:`matlab_get_version_from_release_name` and
   :command:`matlab_get_release_name_from_version` provide a mapping
   between the release name and the version.
@@ -57,7 +57,7 @@
 
 * Windows: The installed versions of Matlab/MCR are retrieved from the
   Windows registry
-* OS X: The installed versions of Matlab/MCR are given by the MATLAB
+* macOS: The installed versions of Matlab/MCR are given by the MATLAB
   default installation paths in ``/Application``. If no such application is
   found, it falls back to the one that might be accessible from the ``PATH``.
 * Unix: The desired Matlab should be accessible from the ``PATH``. This does
@@ -140,8 +140,8 @@
 ``Matlab_VERSION``
   .. versionadded:: 3.27
 
-  the numerical version (e.g. 9.13) of Matlab found. Not to be confused with
-  Matlab release name (e.g. R2022b) that can be obtained with
+  the numerical version (e.g. 23.2.0) of Matlab found. Not to be confused with
+  Matlab release name (e.g. R2023b) that can be obtained with
   :command:`matlab_get_release_name_from_version`.
 ``Matlab_ROOT_DIR``
   the final root of the Matlab installation determined by the FindMatlab
@@ -302,6 +302,7 @@
 endif()
 
 set(MATLAB_VERSIONS_MAPPING
+  "R2023b=23.2"
   "R2023a=9.14"
   "R2022b=9.13"
   "R2022a=9.12"
@@ -336,9 +337,7 @@
 # temporary folder for all Matlab runs
 set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab)
 
-if(NOT EXISTS "${_matlab_temporary_folder}")
-  file(MAKE_DIRECTORY "${_matlab_temporary_folder}")
-endif()
+file(MAKE_DIRECTORY "${_matlab_temporary_folder}")
 
 #[=======================================================================[.rst:
 .. command:: matlab_get_version_from_release_name
@@ -347,14 +346,14 @@
 
     matlab_get_version_from_release_name(release version)
 
-  * Input: ``release`` is the release name (R2022b)
-  * Output: ``version`` is the version of Matlab (9.13)
+  * Input: ``release`` is the release name (e.g. R2023b)
+  * Output: ``version`` is the version of Matlab (e.g. 23.2.0)
 
   Returns the version of Matlab from a release name
 #]=======================================================================]
 macro(matlab_get_version_from_release_name release_name version_name)
 
-  string(REGEX MATCHALL "${release_name}=([0-9]+\\.?[0-9]*)" _matched ${MATLAB_VERSIONS_MAPPING})
+  string(REGEX MATCHALL "${release_name}=([0-9]+\\.[0-9]+)" _matched ${MATLAB_VERSIONS_MAPPING})
 
   set(${version_name} "")
   if(NOT _matched STREQUAL "")
@@ -374,16 +373,24 @@
 
     matlab_get_release_name_from_version(version release_name)
 
-  * Input: ``version`` is the version of Matlab (9.13)
-  * Output: ``release_name`` is the release name (R2022b)
+  * Input: ``version`` is the version of Matlab (e.g. 23.2.0)
+  * Output: ``release_name`` is the release name (R2023b)
 
   Returns the release name from the version of Matlab
 #]=======================================================================]
 macro(matlab_get_release_name_from_version version release_name)
 
+  # only the major.minor version is used
+  string(REGEX MATCH "([0-9]+\\.[0-9]+)" _match ${version})
+  if(CMAKE_MATCH_1)
+    set(short_version ${CMAKE_MATCH_1})
+  else()
+    set(short_version ${version})
+  endif()
+
   set(${release_name} "")
   foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
-    string(REGEX MATCHALL "(.+)=${version}" _matched ${_var})
+    string(REGEX MATCHALL "(.+)=${short_version}" _matched ${_var})
     if(NOT _matched STREQUAL "")
       set(${release_name} ${CMAKE_MATCH_1})
       break()
@@ -393,7 +400,7 @@
   unset(_var)
   unset(_matched)
   if(${release_name} STREQUAL "")
-    message(WARNING "[MATLAB] The version ${version} is not registered")
+    message(WARNING "[MATLAB] The version ${short_version} is not registered")
   endif()
 
 endmacro()
@@ -404,7 +411,7 @@
 macro(matlab_get_supported_releases list_releases)
   set(${list_releases})
   foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
-    string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var})
+    string(REGEX MATCHALL "(.+)=([0-9]+\\.[0-9]+)" _matched ${_var})
     if(NOT _matched STREQUAL "")
       list(APPEND ${list_releases} ${CMAKE_MATCH_1})
     endif()
@@ -421,7 +428,7 @@
 macro(matlab_get_supported_versions list_versions)
   set(${list_versions})
   foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
-    string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var})
+    string(REGEX MATCHALL "(.+)=([0-9]+\\.[0-9]+)" _matched ${_var})
     if(NOT _matched STREQUAL "")
       list(APPEND ${list_versions} ${CMAKE_MATCH_2})
     endif()
@@ -459,50 +466,44 @@
 function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions)
 
   if(NOT CMAKE_HOST_WIN32)
-    message(FATAL_ERROR "[MATLAB] This macro can only be called by a windows host (call to reg.exe)")
+    message(FATAL_ERROR "[MATLAB] This macro can only be called by a Windows host")
   endif()
 
   if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64")
-    set(APPEND_REG "/reg:64")
+    set(_view "64")
   else()
-    set(APPEND_REG "/reg:32")
+    set(_view "32")
   endif()
 
   set(matlabs_from_registry)
 
   foreach(_installation_type IN ITEMS "MATLAB" "MATLAB Runtime" "MATLAB Compiler Runtime")
 
-    # /reg:64 should be added on 64 bits capable OSs in order to enable the
-    # redirection of 64 bits applications
-    execute_process(
-      COMMAND reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\${_installation_type}" /f * /k ${APPEND_REG}
-      RESULT_VARIABLE resultMatlab
-      OUTPUT_VARIABLE varMatlab
-      ERROR_VARIABLE errMatlab
-      INPUT_FILE NUL
-      )
+    cmake_host_system_information(RESULT _reg
+    QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}"
+    SUBKEYS VIEW ${_view}
+    )
 
+    if(_reg)
 
-    if(resultMatlab EQUAL 0)
+      string(REGEX MATCHALL "([0-9]+\\.[0-9]+)" _versions_regex ${_reg})
 
-      string(
-        REGEX MATCHALL "${_installation_type}\\\\([0-9]+(\\.[0-9]+)?)"
-        matlab_versions_regex ${varMatlab})
+      foreach(match IN LISTS _versions_regex)
+        string(REGEX MATCH "([0-9]+\\.[0-9]+)" current_match ${match})
 
-      foreach(match IN LISTS matlab_versions_regex)
-        string(
-          REGEX MATCH "${_installation_type}\\\\(([0-9]+)(\\.([0-9]+))?)"
-          current_match ${match})
-
-        set(_matlab_current_version ${CMAKE_MATCH_1})
-        set(current_matlab_version_major ${CMAKE_MATCH_2})
-        set(current_matlab_version_minor ${CMAKE_MATCH_4})
-        if(NOT current_matlab_version_minor)
-          set(current_matlab_version_minor "0")
+        if(NOT CMAKE_MATCH_1)
+          continue()
         endif()
 
-        list(APPEND matlabs_from_registry ${_matlab_current_version})
-        unset(_matlab_current_version)
+        cmake_host_system_information(RESULT _reg
+          QUERY WINDOWS_REGISTRY "HKLM/SOFTWARE/Mathworks/${_installation_type}/${CMAKE_MATCH_1}"
+          VALUE "MATLABROOT"
+        )
+
+        _Matlab_VersionInfoXML(${_reg} _matlab_version_tmp)
+        if(NOT "${_matlab_version_tmp}" STREQUAL "unknown")
+          list(APPEND matlabs_from_registry ${_matlab_version_tmp})
+        endif()
       endforeach()
 
     endif()
@@ -510,8 +511,7 @@
 
   if(matlabs_from_registry)
     list(REMOVE_DUPLICATES matlabs_from_registry)
-    list(SORT matlabs_from_registry COMPARE NATURAL)
-    list(REVERSE matlabs_from_registry)
+    list(SORT matlabs_from_registry COMPARE NATURAL ORDER DESCENDING)
   endif()
 
   set(${matlab_versions} ${matlabs_from_registry} PARENT_SCOPE)
@@ -529,8 +529,7 @@
   # we order from more recent to older
   if(matlab_supported_versions)
     list(REMOVE_DUPLICATES matlab_supported_versions)
-    list(SORT matlab_supported_versions COMPARE NATURAL)
-    list(REVERSE matlab_supported_versions)
+    list(SORT matlab_supported_versions COMPARE NATURAL ORDER DESCENDING)
   endif()
 
   set(${matlab_versions} ${matlab_supported_versions})
@@ -567,7 +566,7 @@
       "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]"
       ABSOLUTE)
 
-    if(EXISTS "${current_MATLAB_ROOT}")
+    if(IS_DIRECTORY "${current_MATLAB_ROOT}")
       list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT})
     endif()
 
@@ -583,7 +582,7 @@
     # remove the dot
     string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
 
-    if(EXISTS "${current_MATLAB_ROOT}")
+    if(IS_DIRECTORY "${current_MATLAB_ROOT}")
       list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
     endif()
 
@@ -599,7 +598,7 @@
     # remove the dot
     string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
 
-    if(EXISTS "${current_MATLAB_ROOT}")
+    if(IS_DIRECTORY "${current_MATLAB_ROOT}")
       list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
     endif()
 
@@ -842,7 +841,7 @@
 
     string(SUBSTRING "${_matlab_version_from_cmd}" ${index} -1 substring_ans)
     string(
-      REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)?)"
+      REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)+)"
       matlab_versions_regex
       ${substring_ans})
     foreach(match IN LISTS matlab_versions_regex)
@@ -1281,7 +1280,7 @@
   if(NOT matlab_known_version STREQUAL "NOTFOUND")
     # the version is known, we just return it
     set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE)
-    set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)" FORCE)
+    set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)")
     return()
   endif()
 
@@ -1292,7 +1291,7 @@
 
     if(EXISTS "${matlab_root}/appdata/version.xml")
       # we inspect the application version.xml file that contains the product information
-      file(STRINGS "${matlab_root}/appdata/version.xml" productinfo_string NEWLINE_CONSUME)
+      file(READ "${matlab_root}/appdata/version.xml" productinfo_string)
       string(REGEX MATCH "<installedProductData.*displayedString=\"([a-zA-Z ]+)\".*/>"
              product_reg_match
              ${productinfo_string}
@@ -1325,7 +1324,7 @@
     if(NOT _matlab_current_program)
 
       set(_find_matlab_options)
-      if(matlab_root AND EXISTS ${matlab_root})
+      if(IS_DIRECTORY "${matlab_root}")
         set(_find_matlab_options PATHS ${matlab_root} ${matlab_root}/bin NO_DEFAULT_PATH)
       endif()
 
@@ -1337,13 +1336,13 @@
         )
     endif()
 
-    if(NOT _matlab_current_program OR NOT EXISTS ${_matlab_current_program})
+    if(NOT _matlab_current_program)
       # if not found, clear the dependent variables
       if(MATLAB_FIND_DEBUG)
         message(WARNING "[MATLAB] Cannot find the main matlab program under ${matlab_root}")
       endif()
-      set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE)
-      set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE)
+      set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version")
+      set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version")
       unset(_matlab_current_program)
       unset(_matlab_current_program CACHE)
       return()
@@ -1363,10 +1362,16 @@
     # update the location of the program
     set(Matlab_PROG_VERSION_STRING_AUTO_DETECT
         ${_matlab_main_real_path_tmp}
-        CACHE INTERNAL "internal matlab location for the discovered version" FORCE)
+        CACHE INTERNAL "internal matlab location for the discovered version")
 
-    set(matlab_list_of_all_versions)
-    matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions)
+    _Matlab_VersionInfoXML(${matlab_root} _matlab_version_tmp)
+    if(NOT "${_matlab_version_tmp}" STREQUAL "unknown")
+      # at least back to R2016 VersionInfo.xml exists
+      set(matlab_list_of_all_versions ${_matlab_version_tmp})
+    else()
+      # time consuming, less stable way to find Matlab version by running Matlab
+      matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions)
+    endif()
 
     list(LENGTH matlab_list_of_all_versions list_of_all_versions_length)
     if(list_of_all_versions_length GREATER 0)
@@ -1376,40 +1381,54 @@
     endif()
 
     # set the version into the cache
-    set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)" FORCE)
+    set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)")
 
     # warning, just in case several versions found (should not happen)
     if((list_of_all_versions_length GREATER 1) AND MATLAB_FIND_DEBUG)
       message(WARNING "[MATLAB] Found several versions, taking the first one (versions found ${matlab_list_of_all_versions})")
     endif()
 
-    # return the updated value
-    set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE)
-  elseif(EXISTS "${matlab_root}/VersionInfo.xml")
+  else()
     # MCR
     # we cannot run anything in order to extract the version. We assume that the file
     # VersionInfo.xml exists under the MatlabRoot, we look for it and extract the version from there
-    set(_matlab_version_tmp "unknown")
-    file(STRINGS "${matlab_root}/VersionInfo.xml" versioninfo_string NEWLINE_CONSUME)
-
-    if(versioninfo_string)
-      # parses "<version>9.2.0.538062</version>"
-      string(REGEX MATCH "<version>(.*)</version>"
-             version_reg_match
-             ${versioninfo_string}
-            )
-
-      if(CMAKE_MATCH_1 MATCHES "(([0-9]+)\\.([0-9]+))[\\.0-9]*")
-        set(_matlab_version_tmp "${CMAKE_MATCH_1}")
-      endif()
+    _Matlab_VersionInfoXML(${matlab_root} _matlab_version_tmp)
+    if(NOT "${_matlab_version_tmp}" STREQUAL "unknown")
+      set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)")
     endif()
-    set(${matlab_final_version} "${_matlab_version_tmp}" PARENT_SCOPE)
-    set(Matlab_VERSION_STRING_INTERNAL
-        "${_matlab_version_tmp}"
-        CACHE INTERNAL "Matlab (MCR) version (automatically determined)"
-        FORCE)
   endif() # Matlab or MCR
 
+  # return the updated value
+  set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE)
+
+endfunction()
+
+
+function(_Matlab_VersionInfoXML matlab_root _version)
+
+  set(_ver "unknown")
+
+  set(_XMLfile ${matlab_root}/VersionInfo.xml)
+  if(NOT EXISTS ${_XMLfile})
+    return()
+  endif()
+
+  file(READ ${_XMLfile} versioninfo_string)
+
+  if(versioninfo_string)
+    # parses "<version>23.2.0.2365128</version>"
+    string(REGEX MATCH "<version>([0-9]+(\\.[0-9]+)+)</version>"
+      version_reg_match
+      ${versioninfo_string}
+      )
+
+    if(CMAKE_MATCH_1)
+      set(_ver "${CMAKE_MATCH_1}")
+    endif()
+  endif()
+
+  set(${_version} ${_ver} PARENT_SCOPE)
+
 endfunction()
 
 
@@ -1444,8 +1463,8 @@
 
 endfunction()
 
-# Utility function for finding Matlab or MCR on OSX
-function(_Matlab_find_instances_osx matlab_roots)
+# Utility function for finding Matlab or MCR on macOS
+function(_Matlab_find_instances_macos matlab_roots)
 
   set(_matlab_possible_roots)
   # on mac, we look for the /Application paths
@@ -1462,8 +1481,13 @@
     string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
     set(_matlab_base_path "/Applications/MATLAB_${_matlab_current_release}.app")
 
+    _Matlab_VersionInfoXML(${_matlab_base_path} _matlab_version_tmp)
+    if(NOT "${_matlab_version_tmp}" STREQUAL "unknown")
+      set(_matlab_current_version ${_matlab_version_tmp})
+    endif()
+
     # Check Matlab, has precedence over MCR
-    if(EXISTS ${_matlab_base_path})
+    if(IS_DIRECTORY "${_matlab_base_path}")
       if(MATLAB_FIND_DEBUG)
         message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_base_path}")
       endif()
@@ -1472,7 +1496,7 @@
 
     # Checks MCR
     set(_mcr_path "/Applications/MATLAB/MATLAB_Runtime/v${_matlab_current_version_without_dot}")
-    if(EXISTS "${_mcr_path}")
+    if(IS_DIRECTORY "${_mcr_path}")
       if(MATLAB_FIND_DEBUG)
         message(STATUS "[MATLAB] Found MCR version ${_matlab_current_release} (${_matlab_current_version}) in ${_mcr_path}")
       endif()
@@ -1565,7 +1589,7 @@
 if(Matlab_ROOT_DIR)
   # if the user specifies a possible root, we keep this one
 
-  if(NOT EXISTS "${Matlab_ROOT_DIR}")
+  if(NOT IS_DIRECTORY "${Matlab_ROOT_DIR}")
     # if Matlab_ROOT_DIR specified but erroneous
     if(MATLAB_FIND_DEBUG)
       message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})")
@@ -1587,8 +1611,8 @@
     _Matlab_find_instances_win32(_matlab_possible_roots_win32)
     list(APPEND _matlab_possible_roots ${_matlab_possible_roots_win32})
   elseif(APPLE)
-    _Matlab_find_instances_osx(_matlab_possible_roots_osx)
-    list(APPEND _matlab_possible_roots ${_matlab_possible_roots_osx})
+    _Matlab_find_instances_macos(_matlab_possible_roots_macos)
+    list(APPEND _matlab_possible_roots ${_matlab_possible_roots_macos})
   endif()
 endif()
 
@@ -1604,10 +1628,6 @@
   message(STATUS "[MATLAB] Matlab root folders are ${_matlab_possible_roots}")
 endif()
 
-
-
-
-
 # take the first possible Matlab root
 list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots)
 set(Matlab_VERSION_STRING "NOTFOUND")
@@ -1799,7 +1819,7 @@
 # This small stub around find_library is to prevent any pollution of CMAKE_FIND_LIBRARY_PREFIXES in the global scope.
 # This is the function to be used below instead of the find_library directives.
 function(_Matlab_find_library _matlab_library_prefix)
-  set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} ${_matlab_library_prefix})
+  list(APPEND CMAKE_FIND_LIBRARY_PREFIXES ${_matlab_library_prefix})
   find_library(${ARGN})
 endfunction()
 
diff --git a/Modules/FindOpenAL.cmake b/Modules/FindOpenAL.cmake
index 3d58569..c8e295b 100644
--- a/Modules/FindOpenAL.cmake
+++ b/Modules/FindOpenAL.cmake
@@ -106,15 +106,9 @@
 mark_as_advanced(OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
 
 if(OPENAL_FOUND AND NOT TARGET OpenAL::OpenAL)
-  if(OPENAL_LIBRARY MATCHES "/([^/]+)\\.framework$")
-    add_library(OpenAL::OpenAL INTERFACE IMPORTED)
-    set_target_properties(OpenAL::OpenAL PROPERTIES
-      INTERFACE_LINK_LIBRARIES "${OPENAL_LIBRARY}")
-  else()
-    add_library(OpenAL::OpenAL UNKNOWN IMPORTED)
-    set_target_properties(OpenAL::OpenAL PROPERTIES
-      IMPORTED_LOCATION "${OPENAL_LIBRARY}")
-  endif()
+  add_library(OpenAL::OpenAL UNKNOWN IMPORTED)
+  set_target_properties(OpenAL::OpenAL PROPERTIES
+    IMPORTED_LOCATION "${OPENAL_LIBRARY}")
   set_target_properties(OpenAL::OpenAL PROPERTIES
     INTERFACE_INCLUDE_DIRECTORIES "${OPENAL_INCLUDE_DIR}")
 endif()
diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake
index 843f787..1527c31 100644
--- a/Modules/FindOpenGL.cmake
+++ b/Modules/FindOpenGL.cmake
@@ -655,17 +655,8 @@
     # A legacy GL library is available, so use it for the legacy GL target.
     if(IS_ABSOLUTE "${OPENGL_gl_LIBRARY}")
       add_library(OpenGL::GL UNKNOWN IMPORTED)
-      if(OPENGL_gl_LIBRARY MATCHES "/([^/]+)\\.framework$")
-        set(_gl_fw "${OPENGL_gl_LIBRARY}/${CMAKE_MATCH_1}")
-        if(EXISTS "${_gl_fw}.tbd")
-          string(APPEND _gl_fw ".tbd")
-        endif()
-        set_target_properties(OpenGL::GL PROPERTIES
-          IMPORTED_LOCATION "${_gl_fw}")
-      else()
-        set_target_properties(OpenGL::GL PROPERTIES
-          IMPORTED_LOCATION "${OPENGL_gl_LIBRARY}")
-      endif()
+      set_target_properties(OpenGL::GL PROPERTIES
+        IMPORTED_LOCATION "${OPENGL_gl_LIBRARY}")
     else()
       add_library(OpenGL::GL INTERFACE IMPORTED)
       set_target_properties(OpenGL::GL PROPERTIES
@@ -709,17 +700,8 @@
   if(OPENGL_GLU_FOUND AND NOT TARGET OpenGL::GLU)
     if(IS_ABSOLUTE "${OPENGL_glu_LIBRARY}")
       add_library(OpenGL::GLU UNKNOWN IMPORTED)
-      if(OPENGL_glu_LIBRARY MATCHES "/([^/]+)\\.framework$")
-        set(_glu_fw "${OPENGL_glu_LIBRARY}/${CMAKE_MATCH_1}")
-        if(EXISTS "${_glu_fw}.tbd")
-          string(APPEND _glu_fw ".tbd")
-        endif()
-        set_target_properties(OpenGL::GLU PROPERTIES
-          IMPORTED_LOCATION "${_glu_fw}")
-      else()
-        set_target_properties(OpenGL::GLU PROPERTIES
-          IMPORTED_LOCATION "${OPENGL_glu_LIBRARY}")
-      endif()
+      set_target_properties(OpenGL::GLU PROPERTIES
+        IMPORTED_LOCATION "${OPENGL_glu_LIBRARY}")
     else()
       add_library(OpenGL::GLU INTERFACE IMPORTED)
       set_target_properties(OpenGL::GLU PROPERTIES
diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake
index cd912c3..151b215 100644
--- a/Modules/FindOpenMP.cmake
+++ b/Modules/FindOpenMP.cmake
@@ -298,8 +298,9 @@
         set("${OPENMP_LIB_NAMES_VAR}" "" PARENT_SCOPE)
       endif()
       break()
-    elseif(CMAKE_${LANG}_COMPILER_ID STREQUAL "AppleClang"
-      AND CMAKE_${LANG}_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0")
+    elseif((CMAKE_${LANG}_COMPILER_ID STREQUAL "AppleClang"
+      AND CMAKE_${LANG}_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") OR
+      (CMAKE_${LANG}_COMPILER_ID STREQUAL "Clang" AND APPLE))
 
       # Check for separate OpenMP library on AppleClang 7+
       find_library(OpenMP_libomp_LIBRARY
diff --git a/Modules/FindPhysFS.cmake b/Modules/FindPhysFS.cmake
index a32f83a..19e5ba9 100644
--- a/Modules/FindPhysFS.cmake
+++ b/Modules/FindPhysFS.cmake
@@ -5,16 +5,20 @@
 FindPhysFS
 ----------
 
+Locate PhysFS library This module defines:
 
+``PHYSFS_LIBRARY``
+  the name of the library to link against
+``PHYSFS_FOUND``
+  if false, do not try to link to PHYSFS
+``PHYSFS_INCLUDE_DIR``
+  where to find physfs.h
 
-Locate PhysFS library This module defines PHYSFS_LIBRARY, the name of
-the library to link against PHYSFS_FOUND, if false, do not try to link
-to PHYSFS PHYSFS_INCLUDE_DIR, where to find physfs.h
+``$PHYSFSDIR`` is an environment variable that would correspond to::
 
-$PHYSFSDIR is an environment variable that would correspond to the
-./configure --prefix=$PHYSFSDIR used in building PHYSFS.
+  ./configure --prefix=$PHYSFSDIR
 
-Created by Eric Wing.
+used in building PHYSFS.
 #]=======================================================================]
 
 find_path(PHYSFS_INCLUDE_DIR physfs.h
diff --git a/Modules/FindPkgConfig.cmake b/Modules/FindPkgConfig.cmake
index 02f7fb4..27d25fb 100644
--- a/Modules/FindPkgConfig.cmake
+++ b/Modules/FindPkgConfig.cmake
@@ -425,13 +425,19 @@
     unset(_pkgconfig_path)
   endif()
 
-  # Tell pkg-config not to strip any -L paths so we can search them all.
+  # Tell pkg-config not to strip any -I or -L paths so we can search them all.
   if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS})
     set(_pkgconfig_allow_system_libs_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}")
   else()
     unset(_pkgconfig_allow_system_libs_old)
   endif()
   set(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS} 1)
+  if(DEFINED ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS})
+    set(_pkgconfig_allow_system_cflags_old "$ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}")
+  else()
+    unset(_pkgconfig_allow_system_cflags_old)
+  endif()
+  set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} 1)
 endmacro()
 
 macro(_pkg_restore_path_internal)
@@ -445,6 +451,12 @@
   else()
     unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS})
   endif()
+  if(DEFINED _pkgconfig_allow_system_cflags_old)
+    set(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS} "${_pkgconfig_allow_system_cflags_old}")
+    unset(_pkgconfig_allow_system_cflags_old)
+  else()
+    unset(ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS})
+  endif()
 
   unset(_extra_paths)
   unset(_pkgconfig_path_old)
@@ -543,7 +555,7 @@
     endif()
 
     set(_pkg_check_modules_packages)
-    set(_pkg_check_modules_failed)
+    set(_pkg_check_modules_failed "")
 
     _pkg_set_path_internal()
 
@@ -551,8 +563,8 @@
     foreach (_pkg_check_modules_pkg ${_pkg_check_modules_list})
       set(_pkg_check_modules_exist_query)
 
-      # check whether version is given
-      if (_pkg_check_modules_pkg MATCHES "(.*[^><])(=|[><]=?)(.*)")
+      # check whether version is given while ignoring whitespace
+      if (_pkg_check_modules_pkg MATCHES "(.*[^>< \t])[ \t]*(=|[><]=?)[ \t]*(.*)")
         set(_pkg_check_modules_pkg_name "${CMAKE_MATCH_1}")
         set(_pkg_check_modules_pkg_op "${CMAKE_MATCH_2}")
         set(_pkg_check_modules_pkg_ver "${CMAKE_MATCH_3}")
@@ -597,14 +609,14 @@
           message(STATUS "  ${_pkgconfig_error}")
         endif()
 
-        set(_pkg_check_modules_failed 1)
+        string(APPEND _pkg_check_modules_failed " - ${_pkg_check_modules_pkg}\n")
       endif()
     endforeach()
 
     if(_pkg_check_modules_failed)
       # fail when requested
       if (${_is_required})
-        message(FATAL_ERROR "A required package was not found")
+        message(FATAL_ERROR "The following required packages were not found:\n${_pkg_check_modules_failed}")
       endif ()
     else()
       # when we are here, we checked whether requested modules
@@ -911,11 +923,20 @@
 
   .. code-block:: cmake
 
-    pkg_get_variable(<resultVar> <moduleName> <varName>)
+    pkg_get_variable(<resultVar> <moduleName> <varName>
+                     [DEFINE_VARIABLES <key>=<value>...])
 
   If ``pkg-config`` returns multiple values for the specified variable,
   ``resultVar`` will contain a :ref:`;-list <CMake Language Lists>`.
 
+  Options:
+
+  ``DEFINE_VARIABLES <key>=<value>...``
+    .. versionadded:: 3.28
+
+    Specify key-value pairs to redefine variables affecting the variable
+    retrieved with ``pkg-config``.
+
   For example:
 
   .. code-block:: cmake
@@ -923,8 +944,20 @@
     pkg_get_variable(GI_GIRDIR gobject-introspection-1.0 girdir)
 #]========================================]
 function (pkg_get_variable result pkg variable)
+  set(_multiValueArgs DEFINE_VARIABLES)
+
+  CMAKE_PARSE_ARGUMENTS(_parsedArguments "" "" "${_multiValueArgs}" ${ARGN})
+  set(defined_variables )
+  foreach(_def_var ${_parsedArguments_DEFINE_VARIABLES})
+    if(NOT _def_var MATCHES "^.+=.*$")
+      message(FATAL_ERROR "DEFINE_VARIABLES should contain arguments in the form of key=value")
+    endif()
+
+    list(APPEND defined_variables "--define-variable=${_def_var}")
+  endforeach()
+
   _pkg_set_path_internal()
-  _pkgconfig_invoke("${pkg}" "prefix" "result" "" "--variable=${variable}")
+  _pkgconfig_invoke("${pkg}" "prefix" "result" "" "--variable=${variable}" ${defined_variables})
   set("${result}"
     "${prefix_result}"
     PARENT_SCOPE)
diff --git a/Modules/FindProducer.cmake b/Modules/FindProducer.cmake
index 65495b5..8a16e8a 100644
--- a/Modules/FindProducer.cmake
+++ b/Modules/FindProducer.cmake
@@ -5,8 +5,6 @@
 FindProducer
 ------------
 
-
-
 Though Producer isn't directly part of OpenSceneGraph, its primary
 user is OSG so I consider this part of the Findosg* suite used to find
 OpenSceneGraph components.  You'll notice that I accept OSGDIR as an
@@ -17,19 +15,25 @@
 it for you.  This is to allow you control over your own system piece
 by piece in case you need to opt out of certain components or change
 the Find behavior for a particular module (perhaps because the default
-FindOpenGL.cmake module doesn't work with your system as an example).
+:module:`FindOpenGL` module doesn't work with your system as an example).
 If you want to use a more convenient module that includes everything,
-use the FindOpenSceneGraph.cmake instead of the Findosg*.cmake
+use the :module:`FindOpenSceneGraph` instead of the Findosg*.cmake
 modules.
 
-Locate Producer This module defines PRODUCER_LIBRARY PRODUCER_FOUND,
-if false, do not try to link to Producer PRODUCER_INCLUDE_DIR, where
-to find the headers
+Locate Producer This module defines:
 
-$PRODUCER_DIR is an environment variable that would correspond to the
-./configure --prefix=$PRODUCER_DIR used in building osg.
+``PRODUCER_LIBRARY``
 
-Created by Eric Wing.
+``PRODUCER_FOUND``
+  if false, do not try to link to Producer
+``PRODUCER_INCLUDE_DIR``
+  where to find the headers
+
+``$PRODUCER_DIR`` is an environment variable that would correspond to::
+
+  ./configure --prefix=$PRODUCER_DIR
+
+used in building osg.
 #]=======================================================================]
 
 # Header files are presumed to be included like
diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake
index a92fb52..008b537 100644
--- a/Modules/FindProtobuf.cmake
+++ b/Modules/FindProtobuf.cmake
@@ -136,15 +136,95 @@
     Variable to define with autogenerated Python files
   ``ARGN``
     ``.proto`` files
+
+.. command:: protobuf_generate
+
+  .. versionadded:: 3.13
+
+  Automatically generate source files from ``.proto`` schema files at build time::
+
+    protobuf_generate (
+        TARGET <target>
+        [LANGUAGE <lang>]
+        [OUT_VAR <out_var>]
+        [EXPORT_MACRO <macro>]
+        [PROTOC_OUT_DIR <dir>]
+        [PLUGIN <plugin>]
+        [PLUGIN_OPTIONS <plugin_options>]
+        [DEPENDENCIES <depends]
+        [PROTOS <protobuf_files>]
+        [IMPORT_DIRS <dirs>]
+        [GENERATE_EXTENSIONS <extensions>]
+        [PROTOC_OPTIONS <protoc_options>]
+        [APPEND_PATH])
+
+  ``APPEND_PATH``
+    A flag that causes the base path of all proto schema files to be added to
+    ``IMPORT_DIRS``.
+  ``LANGUAGE``
+    A single value: cpp or python. Determines what kind of source files are
+    being generated. Defaults to cpp.
+  ``OUT_VAR``
+    Name of a CMake variable that will be filled with the paths to the generated
+    source files.
+  ``EXPORT_MACRO``
+    Name of a macro that is applied to all generated Protobuf message classes
+    and extern variables. It can, for example, be used to declare DLL exports.
+  ``PROTOC_OUT_DIR``
+    Output directory of generated source files. Defaults to ``CMAKE_CURRENT_BINARY_DIR``.
+  ``PLUGIN``
+    .. versionadded:: 3.21
+
+    An optional plugin executable. This could, for example, be the path to
+    ``grpc_cpp_plugin``.
+  ``PLUGIN_OPTIONS``
+    .. versionadded:: 3.28
+
+    Additional options provided to the plugin, such as ``generate_mock_code=true``
+    for the gRPC cpp plugin.
+  ``DEPENDENCIES``
+    .. versionadded:: 3.28
+
+    Arguments forwarded to the ``DEPENDS`` of the underlying ``add_custom_command``
+    invocation.
+  ``TARGET``
+    CMake target that will have the generated files added as sources.
+  ``PROTOS``
+    List of proto schema files. If omitted, then every source file ending in *proto* of ``TARGET`` will be used.
+  ``IMPORT_DIRS``
+    A common parent directory for the schema files. For example, if the schema file is
+    ``proto/helloworld/helloworld.proto`` and the import directory ``proto/`` then the
+    generated files are ``${PROTOC_OUT_DIR}/helloworld/helloworld.pb.h`` and
+    ``${PROTOC_OUT_DIR}/helloworld/helloworld.pb.cc``.
+  ``GENERATE_EXTENSIONS``
+    If LANGUAGE is omitted then this must be set to the extensions that protoc generates.
+  ``PROTOC_OPTIONS``
+    .. versionadded:: 3.28
+
+    Additional arguments that are forwarded to protoc.
+
+  Example::
+
+    find_package(gRPC CONFIG REQUIRED)
+    find_package(Protobuf REQUIRED)
+    add_library(ProtoTest Test.proto)
+    target_link_libraries(ProtoTest PUBLIC gRPC::grpc++)
+    protobuf_generate(TARGET ProtoTest)
+    protobuf_generate(
+        TARGET ProtoTest
+        LANGUAGE grpc
+        PLUGIN protoc-gen-grpc=$<TARGET_FILE:gRPC::grpc_cpp_plugin>
+        PLUGIN_OPTIONS generate_mock_code=true
+        GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc)
 #]=======================================================================]
 
 function(protobuf_generate)
   set(_options APPEND_PATH DESCRIPTORS)
-  set(_singleargs LANGUAGE OUT_VAR EXPORT_MACRO PROTOC_OUT_DIR PLUGIN)
+  set(_singleargs LANGUAGE OUT_VAR EXPORT_MACRO PROTOC_OUT_DIR PLUGIN PLUGIN_OPTIONS DEPENDENCIES)
   if(COMMAND target_sources)
     list(APPEND _singleargs TARGET)
   endif()
-  set(_multiargs PROTOS IMPORT_DIRS GENERATE_EXTENSIONS)
+  set(_multiargs PROTOS IMPORT_DIRS GENERATE_EXTENSIONS PROTOC_OPTIONS)
 
   cmake_parse_arguments(protobuf_generate "${_options}" "${_singleargs}" "${_multiargs}" "${ARGN}")
 
@@ -168,9 +248,18 @@
   endif()
 
   if(protobuf_generate_EXPORT_MACRO AND protobuf_generate_LANGUAGE STREQUAL cpp)
-    set(_dll_export_decl "dllexport_decl=${protobuf_generate_EXPORT_MACRO}:")
+    set(_dll_export_decl "dllexport_decl=${protobuf_generate_EXPORT_MACRO}")
   endif()
 
+  foreach(_option ${_dll_export_decl} ${protobuf_generate_PLUGIN_OPTIONS})
+    # append comma - not using CMake lists and string replacement as users
+    # might have semicolons in options
+    if(_plugin_options)
+      set( _plugin_options "${_plugin_options},")
+    endif()
+    set(_plugin_options "${_plugin_options}${_option}")
+  endforeach()
+
   if(protobuf_generate_PLUGIN)
     set(_plugin "--plugin=${protobuf_generate_PLUGIN}")
   endif()
@@ -204,14 +293,12 @@
     # Create an include path for each file specified
     foreach(_file ${protobuf_generate_PROTOS})
       get_filename_component(_abs_file ${_file} ABSOLUTE)
-      get_filename_component(_abs_path ${_abs_file} PATH)
-      list(FIND _protobuf_include_path ${_abs_path} _contains_already)
+      get_filename_component(_abs_dir ${_abs_file} DIRECTORY)
+      list(FIND _protobuf_include_path ${_abs_dir} _contains_already)
       if(${_contains_already} EQUAL -1)
-          list(APPEND _protobuf_include_path -I ${_abs_path})
+          list(APPEND _protobuf_include_path -I ${_abs_dir})
       endif()
     endforeach()
-  else()
-    set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
   endif()
 
   foreach(DIR ${protobuf_generate_IMPORT_DIRS})
@@ -222,6 +309,10 @@
     endif()
   endforeach()
 
+  if(NOT _protobuf_include_path)
+    set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
+  endif()
+
   set(_generated_srcs_all)
   foreach(_proto ${protobuf_generate_PROTOS})
     get_filename_component(_abs_file ${_proto} ABSOLUTE)
@@ -246,12 +337,20 @@
     endif()
     list(APPEND _generated_srcs_all ${_generated_srcs})
 
+    set(_comment "Running ${protobuf_generate_LANGUAGE} protocol buffer compiler on ${_proto}")
+    if(protobuf_generate_PROTOC_OPTIONS)
+      set(_comment "${_comment}, protoc-options: ${protobuf_generate_PROTOC_OPTIONS}")
+    endif()
+    if(_plugin_options)
+      set(_comment "${_comment}, plugin-options: ${_plugin_options}")
+    endif()
+
     add_custom_command(
       OUTPUT ${_generated_srcs}
-      COMMAND  protobuf::protoc
-      ARGS --${protobuf_generate_LANGUAGE}_out ${_dll_export_decl}${protobuf_generate_PROTOC_OUT_DIR} ${_plugin} ${_dll_desc_out} ${_protobuf_include_path} ${_abs_file}
-      DEPENDS ${_abs_file} protobuf::protoc
-      COMMENT "Running ${protobuf_generate_LANGUAGE} protocol buffer compiler on ${_proto}"
+      COMMAND protobuf::protoc
+      ARGS ${protobuf_generate_PROTOC_OPTIONS} --${protobuf_generate_LANGUAGE}_out ${_plugin_options}:${protobuf_generate_PROTOC_OUT_DIR} ${_plugin} ${_dll_desc_out} ${_protobuf_include_path} ${_abs_file}
+      DEPENDS ${_abs_file} protobuf::protoc ${protobuf_generate_DEPENDENCIES}
+      COMMENT ${_comment}
       VERBATIM )
   endforeach()
 
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index d483d7f..08ab9c0 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -474,7 +474,7 @@
       set (config_flag "--${NAME}")
     endif()
     string (TOLOWER "${config_flag}" config_flag)
-    execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" ${config_flag}
+    execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} ${config_flag}
                      RESULT_VARIABLE _result
                      OUTPUT_VARIABLE _values
                      ERROR_QUIET
@@ -752,12 +752,43 @@
 endfunction()
 
 function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME)
-  cmake_parse_arguments (PARSE_ARGV 1 _PGL "INTERPRETER;COMPILER" "" "")
+  cmake_parse_arguments (PARSE_ARGV 1 _PGL "INTERPRETER;COMPILER" "CONFIG" "")
 
   unset (${_PYTHON_PGL_NAME} PARENT_SCOPE)
 
   if ((_PGL_INTERPRETER AND NOT _${_PYTHON_PREFIX}_EXECUTABLE)
-      OR (_PGL_COMPILER AND NOT _${_PYTHON_PREFIX}_COMPILER))
+      OR (_PGL_COMPILER AND NOT _${_PYTHON_PREFIX}_COMPILER)
+      OR (_PGL_CONFIG AND NOT _${_PYTHON_PREFIX}_CONFIG))
+    return()
+  endif()
+
+  if (_PGL_CONFIG)
+    # default config script can be launched directly
+    set (${_PYTHON_PGL_NAME} "${_${_PYTHON_PREFIX}_CONFIG}" PARENT_SCOPE)
+
+    if (NOT MINGW)
+      return()
+    endif()
+    # on MINGW environment, python-config script may require bash to be launched
+    execute_process (COMMAND cygpath.exe -u "${_${_PYTHON_PREFIX}_CONFIG}"
+            RESULT_VARIABLE _result
+            OUTPUT_VARIABLE _config
+            ERROR_QUIET
+            OUTPUT_STRIP_TRAILING_WHITESPACE)
+    if (_result)
+      # impossible to convert path, keep default config
+      return()
+    endif()
+    execute_process (COMMAND bash.exe "${_config}" --prefix
+            RESULT_VARIABLE _result
+            OUTPUT_QUIET
+            ERROR_QUIET)
+    if (_result)
+      # fail to execute through bash, keep default config
+      return()
+    endif()
+
+    set(${_PYTHON_PGL_NAME} bash.exe "${_config}" PARENT_SCOPE)
     return()
   endif()
 
@@ -770,7 +801,7 @@
           AND ext STREQUAL ".exe")
         set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE)
       endif()
-    else()
+    elseif (_PGL_COMPILER)
       get_filename_component (name "${_${_PYTHON_PREFIX}_COMPILER}" NAME)
       get_filename_component (ext "${_${_PYTHON_PREFIX}_COMPILER}" LAST_EXT)
       if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES
@@ -981,7 +1012,7 @@
 
   # retrieve python environment version from compiler
   set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir")
-  file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n")
+  file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]])); sys.stdout.flush()\n")
   execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_COMPILER}"
                            ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS}
                            /target:exe /embed "${working_dir}/version.py"
@@ -2520,7 +2551,7 @@
     # retrieve python environment version from compiler
     _python_get_launcher (_${_PYTHON_PREFIX}_COMPILER_LAUNCHER COMPILER)
     set (_${_PYTHON_PREFIX}_VERSION_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir")
-    file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n")
+    file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]])); sys.stdout.flush()\n")
     execute_process (COMMAND ${_${_PYTHON_PREFIX}_COMPILER_LAUNCHER} "${_${_PYTHON_PREFIX}_COMPILER}"
                              ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS}
                              /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py"
@@ -2731,20 +2762,23 @@
                         NO_DEFAULT_PATH)
         endif()
 
+        _python_get_launcher (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER CONFIG "${_${_PYTHON_PREFIX}_CONFIG}")
+
         if (_${_PYTHON_PREFIX}_CONFIG)
-          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help
+          execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --prefix
                            RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                            OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP
-                           ERROR_QUIET
+                           ERROR_VARIABLE __${_PYTHON_PREFIX}_HELP
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
           if (_${_PYTHON_PREFIX}_RESULT)
             # assume config tool is not usable
             unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
           endif()
         endif()
 
         if (_${_PYTHON_PREFIX}_CONFIG)
-          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags
+          execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --abiflags
                            RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                            OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS
                            ERROR_QUIET
@@ -2756,22 +2790,25 @@
           if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
             # Wrong ABI
             unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
           endif()
         endif()
 
         if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
           # check that config tool match library architecture
-          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
+          execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --configdir
                            RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                            OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
                            ERROR_QUIET
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
           if (_${_PYTHON_PREFIX}_RESULT)
             unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
           else()
             string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT)
             if (_${_PYTHON_PREFIX}_RESULT EQUAL -1)
               unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+              unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
             endif()
           endif()
         endif()
@@ -2817,8 +2854,10 @@
 
           unset (_${_PYTHON_PREFIX}_CONFIG_NAMES)
 
+          _python_get_launcher (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER CONFIG "${_${_PYTHON_PREFIX}_CONFIG}")
+
           if (_${_PYTHON_PREFIX}_CONFIG)
-            execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --help
+            execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --prefix
                              RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                              OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP
                              ERROR_QUIET
@@ -2826,6 +2865,7 @@
             if (_${_PYTHON_PREFIX}_RESULT)
               # assume config tool is not usable
               unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+              unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
             endif()
           endif()
 
@@ -2833,7 +2873,7 @@
             continue()
           endif()
 
-          execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --abiflags
+          execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --abiflags
                            RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                            OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS
                            ERROR_QUIET
@@ -2845,23 +2885,26 @@
           if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
             # Wrong ABI
             unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+            unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
             continue()
           endif()
 
           if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE)
             # check that config tool match library architecture
-            execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir
+            execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --configdir
                              RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT
                              OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR
                              ERROR_QUIET
                              OUTPUT_STRIP_TRAILING_WHITESPACE)
             if (_${_PYTHON_PREFIX}_RESULT)
               unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+              unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
               continue()
             endif()
             string (FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT)
             if (_${_PYTHON_PREFIX}_RESULT EQUAL -1)
               unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+              unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER)
               continue()
             endif()
           endif()
diff --git a/Modules/FindQt.cmake b/Modules/FindQt.cmake
index 44a1f41..604f8e3 100644
--- a/Modules/FindQt.cmake
+++ b/Modules/FindQt.cmake
@@ -91,7 +91,7 @@
 # now find qmake
 find_program(QT_QMAKE_EXECUTABLE_FINDQT NAMES qmake PATHS "${QT_SEARCH_PATH}/bin" "$ENV{QTDIR}/bin")
 if(QT_QMAKE_EXECUTABLE_FINDQT)
-  exec_program(${QT_QMAKE_EXECUTABLE_FINDQT} ARGS "-query QT_VERSION"
+  execute_process(COMMAND ${QT_QMAKE_EXECUTABLE_FINDQT} -query QT_VERSION
     OUTPUT_VARIABLE QTVERSION)
   if(QTVERSION MATCHES "4")
     set(QT_QMAKE_EXECUTABLE ${QT_QMAKE_EXECUTABLE_FINDQT} CACHE PATH "Qt4 qmake program.")
@@ -103,8 +103,8 @@
 endif()
 
 if(QT_QMAKE_EXECUTABLE_FINDQT)
-  exec_program( ${QT_QMAKE_EXECUTABLE_FINDQT}
-    ARGS "-query QT_INSTALL_HEADERS"
+  execute_process(COMMAND ${QT_QMAKE_EXECUTABLE_FINDQT}
+    -query QT_INSTALL_HEADERS
     OUTPUT_VARIABLE qt_headers )
 endif()
 
diff --git a/Modules/FindQt4.cmake b/Modules/FindQt4.cmake
index 3154ad3..9174bee 100644
--- a/Modules/FindQt4.cmake
+++ b/Modules/FindQt4.cmake
@@ -36,7 +36,7 @@
 ^^^^^^^^^^^^^^
 
 Qt relies on some bundled tools for code generation, such as ``moc`` for
-meta-object code generation,``uic`` for widget layout and population,
+meta-object code generation, ``uic`` for widget layout and population,
 and ``rcc`` for virtual filesystem content generation.  These tools may be
 automatically invoked by :manual:`cmake(1)` if the appropriate conditions
 are met.  See :manual:`cmake-qt(7)` for more.
diff --git a/Modules/FindQuickTime.cmake b/Modules/FindQuickTime.cmake
index 107486d..89be2a6 100644
--- a/Modules/FindQuickTime.cmake
+++ b/Modules/FindQuickTime.cmake
@@ -5,16 +5,18 @@
 FindQuickTime
 -------------
 
+Locate QuickTime This module defines:
 
+``QUICKTIME_LIBRARY``
 
-Locate QuickTime This module defines QUICKTIME_LIBRARY
-QUICKTIME_FOUND, if false, do not try to link to gdal
-QUICKTIME_INCLUDE_DIR, where to find the headers
+``QUICKTIME_FOUND``
+  if false, do not try to link to gdal
+``QUICKTIME_INCLUDE_DIR``
+  where to find the headers
 
-$QUICKTIME_DIR is an environment variable that would correspond to the
-./configure --prefix=$QUICKTIME_DIR
+``$QUICKTIME_DIR`` is an environment variable that would correspond to::
 
-Created by Eric Wing.
+  ./configure --prefix=$QUICKTIME_DIR
 #]=======================================================================]
 
 find_path(QUICKTIME_INCLUDE_DIR QuickTime/QuickTime.h QuickTime.h
diff --git a/Modules/FindRuby.cmake b/Modules/FindRuby.cmake
index d82f41d..4fe8bd5 100644
--- a/Modules/FindRuby.cmake
+++ b/Modules/FindRuby.cmake
@@ -8,7 +8,7 @@
 Find Ruby
 
 This module finds if Ruby is installed and determines where the
-include files and libraries are.  Ruby 1.8 through 3.1 are
+include files and libraries are.  Ruby 1.8 through 3.2 are
 supported.
 
 The minimum required version of Ruby can be specified using the
@@ -136,13 +136,13 @@
 
 # Set name of possible executables, ignoring the minor
 # Eg:
-# 2.1.1 => from ruby31 to ruby21 included
-# 2.1   => from ruby31 to ruby21 included
-# 2     => from ruby31 to ruby20 included
-# empty => from ruby31 to ruby18 included
+# 2.1.1 => from ruby32 to ruby21 included
+# 2.1   => from ruby32 to ruby21 included
+# 2     => from ruby32 to ruby20 included
+# empty => from ruby32 to ruby18 included
 if(NOT Ruby_FIND_VERSION_EXACT)
 
-  foreach(_ruby_version RANGE 31 18 -1)
+  foreach(_ruby_version RANGE 32 18 -1)
     string(SUBSTRING "${_ruby_version}" 0 1 _ruby_major_version)
     string(SUBSTRING "${_ruby_version}" 1 1 _ruby_minor_version)
 
diff --git a/Modules/FindTIFF.cmake b/Modules/FindTIFF.cmake
index ed2657c..4a93c04 100644
--- a/Modules/FindTIFF.cmake
+++ b/Modules/FindTIFF.cmake
@@ -65,6 +65,79 @@
   Debug and Release variants are found separately.
 #]=======================================================================]
 
+set(_TIFF_args)
+if (TIFF_FIND_QUIETLY)
+  list(APPEND _TIFF_args
+    QUIET)
+endif ()
+if (TIFF_FIND_VERSION)
+  list(APPEND _TIFF_args
+    "${TIFF_FIND_VERSION}")
+  if (TIFF_FIND_VERSION_EXACT)
+    list(APPEND _TIFF_args
+      EXACT)
+  endif ()
+endif ()
+set(_TIFF_component_req)
+set(_TIFF_component_opt)
+foreach (_TIFF_component IN LISTS TIFF_FIND_COMPONENTS)
+  if (TIFF_FIND_REQUIRE_${_TIFF_component})
+    list(APPEND _TIFF_component_req
+      "${_TIFF_component}")
+  else ()
+    list(APPEND _TIFF_component_opt
+      "${_TIFF_component}")
+  endif ()
+endforeach ()
+unset(_TIFF_component)
+if (_TIFF_component_req)
+  list(APPEND _TIFF_args
+    COMPONENTS "${_TIFF_component_req}")
+endif ()
+unset(_TIFF_component_req)
+if (_TIFF_component_opt)
+  list(APPEND _TIFF_args
+    OPTIONAL_COMPONENTS "${_TIFF_component_opt}")
+endif ()
+unset(_TIFF_component_opt)
+find_package(tiff CONFIG ${_TIFF_args})
+unset(_TIFF_args)
+if (tiff_FOUND)
+  if (NOT TARGET TIFF::TIFF)
+    add_library(TIFF::TIFF IMPORTED INTERFACE)
+    set_target_properties(TIFF::TIFF PROPERTIES
+      INTERFACE_LINK_LIBRARIES TIFF::tiff)
+  endif ()
+  get_property(TIFF_INCLUDE_DIRS TARGET TIFF::tiff PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
+  get_property(TIFF_LIBRARIES TARGET TIFF::tiff PROPERTY INTERFACE_LINK_LIBRARIES)
+  get_property(_TIFF_location TARGET TIFF::tiff PROPERTY LOCATION)
+  list(APPEND TIFF_LIBRARIES
+    "${_TIFF_location}")
+  unset(_TIFF_location)
+  set(TIFF_FOUND 1)
+  if("CXX" IN_LIST TIFF_FIND_COMPONENTS)
+    if (TARGET TIFF::CXX)
+      get_property(_TIFF_CXX_location TARGET TIFF::CXX PROPERTY LOCATION)
+      list(APPEND TIFF_LIBRARIES ${_TIFF_CXX_location})
+      unset(_TIFF_CXX_location)
+      set(TIFF_CXX_FOUND 1)
+    else ()
+      set(TIFF_CXX_FOUND 0)
+      if (TIFF_FIND_REQUIRED_CXX)
+        set(TIFF_FOUND 0)
+        list(APPEND TIFF_NOT_FOUND_REASON
+          "No C++ bindings target found")
+      endif ()
+    endif ()
+  endif ()
+  set(TIFF_VERSION_STRING "${tiff_VERSION}")
+  foreach (_TIFF_component IN LISTS TIFF_FIND_COMPONENTS)
+    set(TIFF_${_TIFF_component}_FOUND "${tiff_${_TIFF_component}_FOUND}")
+  endforeach ()
+  unset(_TIFF_component)
+  return ()
+endif ()
+
 cmake_policy(PUSH)
 cmake_policy(SET CMP0057 NEW)
 
diff --git a/Modules/Findosg.cmake b/Modules/Findosg.cmake
index 027f315..9e952c9 100644
--- a/Modules/Findosg.cmake
+++ b/Modules/Findosg.cmake
@@ -7,32 +7,40 @@
 
 
 
-NOTE: It is highly recommended that you use the new
-FindOpenSceneGraph.cmake introduced in CMake 2.6.3 and not use this
-Find module directly.
+.. note::
+  It is highly recommended that you use the new
+  :module:`FindOpenSceneGraph` introduced in CMake 2.6.3 and not use this
+  Find module directly.
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osg This module defines
+Locate osg This module defines:
 
-OSG_FOUND - Was the Osg found? OSG_INCLUDE_DIR - Where to find the
-headers OSG_LIBRARIES - The libraries to link against for the OSG (use
-this)
+``OSG_FOUND``
+  Was the Osg found?
+``OSG_INCLUDE_DIR``
+  Where to find theheaders
+``OSG_LIBRARIES``
+  The libraries to link against for the OSG (use this)
+``OSG_LIBRARY``
+  The OSG library
+``OSG_LIBRARY_DEBUG``
+  The OSG debug library
 
-OSG_LIBRARY - The OSG library OSG_LIBRARY_DEBUG - The OSG debug
-library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgAnimation.cmake b/Modules/FindosgAnimation.cmake
index 65e3016..e31987e 100644
--- a/Modules/FindosgAnimation.cmake
+++ b/Modules/FindosgAnimation.cmake
@@ -7,29 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgAnimation This module defines
+Locate osgAnimation This module defines:
 
-OSGANIMATION_FOUND - Was osgAnimation found? OSGANIMATION_INCLUDE_DIR
-- Where to find the headers OSGANIMATION_LIBRARIES - The libraries to
-link against for the OSG (use this)
+``OSGANIMATION_FOUND``
+  Was osgAnimation found?
+``OSGANIMATION_INCLUDE_DIR``
+  Where to find the headers
+``OSGANIMATION_LIBRARIES``
+  The libraries to link against for the OSG (use this)
+``OSGANIMATION_LIBRARY``
+  The OSG library
+``OSGANIMATION_LIBRARY_DEBUG``
+  The OSG debug library
 
-OSGANIMATION_LIBRARY - The OSG library OSGANIMATION_LIBRARY_DEBUG -
-The OSG debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
 
+used in building osg.
 Created by Eric Wing.
 #]=======================================================================]
 
diff --git a/Modules/FindosgDB.cmake b/Modules/FindosgDB.cmake
index a28f650..9366d21 100644
--- a/Modules/FindosgDB.cmake
+++ b/Modules/FindosgDB.cmake
@@ -37,7 +37,9 @@
 
 ``$OSGDIR`` is an environment variable that would correspond to::
 
-  ./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 #]=======================================================================]
 
diff --git a/Modules/FindosgFX.cmake b/Modules/FindosgFX.cmake
index 438fab7..79362ef 100644
--- a/Modules/FindosgFX.cmake
+++ b/Modules/FindosgFX.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgFX This module defines
+Locate osgFX This module defines:
 
-OSGFX_FOUND - Was osgFX found? OSGFX_INCLUDE_DIR - Where to find the
-headers OSGFX_LIBRARIES - The libraries to link against for the osgFX
-(use this)
+``OSGFX_FOUND``
+  Was osgFX found?
+``OSGFX_INCLUDE_DIR``
+  Where to find the headers
+``OSGFX_LIBRARIES``
+  The libraries to link against for the osgFX (use this)
+``OSGFX_LIBRARY``
+  The osgFX library
+``OSGFX_LIBRARY_DEBUG``
+  The osgFX debug library
 
-OSGFX_LIBRARY - The osgFX library OSGFX_LIBRARY_DEBUG - The osgFX
-debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgGA.cmake b/Modules/FindosgGA.cmake
index 7b6ef30..7ebcce8 100644
--- a/Modules/FindosgGA.cmake
+++ b/Modules/FindosgGA.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgGA This module defines
+Locate osgGA This module defines:
 
-OSGGA_FOUND - Was osgGA found? OSGGA_INCLUDE_DIR - Where to find the
-headers OSGGA_LIBRARIES - The libraries to link against for the osgGA
-(use this)
+``OSGGA_FOUND``
+  Was osgGA found?
+``OSGGA_INCLUDE_DIR``
+  Where to find the headers
+``OSGGA_LIBRARIES``
+  The libraries to link against for the osgGA (use this)
+``OSGGA_LIBRARY``
+  The osgGA library
+``OSGGA_LIBRARY_DEBUG``
+  The osgGA debug library
 
-OSGGA_LIBRARY - The osgGA library OSGGA_LIBRARY_DEBUG - The osgGA
-debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgIntrospection.cmake b/Modules/FindosgIntrospection.cmake
index 625e4c2..c0c28f2 100644
--- a/Modules/FindosgIntrospection.cmake
+++ b/Modules/FindosgIntrospection.cmake
@@ -7,29 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgINTROSPECTION This module defines
+Locate osgINTROSPECTION This module defines:
 
-OSGINTROSPECTION_FOUND - Was osgIntrospection found?
-OSGINTROSPECTION_INCLUDE_DIR - Where to find the headers
-OSGINTROSPECTION_LIBRARIES - The libraries to link for
-osgIntrospection (use this)
+``OSGINTROSPECTION_FOUND``
+  Was osgIntrospection found?
+``OSGINTROSPECTION_INCLUDE_DIR``
+  Where to find the headers
+``OSGINTROSPECTION_LIBRARIES``
+  The libraries to link for osgIntrospection (use this)
+``OSGINTROSPECTION_LIBRARY``
+  The osgIntrospection library
+``OSGINTROSPECTION_LIBRARY_DEBUG``
+  The osgIntrospection debug library
 
-OSGINTROSPECTION_LIBRARY - The osgIntrospection library
-OSGINTROSPECTION_LIBRARY_DEBUG - The osgIntrospection debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgManipulator.cmake b/Modules/FindosgManipulator.cmake
index 857ff5d..47c74f6 100644
--- a/Modules/FindosgManipulator.cmake
+++ b/Modules/FindosgManipulator.cmake
@@ -7,29 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgManipulator This module defines
+Locate osgManipulator This module defines:
 
-OSGMANIPULATOR_FOUND - Was osgManipulator found?
-OSGMANIPULATOR_INCLUDE_DIR - Where to find the headers
-OSGMANIPULATOR_LIBRARIES - The libraries to link for osgManipulator
-(use this)
+``OSGMANIPULATOR_FOUND``
+  Was osgManipulator found?
+``OSGMANIPULATOR_INCLUDE_DIR``
+  Where to find the headers
+``OSGMANIPULATOR_LIBRARIES``
+  The libraries to link for osgManipulator (use this)
+``OSGMANIPULATOR_LIBRARY``
+  The osgManipulator library
+``OSGMANIPULATOR_LIBRARY_DEBUG``
+  The osgManipulator debug library
 
-OSGMANIPULATOR_LIBRARY - The osgManipulator library
-OSGMANIPULATOR_LIBRARY_DEBUG - The osgManipulator debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgParticle.cmake b/Modules/FindosgParticle.cmake
index 91a30dc..cbe033d 100644
--- a/Modules/FindosgParticle.cmake
+++ b/Modules/FindosgParticle.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgParticle This module defines
+Locate osgParticle This module defines:
 
-OSGPARTICLE_FOUND - Was osgParticle found? OSGPARTICLE_INCLUDE_DIR -
-Where to find the headers OSGPARTICLE_LIBRARIES - The libraries to
-link for osgParticle (use this)
+``OSGPARTICLE_FOUND``
+  Was osgParticle found?
+``OSGPARTICLE_INCLUDE_DIR``
+  Where to find the headers
+``OSGPARTICLE_LIBRARIES``
+  The libraries to link for osgParticle (use this)
+``OSGPARTICLE_LIBRARY``
+  The osgParticle library
+``OSGPARTICLE_LIBRARY_DEBUG``
+  The osgParticle debug library
 
-OSGPARTICLE_LIBRARY - The osgParticle library
-OSGPARTICLE_LIBRARY_DEBUG - The osgParticle debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgPresentation.cmake b/Modules/FindosgPresentation.cmake
index eae75d6..e8c8b4f 100644
--- a/Modules/FindosgPresentation.cmake
+++ b/Modules/FindosgPresentation.cmake
@@ -7,30 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgPresentation This module defines
+Locate osgPresentation This module defines:
 
-OSGPRESENTATION_FOUND - Was osgPresentation found?
-OSGPRESENTATION_INCLUDE_DIR - Where to find the headers
-OSGPRESENTATION_LIBRARIES - The libraries to link for osgPresentation
-(use this)
+``OSGPRESENTATION_FOUND``
+  Was osgPresentation found?
+``OSGPRESENTATION_INCLUDE_DIR``
+  Where to find the headers
+``OSGPRESENTATION_LIBRARIES``
+  The libraries to link for osgPresentation (use this)
+``OSGPRESENTATION_LIBRARY``
+  The osgPresentation library
+``OSGPRESENTATION_LIBRARY_DEBUG``
+  The osgPresentation debug library
 
-OSGPRESENTATION_LIBRARY - The osgPresentation library
-OSGPRESENTATION_LIBRARY_DEBUG - The osgPresentation debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
 
+used in building osg.
 Created by Eric Wing.  Modified to work with osgPresentation by Robert
 Osfield, January 2012.
 #]=======================================================================]
diff --git a/Modules/FindosgProducer.cmake b/Modules/FindosgProducer.cmake
index 33b9f73..92f0d20 100644
--- a/Modules/FindosgProducer.cmake
+++ b/Modules/FindosgProducer.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgProducer This module defines
+Locate osgProducer This module defines:
 
-OSGPRODUCER_FOUND - Was osgProducer found? OSGPRODUCER_INCLUDE_DIR -
-Where to find the headers OSGPRODUCER_LIBRARIES - The libraries to
-link for osgProducer (use this)
+``OSGPRODUCER_FOUND``
+  Was osgProducer found?
+``OSGPRODUCER_INCLUDE_DIR``
+  Where to find the headers
+``OSGPRODUCER_LIBRARIES``
+  The libraries to link for osgProducer (use this)
+``OSGPRODUCER_LIBRARY``
+  The osgProducer library
+``OSGPRODUCER_LIBRARY_DEBUG``
+  The osgProducer debug library
 
-OSGPRODUCER_LIBRARY - The osgProducer library
-OSGPRODUCER_LIBRARY_DEBUG - The osgProducer debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgQt.cmake b/Modules/FindosgQt.cmake
index cf35630..2ad7174 100644
--- a/Modules/FindosgQt.cmake
+++ b/Modules/FindosgQt.cmake
@@ -7,27 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgQt This module defines
+Locate osgQt This module defines:
 
-OSGQT_FOUND - Was osgQt found? OSGQT_INCLUDE_DIR - Where to find the
-headers OSGQT_LIBRARIES - The libraries to link for osgQt (use this)
+``OSGQT_FOUND``
+  Was osgQt found?
+``OSGQT_INCLUDE_DIR``
+  Where to find the headers
+``OSGQT_LIBRARIES``
+  The libraries to link for osgQt (use this)
+``OSGQT_LIBRARY``
+  The osgQt library
+``OSGQT_LIBRARY_DEBUG``
+  The osgQt debug library
 
-OSGQT_LIBRARY - The osgQt library OSGQT_LIBRARY_DEBUG - The osgQt
-debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.  Modified to work with osgQt by Robert Osfield,
 January 2012.
diff --git a/Modules/FindosgShadow.cmake b/Modules/FindosgShadow.cmake
index 0049c4e..12eb9da 100644
--- a/Modules/FindosgShadow.cmake
+++ b/Modules/FindosgShadow.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgShadow This module defines
+Locate osgShadow This module defines:
 
-OSGSHADOW_FOUND - Was osgShadow found? OSGSHADOW_INCLUDE_DIR - Where
-to find the headers OSGSHADOW_LIBRARIES - The libraries to link for
-osgShadow (use this)
+``OSGSHADOW_FOUND``
+  Was osgShadow found?
+``OSGSHADOW_INCLUDE_DIR``
+  Where to find the headers
+``OSGSHADOW_LIBRARIES``
+  The libraries to link for osgShadow (use this)
+``OSGSHADOW_LIBRARY``
+  The osgShadow library
+``OSGSHADOW_LIBRARY_DEBUG``
+  The osgShadow debug library
 
-OSGSHADOW_LIBRARY - The osgShadow library OSGSHADOW_LIBRARY_DEBUG -
-The osgShadow debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgSim.cmake b/Modules/FindosgSim.cmake
index 43ba542..37ef03c 100644
--- a/Modules/FindosgSim.cmake
+++ b/Modules/FindosgSim.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgSim This module defines
+Locate osgSim This module defines:
 
-OSGSIM_FOUND - Was osgSim found? OSGSIM_INCLUDE_DIR - Where to find
-the headers OSGSIM_LIBRARIES - The libraries to link for osgSim (use
-this)
+``OSGSIM_FOUND``
+  Was osgSim found?
+``OSGSIM_INCLUDE_DIR``
+  Where to find the headers
+``OSGSIM_LIBRARIES``
+  The libraries to link for osgSim (use this)
+``OSGSIM_LIBRARY``
+  The osgSim library
+``OSGSIM_LIBRARY_DEBUG``
+  The osgSim debug library
 
-OSGSIM_LIBRARY - The osgSim library OSGSIM_LIBRARY_DEBUG - The osgSim
-debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgTerrain.cmake b/Modules/FindosgTerrain.cmake
index c6f5b69..a2de4ea 100644
--- a/Modules/FindosgTerrain.cmake
+++ b/Modules/FindosgTerrain.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgTerrain This module defines
+Locate osgTerrain This module defines:
 
-OSGTERRAIN_FOUND - Was osgTerrain found? OSGTERRAIN_INCLUDE_DIR -
-Where to find the headers OSGTERRAIN_LIBRARIES - The libraries to link
-for osgTerrain (use this)
+``OSGTERRAIN_FOUND``
+  Was osgTerrain found?
+``OSGTERRAIN_INCLUDE_DIR``
+  Where to find the headers
+``OSGTERRAIN_LIBRARIES``
+  The libraries to link for osgTerrain (use this)
+``OSGTERRAIN_LIBRARY``
+  The osgTerrain library
+``OSGTERRAIN_LIBRARY_DEBUG``
+  The osgTerrain debug library
 
-OSGTERRAIN_LIBRARY - The osgTerrain library OSGTERRAIN_LIBRARY_DEBUG -
-The osgTerrain debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgText.cmake b/Modules/FindosgText.cmake
index fd3c232..7646ad0 100644
--- a/Modules/FindosgText.cmake
+++ b/Modules/FindosgText.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgText This module defines
+Locate osgText This module defines:
 
-OSGTEXT_FOUND - Was osgText found? OSGTEXT_INCLUDE_DIR - Where to find
-the headers OSGTEXT_LIBRARIES - The libraries to link for osgText (use
-this)
+``OSGTEXT_FOUND``
+  Was osgText found?
+``OSGTEXT_INCLUDE_DIR``
+  Where to find the headers
+``OSGTEXT_LIBRARIES``
+  The libraries to link for osgText (use this)
+``OSGTEXT_LIBRARY``
+  The osgText library
+``OSGTEXT_LIBRARY_DEBUG``
+  The osgText debug library
 
-OSGTEXT_LIBRARY - The osgText library OSGTEXT_LIBRARY_DEBUG - The
-osgText debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgUtil.cmake b/Modules/FindosgUtil.cmake
index e84727a..a34fea0 100644
--- a/Modules/FindosgUtil.cmake
+++ b/Modules/FindosgUtil.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgUtil This module defines
+Locate osgUtil This module defines:
 
-OSGUTIL_FOUND - Was osgUtil found? OSGUTIL_INCLUDE_DIR - Where to find
-the headers OSGUTIL_LIBRARIES - The libraries to link for osgUtil (use
-this)
+``OSGUTIL_FOUND``
+  Was osgUtil found?
+``OSGUTIL_INCLUDE_DIR``
+  Where to find the headers
+``OSGUTIL_LIBRARIES``
+  The libraries to link for osgUtil (use this)
+``OSGUTIL_LIBRARY``
+  The osgUtil library
+``OSGUTIL_LIBRARY_DEBUG``
+  The osgUtil debug library
 
-OSGUTIL_LIBRARY - The osgUtil library OSGUTIL_LIBRARY_DEBUG - The
-osgUtil debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgViewer.cmake b/Modules/FindosgViewer.cmake
index 2174357..c3834e8 100644
--- a/Modules/FindosgViewer.cmake
+++ b/Modules/FindosgViewer.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgViewer This module defines
+Locate osgViewer This module defines:
 
-OSGVIEWER_FOUND - Was osgViewer found? OSGVIEWER_INCLUDE_DIR - Where
-to find the headers OSGVIEWER_LIBRARIES - The libraries to link for
-osgViewer (use this)
+``OSGVIEWER_FOUND``
+  Was osgViewer found?
+``OSGVIEWER_INCLUDE_DIR``
+  Where to find the headers
+``OSGVIEWER_LIBRARIES``
+  The libraries to link for osgViewer (use this)
+``OSGVIEWER_LIBRARY``
+  The osgViewer library
+``OSGVIEWER_LIBRARY_DEBUG``
+  The osgViewer debug library
 
-OSGVIEWER_LIBRARY - The osgViewer library OSGVIEWER_LIBRARY_DEBUG -
-The osgViewer debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgVolume.cmake b/Modules/FindosgVolume.cmake
index 35defef..58d9f7a 100644
--- a/Modules/FindosgVolume.cmake
+++ b/Modules/FindosgVolume.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgVolume This module defines
+Locate osgVolume This module defines:
 
-OSGVOLUME_FOUND - Was osgVolume found? OSGVOLUME_INCLUDE_DIR - Where
-to find the headers OSGVOLUME_LIBRARIES - The libraries to link for
-osgVolume (use this)
+``OSGVOLUME_FOUND``
+  Was osgVolume found?
+``OSGVOLUME_INCLUDE_DIR``
+  Where to find the headers
+``OSGVOLUME_LIBRARIES``
+  The libraries to link for osgVolume (use this)
+``OSGVOLUME_LIBRARY``
+  The osgVolume library
+``OSGVOLUME_LIBRARY_DEBUG``
+  The osgVolume debug library
 
-OSGVOLUME_LIBRARY - The osgVolume library OSGVOLUME_LIBRARY_DEBUG -
-The osgVolume debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 Created by Eric Wing.
 #]=======================================================================]
diff --git a/Modules/FindosgWidget.cmake b/Modules/FindosgWidget.cmake
index c7aae44..4049fad 100644
--- a/Modules/FindosgWidget.cmake
+++ b/Modules/FindosgWidget.cmake
@@ -7,28 +7,35 @@
 
 
 
-This is part of the Findosg* suite used to find OpenSceneGraph
+This is part of the ``Findosg*`` suite used to find OpenSceneGraph
 components.  Each component is separate and you must opt in to each
 module.  You must also opt into OpenGL and OpenThreads (and Producer
 if needed) as these modules won't do it for you.  This is to allow you
 control over your own system piece by piece in case you need to opt
 out of certain components or change the Find behavior for a particular
-module (perhaps because the default FindOpenGL.cmake module doesn't
+module (perhaps because the default :module:`FindOpenGL` module doesn't
 work with your system as an example).  If you want to use a more
 convenient module that includes everything, use the
-FindOpenSceneGraph.cmake instead of the Findosg*.cmake modules.
+:module:`FindOpenSceneGraph` instead of the ``Findosg*.cmake`` modules.
 
-Locate osgWidget This module defines
+Locate osgWidget This module defines:
 
-OSGWIDGET_FOUND - Was osgWidget found? OSGWIDGET_INCLUDE_DIR - Where
-to find the headers OSGWIDGET_LIBRARIES - The libraries to link for
-osgWidget (use this)
+``OSGWIDGET_FOUND``
+  Was osgWidget found?
+``OSGWIDGET_INCLUDE_DIR``
+  Where to find the headers
+``OSGWIDGET_LIBRARIES``
+  The libraries to link for osgWidget (use this)
+``OSGWIDGET_LIBRARY``
+  The osgWidget library
+``OSGWIDGET_LIBRARY_DEBUG``
+  The osgWidget debug library
 
-OSGWIDGET_LIBRARY - The osgWidget library OSGWIDGET_LIBRARY_DEBUG -
-The osgWidget debug library
+``$OSGDIR`` is an environment variable that would correspond to::
 
-$OSGDIR is an environment variable that would correspond to the
-./configure --prefix=$OSGDIR used in building osg.
+  ./configure --prefix=$OSGDIR
+
+used in building osg.
 
 FindosgWidget.cmake tweaked from Findosg* suite as created by Eric
 Wing.
diff --git a/Modules/Findosg_functions.cmake b/Modules/Findosg_functions.cmake
index 563b6bd..5226102 100644
--- a/Modules/Findosg_functions.cmake
+++ b/Modules/Findosg_functions.cmake
@@ -10,7 +10,7 @@
 
 
 This CMake file contains two macros to assist with searching for OSG
-libraries and nodekits.  Please see FindOpenSceneGraph.cmake for full
+libraries and nodekits.  Please see :module:`FindOpenSceneGraph` for full
 documentation.
 #]=======================================================================]
 
diff --git a/Modules/FindwxWidgets.cmake b/Modules/FindwxWidgets.cmake
index cc76b35..78fa481 100644
--- a/Modules/FindwxWidgets.cmake
+++ b/Modules/FindwxWidgets.cmake
@@ -848,7 +848,8 @@
       DBG_MSG_V("wxWidgets required components : ${_cmp_req}")
       DBG_MSG_V("wxWidgets optional components : ${_cmp_opt}")
       if(DEFINED _cmp_opt)
-        string(REPLACE ";" "," _cmp_opt "--optional-libs ${_cmp_opt}")
+        string(REPLACE ";" "," _cmp_opt "${_cmp_opt}")
+        set(_cmp_opt "--optional-libs" ${_cmp_opt})
       endif()
       string(REPLACE ";" "," _cmp_req "${_cmp_req}")
       execute_process(
diff --git a/Modules/FindwxWindows.cmake b/Modules/FindwxWindows.cmake
index 6e4be91..d1b25e1 100644
--- a/Modules/FindwxWindows.cmake
+++ b/Modules/FindwxWindows.cmake
@@ -635,14 +635,14 @@
       # remember: always link shared to use systems GL etc. libs (no static
       # linking, just link *against* static .a libs)
       if(WXWINDOWS_USE_SHARED_LIBS)
-        set(WX_CONFIG_ARGS_LIBS "--libs")
+        set(WX_CONFIG_ARGS_LIBS --libs)
       else()
-        set(WX_CONFIG_ARGS_LIBS "--static --libs")
+        set(WX_CONFIG_ARGS_LIBS --static --libs)
       endif()
 
       # do we need additionial wx GL stuff like GLCanvas ?
       if(WXWINDOWS_USE_GL)
-        string(APPEND WX_CONFIG_ARGS_LIBS " --gl-libs" )
+        list(APPEND WX_CONFIG_ARGS_LIBS --gl-libs)
       endif()
       ##message("DBG: WX_CONFIG_ARGS_LIBS=${WX_CONFIG_ARGS_LIBS}===")
 
@@ -662,14 +662,15 @@
       ##CMAKE_WXWINDOWS_CXX_FLAGS=${CMAKE_WXWINDOWS_CXX_FLAGS}===")
 
       # keep the back-quoted string for clarity
-      set(WXWINDOWS_LIBRARIES "`${CMAKE_WXWINDOWS_WXCONFIG_EXECUTABLE} ${WX_CONFIG_ARGS_LIBS}`")
+      string(REPLACE ";" " " _wx_config_args_libs "${WX_CONFIG_ARGS_LIBS}")
+      set(WXWINDOWS_LIBRARIES "`${CMAKE_WXWINDOWS_WXCONFIG_EXECUTABLE} ${_wx_config_args_libs}`")
       ##message("DBG2: for linking:
       ##WXWINDOWS_LIBRARIES=${WXWINDOWS_LIBRARIES}===")
 
       # evaluate wx-config output to separate linker flags and linkdirs for
       # rpath:
-      exec_program(${CMAKE_WXWINDOWS_WXCONFIG_EXECUTABLE}
-        ARGS ${WX_CONFIG_ARGS_LIBS}
+      execute_process(COMMAND ${CMAKE_WXWINDOWS_WXCONFIG_EXECUTABLE}
+        ${WX_CONFIG_ARGS_LIBS}
         OUTPUT_VARIABLE WX_CONFIG_LIBS )
 
       ## extract linkdirs (-L) for rpath
diff --git a/Modules/Internal/CMakeCUDAArchitecturesAll.cmake b/Modules/Internal/CMakeCUDAArchitecturesAll.cmake
new file mode 100644
index 0000000..873400a
--- /dev/null
+++ b/Modules/Internal/CMakeCUDAArchitecturesAll.cmake
@@ -0,0 +1,88 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# See supported GPUs on Wikipedia
+# https://en.wikipedia.org/wiki/CUDA#GPUs_supported
+
+function(cmake_cuda_architectures_all lang lang_var_)
+  # Initial set based on CUDA 7.0.
+  set(CMAKE_CUDA_ARCHITECTURES_ALL 20 21 30 35 37 50 52 53)
+  set(CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20 30 35 50)
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 8.0)
+    list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 60 61 62)
+    list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 60)
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 9.0)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA"
+        OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)
+        )
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 70 72)
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 70)
+    endif()
+
+    list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 20 21)
+    list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 20)
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 10.0)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA"
+        OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)
+        )
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 75)
+    endif()
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.0)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA"
+        OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 11.0)
+        )
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 80)
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 80)
+    endif()
+
+    list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 30)
+    list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 30)
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.1)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA"
+        OR (CMAKE_${lang}_COMPILER_ID STREQUAL "Clang" AND CMAKE_${lang}_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0)
+        )
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 86)
+    endif()
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.4)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA")
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 87)
+    endif()
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 11.8)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA")
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL 89 90)
+      list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 90)
+    endif()
+  endif()
+
+  if(${lang_var_}TOOLKIT_VERSION VERSION_GREATER_EQUAL 12.0)
+    if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA")
+      list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL 35 37)
+      list(REMOVE_ITEM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR 35)
+    endif()
+  endif()
+
+  # only generate jit code for the newest arch for all/all-major
+  list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL _latest_arch)
+  list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL APPEND "-real")
+  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL ${_latest_arch})
+
+  list(POP_BACK CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR _latest_arch)
+  list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR APPEND "-real")
+  list(APPEND CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR ${_latest_arch})
+
+  set(CMAKE_${lang}_ARCHITECTURES_ALL "${CMAKE_CUDA_ARCHITECTURES_ALL}" PARENT_SCOPE)
+  set(CMAKE_${lang}_ARCHITECTURES_ALL_MAJOR "${CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR}" PARENT_SCOPE)
+endfunction()
diff --git a/Modules/Internal/CMakeCUDAArchitecturesNative.cmake b/Modules/Internal/CMakeCUDAArchitecturesNative.cmake
new file mode 100644
index 0000000..4185eda
--- /dev/null
+++ b/Modules/Internal/CMakeCUDAArchitecturesNative.cmake
@@ -0,0 +1,49 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+function(cmake_cuda_architectures_native lang)
+  # Run the test binary to detect the native architectures.
+  execute_process(COMMAND "${CMAKE_PLATFORM_INFO_DIR}/CMakeDetermineCompilerABI_${lang}.bin"
+    RESULT_VARIABLE archs_result
+    OUTPUT_VARIABLE archs_output
+    ERROR_VARIABLE  archs_output
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+  if(archs_result EQUAL 0)
+    if("$ENV{CMAKE_CUDA_ARCHITECTURES_NATIVE_CLAMP}")
+      # Undocumented hook used by CMake's CI.
+      # Clamp native architecture to version range supported by this CUDA.
+      list(GET CMAKE_${lang}_ARCHITECTURES_ALL 0  arch_min)
+      list(GET CMAKE_${lang}_ARCHITECTURES_ALL -1 arch_max)
+      set(CMAKE_CUDA_ARCHITECTURES_NATIVE "")
+      foreach(arch IN LISTS archs_output)
+        if(arch LESS arch_min)
+          set(arch "${arch_min}")
+        endif()
+        if(arch GREATER arch_max)
+          set(arch "${arch_max}")
+        endif()
+        list(APPEND CMAKE_CUDA_ARCHITECTURES_NATIVE ${arch})
+      endforeach()
+      unset(arch)
+      unset(arch_min)
+      unset(arch_max)
+    else()
+      set(CMAKE_CUDA_ARCHITECTURES_NATIVE "${archs_output}")
+    endif()
+    list(REMOVE_DUPLICATES CMAKE_CUDA_ARCHITECTURES_NATIVE)
+    list(TRANSFORM CMAKE_CUDA_ARCHITECTURES_NATIVE APPEND "-real")
+  else()
+    if(NOT archs_result MATCHES "[0-9]+")
+      set(archs_status " (${archs_result})")
+    else()
+      set(archs_status "")
+    endif()
+    string(REPLACE "\n" "\n  " archs_output "  ${archs_output}")
+    message(CONFIGURE_LOG
+      "Detecting the CUDA native architecture(s) failed with "
+      "the following output${archs_status}:\n${archs_output}\n\n")
+  endif()
+
+  set(CMAKE_${lang}_ARCHITECTURES_NATIVE "${CMAKE_CUDA_ARCHITECTURES_NATIVE}" PARENT_SCOPE)
+endfunction()
diff --git a/Modules/Internal/CMakeCUDAArchitecturesValidate.cmake b/Modules/Internal/CMakeCUDAArchitecturesValidate.cmake
new file mode 100644
index 0000000..b6997d2
--- /dev/null
+++ b/Modules/Internal/CMakeCUDAArchitecturesValidate.cmake
@@ -0,0 +1,19 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+function(cmake_cuda_architectures_validate lang)
+  if(DEFINED CMAKE_${lang}_ARCHITECTURES)
+    if(CMAKE_${lang}_ARCHITECTURES STREQUAL "")
+      message(FATAL_ERROR "CMAKE_${lang}_ARCHITECTURES must be non-empty if set.")
+    elseif(CMAKE_${lang}_ARCHITECTURES AND NOT CMAKE_${lang}_ARCHITECTURES MATCHES "^([0-9]+a?(-real|-virtual)?(;[0-9]+a?(-real|-virtual)?|;)*|all|all-major|native)$")
+      message(FATAL_ERROR
+        "CMAKE_${lang}_ARCHITECTURES:\n"
+        "  ${CMAKE_${lang}_ARCHITECTURES}\n"
+        "is not one of the following:\n"
+        "  * a semicolon-separated list of integers, each optionally\n"
+        "    followed by '-real' or '-virtual'\n"
+        "  * a special value: all, all-major, native\n"
+        )
+    endif()
+  endif()
+endfunction()
diff --git a/Modules/Internal/CMakeCUDAFilterImplicitLibs.cmake b/Modules/Internal/CMakeCUDAFilterImplicitLibs.cmake
new file mode 100644
index 0000000..60287af
--- /dev/null
+++ b/Modules/Internal/CMakeCUDAFilterImplicitLibs.cmake
@@ -0,0 +1,20 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# In CMakeDetermineCUDACompiler and CMakeTestCUDACompiler we detect
+# libraries that the CUDA compiler implicitly passes to the host linker.
+# CMake invokes the host linker directly and so needs to pass these libraries.
+# Filter out implicit link libraries that should not be passed unconditionally.
+macro(cmake_cuda_filter_implicit_libs _var_CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES)
+  list(REMOVE_ITEM "${_var_CMAKE_CUDA_IMPLICIT_LINK_LIBRARIES}"
+    # The CUDA runtime libraries are controlled by CMAKE_CUDA_RUNTIME_LIBRARY.
+    cudart        cudart.lib
+    cudart_static cudart_static.lib
+    cudadevrt     cudadevrt.lib
+
+    # Dependencies of the CUDA static runtime library on Linux hosts.
+    rt
+    pthread
+    dl
+    )
+endmacro()
diff --git a/Modules/Internal/CMakeCUDAFindToolkit.cmake b/Modules/Internal/CMakeCUDAFindToolkit.cmake
new file mode 100644
index 0000000..58d1e55
--- /dev/null
+++ b/Modules/Internal/CMakeCUDAFindToolkit.cmake
@@ -0,0 +1,173 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+macro(cmake_cuda_find_toolkit lang lang_var_)
+  # This is very similar to FindCUDAToolkit, but somewhat simplified since we can issue fatal errors
+  # if we fail and we don't need to account for searching the libraries.
+
+  # For NVCC we can easily deduce the SDK binary directory from the compiler path.
+  if(CMAKE_${lang}_COMPILER_ID STREQUAL "NVIDIA")
+    set(_CUDA_NVCC_EXECUTABLE "${CMAKE_${lang}_COMPILER}")
+  else()
+    # Search using CUDAToolkit_ROOT and then CUDA_PATH for equivalence with FindCUDAToolkit.
+    # In FindCUDAToolkit CUDAToolkit_ROOT is searched automatically due to being in a find_package().
+    # First we search candidate non-default paths to give them priority.
+    find_program(_CUDA_NVCC_EXECUTABLE
+      NAMES nvcc nvcc.exe
+      PATHS ${CUDAToolkit_ROOT}
+      ENV CUDAToolkit_ROOT
+      ENV CUDA_PATH
+      PATH_SUFFIXES bin
+      NO_DEFAULT_PATH
+      NO_CACHE
+    )
+
+    # If we didn't find NVCC, then try the default paths.
+    find_program(_CUDA_NVCC_EXECUTABLE
+      NAMES nvcc nvcc.exe
+      PATH_SUFFIXES bin
+      NO_CACHE
+    )
+
+    # If the user specified CUDAToolkit_ROOT but nvcc could not be found, this is an error.
+    if(NOT _CUDA_NVCC_EXECUTABLE AND (DEFINED CUDAToolkit_ROOT OR DEFINED ENV{CUDAToolkit_ROOT}))
+      set(fail_base "Could not find nvcc executable in path specified by")
+
+      if(DEFINED CUDAToolkit_ROOT)
+        message(FATAL_ERROR "${fail_base} CUDAToolkit_ROOT=${CUDAToolkit_ROOT}")
+      elseif(DEFINED ENV{CUDAToolkit_ROOT})
+        message(FATAL_ERROR "${fail_base} environment variable CUDAToolkit_ROOT=$ENV{CUDAToolkit_ROOT}")
+      endif()
+    endif()
+
+    # CUDAToolkit_ROOT cmake/env variable not specified, try platform defaults.
+    #
+    # - Linux: /usr/local/cuda-X.Y
+    # - macOS: /Developer/NVIDIA/CUDA-X.Y
+    # - Windows: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\vX.Y
+    #
+    # We will also search the default symlink location /usr/local/cuda first since
+    # if CUDAToolkit_ROOT is not specified, it is assumed that the symlinked
+    # directory is the desired location.
+    if(NOT _CUDA_NVCC_EXECUTABLE)
+      if(UNIX)
+        if(NOT APPLE)
+          set(platform_base "/usr/local/cuda-")
+        else()
+          set(platform_base "/Developer/NVIDIA/CUDA-")
+        endif()
+      else()
+        set(platform_base "C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v")
+      endif()
+
+      # Build out a descending list of possible cuda installations, e.g.
+      file(GLOB possible_paths "${platform_base}*")
+      # Iterate the glob results and create a descending list.
+      set(versions)
+      foreach(p ${possible_paths})
+        # Extract version number from end of string
+        string(REGEX MATCH "[0-9][0-9]?\\.[0-9]$" p_version ${p})
+        if(IS_DIRECTORY ${p} AND p_version)
+          list(APPEND versions ${p_version})
+        endif()
+      endforeach()
+
+      # Sort numerically in descending order, so we try the newest versions first.
+      list(SORT versions COMPARE NATURAL ORDER DESCENDING)
+
+      # With a descending list of versions, populate possible paths to search.
+      set(search_paths)
+      foreach(v ${versions})
+        list(APPEND search_paths "${platform_base}${v}")
+      endforeach()
+
+      # Force the global default /usr/local/cuda to the front on Unix.
+      if(UNIX)
+        list(INSERT search_paths 0 "/usr/local/cuda")
+      endif()
+
+      # Now search for nvcc again using the platform default search paths.
+      find_program(_CUDA_NVCC_EXECUTABLE
+        NAMES nvcc nvcc.exe
+        PATHS ${search_paths}
+        PATH_SUFFIXES bin
+        NO_CACHE
+      )
+
+      # We are done with these variables now, cleanup.
+      unset(platform_base)
+      unset(possible_paths)
+      unset(versions)
+      unset(search_paths)
+
+      if(NOT _CUDA_NVCC_EXECUTABLE)
+        message(FATAL_ERROR "Failed to find nvcc.\nCompiler ${CMAKE_${lang}_COMPILER_ID} requires the CUDA toolkit. Please set the CUDAToolkit_ROOT variable.")
+      endif()
+    endif()
+  endif()
+
+  # Given that NVCC can be provided by multiple different sources (NVIDIA HPC SDK, CUDA Toolkit, distro)
+  # each of which has a different layout, we need to extract the CUDA toolkit root from the compiler
+  # itself, allowing us to support numerous different scattered toolkit layouts
+  execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "-v" "__cmake_determine_cuda"
+    OUTPUT_VARIABLE _CUDA_NVCC_OUT ERROR_VARIABLE _CUDA_NVCC_OUT)
+  if(_CUDA_NVCC_OUT MATCHES "\\#\\$ TOP=([^\r\n]*)")
+    get_filename_component(${lang_var_}TOOLKIT_ROOT "${CMAKE_MATCH_1}" ABSOLUTE)
+  else()
+    get_filename_component(${lang_var_}TOOLKIT_ROOT "${_CUDA_NVCC_EXECUTABLE}" DIRECTORY)
+    get_filename_component(${lang_var_}TOOLKIT_ROOT "${${lang_var_}TOOLKIT_ROOT}" DIRECTORY)
+  endif()
+
+  if(_CUDA_NVCC_OUT MATCHES "\\#\\$ NVVMIR_LIBRARY_DIR=([^\r\n]*)")
+    get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${CMAKE_MATCH_1}" ABSOLUTE)
+
+    #We require the path to end in `/nvvm/libdevice'
+    if(_CUDA_NVVMIR_LIBRARY_DIR MATCHES "nvvm/libdevice$")
+      get_filename_component(_CUDA_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}/../.." ABSOLUTE)
+      set(_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR "${_CUDA_NVVMIR_LIBRARY_DIR}")
+    endif()
+
+    unset(_CUDA_NVVMIR_LIBRARY_DIR)
+    unset(_cuda_nvvmir_dir_name)
+  endif()
+  unset(_CUDA_NVCC_OUT)
+
+  # In a non-scattered installation the following are equivalent to ${lang_var_}TOOLKIT_ROOT.
+  # We first check for a non-scattered installation to prefer it over a scattered installation.
+
+  # ${lang_var_}LIBRARY_ROOT contains the device library.
+  if(DEFINED _CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR)
+    set(${lang_var_}LIBRARY_ROOT "${_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR}")
+  elseif(EXISTS "${${lang_var_}TOOLKIT_ROOT}/nvvm/libdevice")
+    set(${lang_var_}LIBRARY_ROOT "${${lang_var_}TOOLKIT_ROOT}")
+  elseif(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/cuda/nvvm/libdevice")
+    set(${lang_var_}LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/cuda")
+  elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/cuda/nvvm/libdevice")
+    set(${lang_var_}LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/cuda")
+  else()
+    message(FATAL_ERROR "Couldn't find CUDA library root.")
+  endif()
+  unset(_CUDA_COMPILER_LIBRARY_ROOT_FROM_NVVMIR_LIBRARY_DIR)
+
+  # ${lang_var_}TOOLKIT_LIBRARY_ROOT contains the linking stubs necessary for device linking and other low-level library files.
+  if(CMAKE_SYSROOT_LINK AND EXISTS "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub")
+    set(${lang_var_}TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT_LINK}/usr/lib/nvidia-cuda-toolkit")
+  elseif(EXISTS "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit/bin/crt/link.stub")
+    set(${lang_var_}TOOLKIT_LIBRARY_ROOT "${CMAKE_SYSROOT}/usr/lib/nvidia-cuda-toolkit")
+  else()
+    set(${lang_var_}TOOLKIT_LIBRARY_ROOT "${${lang_var_}TOOLKIT_ROOT}")
+  endif()
+
+  # For regular nvcc we the toolkit version is the same as the compiler version and we can parse it from the vendor test output.
+  # For Clang we need to invoke nvcc to get version output.
+  if(CMAKE_${lang}_COMPILER_ID STREQUAL "Clang")
+    execute_process(COMMAND ${_CUDA_NVCC_EXECUTABLE} "--version" OUTPUT_VARIABLE CMAKE_${lang}_COMPILER_ID_OUTPUT)
+  endif()
+
+  if(CMAKE_${lang}_COMPILER_ID_OUTPUT MATCHES [=[V([0-9]+\.[0-9]+\.[0-9]+)]=])
+    set(${lang_var_}TOOLKIT_VERSION "${CMAKE_MATCH_1}")
+  endif()
+
+  # Don't leak variables unnecessarily to user code.
+  unset(_CUDA_NVCC_EXECUTABLE)
+endmacro()
diff --git a/Modules/Internal/CMakeNVCCFilterImplicitInfo.cmake b/Modules/Internal/CMakeNVCCFilterImplicitInfo.cmake
new file mode 100644
index 0000000..dee7580
--- /dev/null
+++ b/Modules/Internal/CMakeNVCCFilterImplicitInfo.cmake
@@ -0,0 +1,16 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+macro(cmake_nvcc_filter_implicit_info lang lang_var_)
+  # Remove the CUDA Toolkit include directories from the set of
+  # implicit system include directories.
+  # This resolves the issue that NVCC doesn't specify these
+  # includes as SYSTEM includes when compiling device code, and sometimes
+  # they contain headers that generate warnings, so let users mark them
+  # as SYSTEM explicitly
+  if(${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES)
+    list(REMOVE_ITEM CMAKE_${lang}_IMPLICIT_INCLUDE_DIRECTORIES
+      ${${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES}
+      )
+  endif()
+endmacro()
diff --git a/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake b/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake
new file mode 100644
index 0000000..32ff28a
--- /dev/null
+++ b/Modules/Internal/CMakeNVCCParseImplicitInfo.cmake
@@ -0,0 +1,149 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+macro(cmake_nvcc_parse_implicit_info lang lang_var_)
+  set(_nvcc_log "")
+  string(REPLACE "\r" "" _nvcc_output_orig "${CMAKE_${lang}_COMPILER_PRODUCED_OUTPUT}")
+  if(_nvcc_output_orig MATCHES "#\\\$ +PATH= *([^\n]*)\n")
+    set(_nvcc_path "${CMAKE_MATCH_1}")
+    string(APPEND _nvcc_log "  found 'PATH=' string: [${_nvcc_path}]\n")
+    string(REPLACE ":" ";" _nvcc_path "${_nvcc_path}")
+  else()
+    set(_nvcc_path "")
+    string(REPLACE "\n" "\n    " _nvcc_output_log "\n${_nvcc_output_orig}")
+    string(APPEND _nvcc_log "  no 'PATH=' string found in nvcc output:${_nvcc_output_log}\n")
+  endif()
+  if(_nvcc_output_orig MATCHES "#\\\$ +LIBRARIES= *([^\n]*)\n")
+    set(_nvcc_libraries "${CMAKE_MATCH_1}")
+    string(APPEND _nvcc_log "  found 'LIBRARIES=' string: [${_nvcc_libraries}]\n")
+  else()
+    set(_nvcc_libraries "")
+    string(REPLACE "\n" "\n    " _nvcc_output_log "\n${_nvcc_output_orig}")
+    string(APPEND _nvcc_log "  no 'LIBRARIES=' string found in nvcc output:${_nvcc_output_log}\n")
+  endif()
+  if(_nvcc_output_orig MATCHES "#\\\$ +INCLUDES= *([^\n]*)\n")
+    set(_nvcc_includes "${CMAKE_MATCH_1}")
+    string(APPEND _nvcc_log "  found 'INCLUDES=' string: [${_nvcc_includes}]\n")
+  else()
+    set(_nvcc_includes "")
+    string(REPLACE "\n" "\n    " _nvcc_output_log "\n${_nvcc_output_orig}")
+    string(APPEND _nvcc_log "  no 'INCLUDES=' string found in nvcc output:${_nvcc_output_log}\n")
+  endif()
+  string(REGEX MATCHALL "-arch compute_([0-9]+)" _nvcc_target_cpus "${_nvcc_output_orig}")
+  foreach(_nvcc_target_cpu ${_nvcc_target_cpus})
+    if(_nvcc_target_cpu MATCHES "-arch compute_([0-9]+)")
+      list(APPEND CMAKE_${lang}_ARCHITECTURES_DEFAULT "${CMAKE_MATCH_1}")
+    endif()
+  endforeach()
+
+  set(_nvcc_link_line "")
+  if(_nvcc_libraries)
+    # Remove variable assignments.
+    string(REGEX REPLACE "#\\\$ *[^= ]+=[^\n]*\n" "" _nvcc_output "${_nvcc_output_orig}")
+    # Encode [] characters that break list expansion.
+    string(REPLACE "[" "{==={" _nvcc_output "${_nvcc_output}")
+    string(REPLACE "]" "}===}" _nvcc_output "${_nvcc_output}")
+    # Split lines.
+    string(REGEX REPLACE "\n+(#\\\$ )?" ";" _nvcc_output "${_nvcc_output}")
+    foreach(line IN LISTS _nvcc_output)
+      set(_nvcc_output_line "${line}")
+      string(REPLACE "{==={" "[" _nvcc_output_line "${_nvcc_output_line}")
+      string(REPLACE "}===}" "]" _nvcc_output_line "${_nvcc_output_line}")
+      string(APPEND _nvcc_log "  considering line: [${_nvcc_output_line}]\n")
+      if("${_nvcc_output_line}" MATCHES "^ *nvlink")
+        string(APPEND _nvcc_log "    ignoring nvlink line\n")
+      elseif(_nvcc_libraries)
+        if("${_nvcc_output_line}" MATCHES "(@\"?((tmp/)?a\\.exe\\.res)\"?)")
+          set(_nvcc_link_res_arg "${CMAKE_MATCH_1}")
+          set(_nvcc_link_res_file "${CMAKE_MATCH_2}")
+          set(_nvcc_link_res "${CMAKE_PLATFORM_INFO_DIR}/CompilerId${lang}/${_nvcc_link_res_file}")
+          if(EXISTS "${_nvcc_link_res}")
+            file(READ "${_nvcc_link_res}" _nvcc_link_res_content)
+            string(REPLACE "${_nvcc_link_res_arg}" "${_nvcc_link_res_content}" _nvcc_output_line "${_nvcc_output_line}")
+          endif()
+        endif()
+        string(FIND "${_nvcc_output_line}" "${_nvcc_libraries}" _nvcc_libraries_pos)
+        if(NOT _nvcc_libraries_pos EQUAL -1)
+          set(_nvcc_link_line "${_nvcc_output_line}")
+          string(APPEND _nvcc_log "    extracted link line: [${_nvcc_link_line}]\n")
+        endif()
+      endif()
+    endforeach()
+  endif()
+
+  if(_nvcc_link_line)
+    if("x${CMAKE_${lang}_SIMULATE_ID}" STREQUAL "xMSVC")
+      set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${CMAKE_LINKER}")
+    else()
+      #extract the compiler that is being used for linking
+      separate_arguments(_nvcc_link_line_args UNIX_COMMAND "${_nvcc_link_line}")
+      list(GET _nvcc_link_line_args 0 _nvcc_host_link_launcher)
+      if(IS_ABSOLUTE "${_nvcc_host_link_launcher}")
+        string(APPEND _nvcc_log "  extracted link launcher absolute path: [${_nvcc_host_link_launcher}]\n")
+        set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}")
+      else()
+        string(APPEND _nvcc_log "  extracted link launcher name: [${_nvcc_host_link_launcher}]\n")
+        find_program(_nvcc_find_host_link_launcher
+          NAMES ${_nvcc_host_link_launcher}
+          PATHS ${_nvcc_path} NO_DEFAULT_PATH)
+        find_program(_nvcc_find_host_link_launcher
+          NAMES ${_nvcc_host_link_launcher})
+        if(_nvcc_find_host_link_launcher)
+          string(APPEND _nvcc_log "  found link launcher absolute path: [${_nvcc_find_host_link_launcher}]\n")
+          set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${_nvcc_find_host_link_launcher}")
+        else()
+          string(APPEND _nvcc_log "  could not find link launcher absolute path\n")
+          set(CMAKE_${lang}_HOST_LINK_LAUNCHER "${_nvcc_host_link_launcher}")
+        endif()
+        unset(_nvcc_find_host_link_launcher CACHE)
+      endif()
+    endif()
+
+    #prefix the line with cuda-fake-ld so that implicit link info believes it is
+    #a link line
+    set(_nvcc_link_line "cuda-fake-ld ${_nvcc_link_line}")
+    CMAKE_PARSE_IMPLICIT_LINK_INFO("${_nvcc_link_line}"
+                                   CMAKE_${lang}_HOST_IMPLICIT_LINK_LIBRARIES
+                                   CMAKE_${lang}_HOST_IMPLICIT_LINK_DIRECTORIES
+                                   CMAKE_${lang}_HOST_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES
+                                   log
+                                   "${CMAKE_${lang}_IMPLICIT_OBJECT_REGEX}"
+                                   LANGUAGE ${lang})
+
+    # Detect CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT from the compiler by looking at which
+    # cudart library exists in the implicit link libraries passed to the host linker.
+    # This is required when a project sets the cuda runtime library as part of the
+    # initial flags.
+    if(";${CMAKE_${lang}_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart_static(\.lib)?;]])
+      set(CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT "STATIC")
+    elseif(";${CMAKE_${lang}_HOST_IMPLICIT_LINK_LIBRARIES};" MATCHES [[;cudart(\.lib)?;]])
+      set(CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT "SHARED")
+    else()
+      set(CMAKE_${lang}_RUNTIME_LIBRARY_DEFAULT "NONE")
+    endif()
+
+    message(CONFIGURE_LOG
+      "Parsed ${lang} nvcc implicit link information:\n${_nvcc_log}\n${log}\n\n")
+  else()
+    message(CONFIGURE_LOG
+      "Failed to parse ${lang} nvcc implicit link information:\n${_nvcc_log}\n\n")
+    message(FATAL_ERROR "Failed to extract nvcc implicit link line.")
+  endif()
+
+  set(${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES)
+  if(_nvcc_includes)
+    # across all operating system each include directory is prefixed with -I
+    separate_arguments(_nvcc_output NATIVE_COMMAND "${_nvcc_includes}")
+    foreach(line IN LISTS _nvcc_output)
+      string(REGEX REPLACE "^-I" "" line "${line}")
+      get_filename_component(line "${line}" ABSOLUTE)
+      list(APPEND ${lang_var_}TOOLKIT_INCLUDE_DIRECTORIES "${line}")
+    endforeach()
+
+    message(CONFIGURE_LOG
+      "Parsed CUDA nvcc include information:\n${_nvcc_log}\n${log}\n\n")
+  else()
+    message(CONFIGURE_LOG
+      "Failed to detect CUDA nvcc include information:\n${_nvcc_log}\n\n")
+  endif()
+endmacro()
diff --git a/Modules/Internal/CPack/NSIS.template.in b/Modules/Internal/CPack/NSIS.template.in
index 21753af..6349f9d 100644
--- a/Modules/Internal/CPack/NSIS.template.in
+++ b/Modules/Internal/CPack/NSIS.template.in
@@ -981,7 +981,7 @@
     ;MessageBox MB_OK 'User "$0" is in the Admin group'
     StrCpy $SV_ALLUSERS "AllUsers"
     Goto done
-  StrCmp $1 "Power" 0 +4
+  StrCmp $1 "Power" 0 +3
     SetShellVarContext all
     ;MessageBox MB_OK 'User "$0" is in the Power Users group'
     StrCpy $SV_ALLUSERS "AllUsers"
diff --git a/Modules/Internal/FeatureTesting.cmake b/Modules/Internal/FeatureTesting.cmake
index 1a8a27e..c2bd14f 100644
--- a/Modules/Internal/FeatureTesting.cmake
+++ b/Modules/Internal/FeatureTesting.cmake
@@ -37,6 +37,7 @@
     LINK_LIBRARIES "${compile_flags_for_link}"
     COPY_FILE "${CMAKE_BINARY_DIR}/CMakeFiles/feature_tests.bin"
     COPY_FILE_ERROR _copy_error
+    __CMAKE_INTERNAL FEATURE_TESTING
     )
   if(NOT CMAKE_${lang}_FEATURE_TEST)
     set(_result 255)
diff --git a/Modules/Platform/Apple-Clang.cmake b/Modules/Platform/Apple-Clang.cmake
index 0681bfb..57b3910 100644
--- a/Modules/Platform/Apple-Clang.cmake
+++ b/Modules/Platform/Apple-Clang.cmake
@@ -14,6 +14,10 @@
   if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 3.2)
     set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ")
   endif()
+
+  set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
+  set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
+
   if(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneOS")
     set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-miphoneos-version-min=")
   elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/iPhoneSimulator")
@@ -22,6 +26,10 @@
     set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mtvos-version-min=")
   elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/AppleTVSimulator")
     set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mtvos-simulator-version-min=")
+    elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/XROS")
+    set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mtargetos=xros")
+  elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/XRSimulator")
+    set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mtargetos=xros")
   elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/WatchOS")
     set(CMAKE_${lang}_OSX_DEPLOYMENT_TARGET_FLAG "-mwatchos-version-min=")
   elseif(_CMAKE_OSX_SYSROOT_PATH MATCHES "/WatchSimulator")
diff --git a/Modules/Platform/Apple-GNU.cmake b/Modules/Platform/Apple-GNU.cmake
index 9572736..823c790 100644
--- a/Modules/Platform/Apple-GNU.cmake
+++ b/Modules/Platform/Apple-GNU.cmake
@@ -14,6 +14,9 @@
   if(NOT CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 4.3)
     set(CMAKE_${lang}_SYSTEM_FRAMEWORK_SEARCH_FLAG "-iframework ")
   endif()
+
+  set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK "-framework <LIBRARY>")
+  set(CMAKE_${lang}_LINK_LIBRARY_USING_FRAMEWORK_SUPPORTED TRUE)
 endmacro()
 
 macro(cmake_gnu_set_sysroot_flag lang)
diff --git a/Modules/Platform/Darwin-Initialize.cmake b/Modules/Platform/Darwin-Initialize.cmake
index e253392..8d5bf8c 100644
--- a/Modules/Platform/Darwin-Initialize.cmake
+++ b/Modules/Platform/Darwin-Initialize.cmake
@@ -43,7 +43,7 @@
   unset(_sysctl_stdout)
 endif()
 
-# macOS, iOS, tvOS, and watchOS should lookup compilers from
+# macOS, iOS, tvOS, visionOS, and watchOS should lookup compilers from
 # Platform/Apple-${CMAKE_CXX_COMPILER_ID}-<LANG>
 set(CMAKE_EFFECTIVE_SYSTEM_NAME "Apple")
 
@@ -76,6 +76,8 @@
   set(_CMAKE_OSX_SYSROOT_DEFAULT "iphoneos")
 elseif(CMAKE_SYSTEM_NAME STREQUAL tvOS)
   set(_CMAKE_OSX_SYSROOT_DEFAULT "appletvos")
+elseif(CMAKE_SYSTEM_NAME STREQUAL visionOS)
+  set(_CMAKE_OSX_SYSROOT_DEFAULT "xros")
 elseif(CMAKE_SYSTEM_NAME STREQUAL watchOS)
   set(_CMAKE_OSX_SYSROOT_DEFAULT "watchos")
 elseif("${CMAKE_GENERATOR}" MATCHES Xcode
diff --git a/Modules/Platform/Darwin.cmake b/Modules/Platform/Darwin.cmake
index 48a9065..d614182 100644
--- a/Modules/Platform/Darwin.cmake
+++ b/Modules/Platform/Darwin.cmake
@@ -1,4 +1,4 @@
-if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
   if(NOT DEFINED CMAKE_MACOSX_BUNDLE)
     set(CMAKE_MACOSX_BUNDLE ON)
   endif()
diff --git a/Modules/Platform/Windows-GNU.cmake b/Modules/Platform/Windows-GNU.cmake
index 088b238..412af6b 100644
--- a/Modules/Platform/Windows-GNU.cmake
+++ b/Modules/Platform/Windows-GNU.cmake
@@ -10,6 +10,25 @@
 
 set(MINGW 1)
 
+# On Windows hosts, in MSYSTEM environments, search standard prefixes.
+if(CMAKE_HOST_WIN32)
+  # Bootstrap CMake does not have cmake_host_system_information.
+  if(COMMAND cmake_host_system_information)
+    cmake_host_system_information(RESULT _MSYSTEM_PREFIX QUERY MSYSTEM_PREFIX)
+  elseif(IS_DIRECTORY "$ENV{MSYSTEM_PREFIX}")
+    set(_MSYSTEM_PREFIX "$ENV{MSYSTEM_PREFIX}")
+  endif()
+
+  # Search this MSYSTEM environment's equivalent to /usr/local and /usr.
+  if(_MSYSTEM_PREFIX)
+    list(PREPEND CMAKE_SYSTEM_PREFIX_PATH "${_MSYSTEM_PREFIX}")
+    if(IS_DIRECTORY "${_MSYSTEM_PREFIX}/local")
+      list(PREPEND CMAKE_SYSTEM_PREFIX_PATH "${_MSYSTEM_PREFIX}/local")
+    endif()
+  endif()
+  unset(_MSYSTEM_PREFIX)
+endif()
+
 set(CMAKE_IMPORT_LIBRARY_PREFIX "lib")
 set(CMAKE_SHARED_LIBRARY_PREFIX "lib")
 set(CMAKE_SHARED_MODULE_PREFIX  "lib")
diff --git a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
index 64dc0da..3e22a6e 100644
--- a/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
+++ b/Modules/Platform/Windows-LLVMFlang-Fortran.cmake
@@ -1,3 +1,58 @@
-include(Platform/Windows-GNU)
-__windows_compiler_gnu(Fortran)
-# TODO: MSVC ABI Support
+if("x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xGNU")
+  include(Platform/Windows-GNU)
+  __windows_compiler_gnu(Fortran)
+elseif("x${CMAKE_Fortran_SIMULATE_ID}" STREQUAL "xMSVC")
+  include(Platform/Windows-MSVC)
+  __windows_compiler_msvc(Fortran)
+
+  # FIXME(LLVMFlang): It does not provides MSVC runtime library selection flags.
+  # It should be given a flag like classic Flang's `-Xclang --dependent-lib=`, or a
+  # dedicated flag to select among multiple `Fortran*.lib` runtime library variants
+  # that each depend on a different MSVC runtime library.  For now, LLVMFlang's
+  # `Fortran*.lib` runtime libraries hard-code use of msvcrt (MultiThreadedDLL),
+  # so we link to it ourselves.
+  set(_LLVMFlang_LINK_RUNTIME "-defaultlib:msvcrt")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "")
+
+  # FIXME(LLVMFlang): It does not provide all debug information format flags or predefines.
+  # It should be given a flag to enable Embedded debug information like MSVC -Z7.
+  #set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded)        # not supported by LLVMFlang
+  #set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue) # not supported by LLVMFlang
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase "-g")
+
+  set(CMAKE_Fortran_COMPILE_OBJECT "<CMAKE_Fortran_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")
+
+  if(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT)
+    set(_g "")
+  else()
+    set(_g " -g")
+  endif()
+  string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT "${_g}")
+  string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT "")
+  string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT "${_g}")
+  string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT "")
+  unset(_g)
+
+  # We link with lld-link.exe instead of the compiler driver, so explicitly
+  # pass implicit link information previously detected from the compiler.
+  set(_LLVMFlang_LINK_DIRS "${CMAKE_Fortran_IMPLICIT_LINK_DIRECTORIES}")
+  list(TRANSFORM _LLVMFlang_LINK_DIRS PREPEND "-libpath:\"")
+  list(TRANSFORM _LLVMFlang_LINK_DIRS APPEND "\"")
+  string(JOIN " " _LLVMFlang_LINK_DIRS ${_LLVMFlang_LINK_DIRS})
+  string(JOIN " " _LLVMFlang_LINK_LIBS ${CMAKE_Fortran_IMPLICIT_LINK_LIBRARIES})
+  foreach(v IN ITEMS
+      CMAKE_Fortran_LINK_EXECUTABLE
+      CMAKE_Fortran_CREATE_SHARED_LIBRARY
+      CMAKE_Fortran_CREATE_SHARED_MODULE
+      )
+    string(APPEND "${v}" " ${_LLVMFlang_LINK_DIRS} ${_LLVMFlang_LINK_LIBS} ${_LLVMFlang_LINK_RUNTIME}")
+  endforeach()
+  unset(_LLVMFlang_LINK_DIRS)
+  unset(_LLVMFlang_LINK_LIBS)
+  unset(_LLVMFlang_LINK_RUNTIME)
+else()
+  message(FATAL_ERROR "LLVMFlang target ABI unrecognized: ${CMAKE_Fortran_SIMULATE_ID}")
+endif()
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index 8e96bf4..829ab9b 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -28,6 +28,8 @@
 
 if(CMAKE_SYSTEM_NAME STREQUAL "WindowsCE")
   set(_PLATFORM_LINK_FLAGS " /subsystem:windowsce")
+elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver")
+  set(_PLATFORM_LINK_FLAGS " -subsystem:native -kernel -MANIFEST:NO")
 else()
   set(_PLATFORM_LINK_FLAGS "")
 endif()
@@ -223,6 +225,18 @@
   else()
     set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib")
   endif()
+elseif(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver")
+  set(CMAKE_C_STANDARD_LIBRARIES_INIT "")
+  set(_FLAGS_C   " -kernel")
+  set(_FLAGS_CXX " -kernel")
+  foreach(t EXE SHARED MODULE)
+    string(APPEND CMAKE_${t}_LINKER_FLAGS_INIT " -NODEFAULTLIB")
+  endforeach()
+  if((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "x64") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "x64"))
+    set(_PLATFORM_DEFINES "${_PLATFORM_DEFINES} -D_AMD64_ -DAMD64")
+  elseif((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM64") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM64"))
+    set(_PLATFORM_DEFINES "${_PLATFORM_DEFINES} -D_ARM64_ -DARM64")
+  endif()
 else()
   set(_PLATFORM_DEFINES "/DWIN32")
   if((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM64EC") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM64EC"))
@@ -294,7 +308,7 @@
 # add /debug and /INCREMENTAL:YES to DEBUG and RELWITHDEBINFO also add pdbtype
 # on versions that support it
 set( MSVC_INCREMENTAL_YES_FLAG "")
-if(NOT WINDOWS_PHONE AND NOT WINDOWS_STORE)
+if(NOT WINDOWS_PHONE AND NOT WINDOWS_STORE AND NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver")
   if(NOT MSVC_INCREMENTAL_DEFAULT)
     set( MSVC_INCREMENTAL_YES_FLAG "/INCREMENTAL:YES")
   else()
@@ -353,8 +367,14 @@
     set(_CMAKE_VS_LINK_DLL "<CMAKE_COMMAND> -E vs_link_dll --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- ")
     set(_CMAKE_VS_LINK_EXE "<CMAKE_COMMAND> -E vs_link_exe --intdir=<OBJECT_DIR> --rc=<CMAKE_RC_COMPILER> --mt=<CMAKE_MT> --manifests <MANIFESTS> -- ")
   endif()
+  if(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver")
+    set(_DLL_DRIVER "-driver")
+  else()
+    set(_DLL_DRIVER "/dll")
+  endif()
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "${_CMAKE_VS_LINK_DLL}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> /dll /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
+    "${_CMAKE_VS_LINK_DLL}<CMAKE_LINKER> ${CMAKE_CL_NOLOGO} <OBJECTS> ${CMAKE_START_TEMP_FILE} /out:<TARGET> /implib:<TARGET_IMPLIB> /pdb:<TARGET_PDB> ${_DLL_DRIVER} /version:<TARGET_VERSION_MAJOR>.<TARGET_VERSION_MINOR>${_PLATFORM_LINK_FLAGS} <LINK_FLAGS> <LINK_LIBRARIES> ${CMAKE_END_TEMP_FILE}")
+  unset(_DLL_DRIVER)
 
   set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY})
   set(CMAKE_${lang}_CREATE_STATIC_LIBRARY  "<CMAKE_AR> ${CMAKE_CL_NOLOGO} <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
diff --git a/Modules/Platform/Windows-OrangeC-C.cmake b/Modules/Platform/Windows-OrangeC-C.cmake
new file mode 100644
index 0000000..6b7e6b5
--- /dev/null
+++ b/Modules/Platform/Windows-OrangeC-C.cmake
@@ -0,0 +1,2 @@
+include(Platform/Windows-OrangeC)
+__windows_compiler_orangec(C)
diff --git a/Modules/Platform/Windows-OrangeC-CXX.cmake b/Modules/Platform/Windows-OrangeC-CXX.cmake
new file mode 100644
index 0000000..7de6716
--- /dev/null
+++ b/Modules/Platform/Windows-OrangeC-CXX.cmake
@@ -0,0 +1,2 @@
+include(Platform/Windows-OrangeC)
+__windows_compiler_orangec(CXX)
diff --git a/Modules/Platform/Windows-OrangeC.cmake b/Modules/Platform/Windows-OrangeC.cmake
new file mode 100644
index 0000000..4f66e0e
--- /dev/null
+++ b/Modules/Platform/Windows-OrangeC.cmake
@@ -0,0 +1,10 @@
+set(CMAKE_LINK_LIBRARY_SUFFIX "")
+set(CMAKE_STATIC_LIBRARY_SUFFIX ".l")
+set(CMAKE_IMPORT_LIBRARY_SUFFIX ".l")
+set(CMAKE_FIND_LIBRARY_PREFIXES "")
+set(CMAKE_FIND_LIBRARY_SUFFIXES ".l")
+
+macro(__windows_compiler_orangec lang)
+  set(CMAKE_${lang}_CREATE_WIN32_EXE "-Wg")
+  set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Wc")
+endmacro()
diff --git a/Modules/Platform/Windows.cmake b/Modules/Platform/Windows.cmake
index 8697e7a..1bf39cf 100644
--- a/Modules/Platform/Windows.cmake
+++ b/Modules/Platform/Windows.cmake
@@ -1,7 +1,11 @@
 set(CMAKE_STATIC_LIBRARY_PREFIX "")
 set(CMAKE_STATIC_LIBRARY_SUFFIX ".lib")
 set(CMAKE_SHARED_LIBRARY_PREFIX "")          # lib
-set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll")          # .so
+if(CMAKE_SYSTEM_NAME STREQUAL "WindowsKernelModeDriver")
+  set(CMAKE_SHARED_LIBRARY_SUFFIX ".sys")          # .so
+else()
+  set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll")          # .so
+endif()
 set(CMAKE_IMPORT_LIBRARY_PREFIX "")
 set(CMAKE_IMPORT_LIBRARY_SUFFIX ".lib")
 set(CMAKE_EXECUTABLE_SUFFIX ".exe")          # .exe
diff --git a/Modules/Platform/WindowsKernelModeDriver-Initialize.cmake b/Modules/Platform/WindowsKernelModeDriver-Initialize.cmake
new file mode 100644
index 0000000..6f0ef33
--- /dev/null
+++ b/Modules/Platform/WindowsKernelModeDriver-Initialize.cmake
@@ -0,0 +1,11 @@
+# undocumented, do not use outside of CMake
+cmake_language(GET_EXPERIMENTAL_FEATURE_ENABLED "WindowsKernelModeDriver" _cmake_windows_kernel_mode_driver_enabled)
+if(NOT _cmake_windows_kernel_mode_driver_enabled)
+  message(FATAL_ERROR "Windows kernel-mode driver experimental support is not enabled.")
+endif()
+
+if(CMAKE_GENERATOR MATCHES "Visual Studio")
+  message(FATAL_ERROR "Visual Studio generators do not yet support CMAKE_SYSTEM_NAME=WindowsKernelModeDriver.")
+endif()
+
+set(_CMAKE_FEATURE_DETECTION_TARGET_TYPE STATIC_LIBRARY)
diff --git a/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake b/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake
new file mode 100644
index 0000000..ce8060b
--- /dev/null
+++ b/Modules/Platform/WindowsKernelModeDriver-MSVC-C.cmake
@@ -0,0 +1 @@
+include(Platform/Windows-MSVC-C)
diff --git a/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake b/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake
new file mode 100644
index 0000000..281eadc
--- /dev/null
+++ b/Modules/Platform/WindowsKernelModeDriver-MSVC-CXX.cmake
@@ -0,0 +1 @@
+include(Platform/Windows-MSVC-CXX)
diff --git a/Modules/Platform/WindowsKernelModeDriver.cmake b/Modules/Platform/WindowsKernelModeDriver.cmake
new file mode 100644
index 0000000..65b2eae
--- /dev/null
+++ b/Modules/Platform/WindowsKernelModeDriver.cmake
@@ -0,0 +1 @@
+include(Platform/Windows)
diff --git a/Modules/Platform/visionOS-Determine-CXX.cmake b/Modules/Platform/visionOS-Determine-CXX.cmake
new file mode 100644
index 0000000..ac80fa6
--- /dev/null
+++ b/Modules/Platform/visionOS-Determine-CXX.cmake
@@ -0,0 +1 @@
+include(Platform/Darwin-Determine-CXX)
diff --git a/Modules/Platform/visionOS-Initialize.cmake b/Modules/Platform/visionOS-Initialize.cmake
new file mode 100644
index 0000000..e8431bc
--- /dev/null
+++ b/Modules/Platform/visionOS-Initialize.cmake
@@ -0,0 +1,7 @@
+include(Platform/Darwin-Initialize)
+
+if(NOT _CMAKE_OSX_SYSROOT_PATH MATCHES "/XR(OS|Simulator)")
+  message(FATAL_ERROR "${CMAKE_OSX_SYSROOT} is not an visionOS SDK")
+endif()
+
+set(_CMAKE_FEATURE_DETECTION_TARGET_TYPE STATIC_LIBRARY)
diff --git a/Modules/Platform/visionOS.cmake b/Modules/Platform/visionOS.cmake
new file mode 100644
index 0000000..850ddc2
--- /dev/null
+++ b/Modules/Platform/visionOS.cmake
@@ -0,0 +1 @@
+include(Platform/Darwin)
diff --git a/Modules/UsePkgConfig.cmake b/Modules/UsePkgConfig.cmake
index 32d228d..b020259 100644
--- a/Modules/UsePkgConfig.cmake
+++ b/Modules/UsePkgConfig.cmake
@@ -35,25 +35,25 @@
   # if pkg-config has been found
   if(PKGCONFIG_EXECUTABLE)
 
-    exec_program(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --exists RETURN_VALUE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull )
+    execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --exists RESULT_VARIABLE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull )
 
     # and if the package of interest also exists for pkg-config, then get the information
     if(NOT _return_VALUE)
 
-      exec_program(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --variable=includedir
+      execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --variable=includedir
         OUTPUT_VARIABLE ${_include_DIR} )
       string(REGEX REPLACE "[\r\n]" " " ${_include_DIR} "${${_include_DIR}}")
 
 
-      exec_program(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --variable=libdir
+      execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --variable=libdir
         OUTPUT_VARIABLE ${_link_DIR} )
       string(REGEX REPLACE "[\r\n]" " " ${_link_DIR} "${${_link_DIR}}")
 
-      exec_program(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --libs
+      execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --libs
         OUTPUT_VARIABLE ${_link_FLAGS} )
       string(REGEX REPLACE "[\r\n]" " " ${_link_FLAGS} "${${_link_FLAGS}}")
 
-      exec_program(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --cflags
+      execute_process(COMMAND ${PKGCONFIG_EXECUTABLE} ${_package} --cflags
         OUTPUT_VARIABLE ${_cflags} )
       string(REGEX REPLACE "[\r\n]" " " ${_cflags} "${${_cflags}}")
 
diff --git a/Modules/UseSWIG.cmake b/Modules/UseSWIG.cmake
index ca16bc2..cece973 100644
--- a/Modules/UseSWIG.cmake
+++ b/Modules/UseSWIG.cmake
@@ -189,7 +189,7 @@
   :ref:`Makefile <Makefile Generators>`,
   :ref:`Ninja <Ninja Generators>`, :generator:`Xcode`, and
   :ref:`Visual Studio <Visual Studio Generators>`
-  (:generator:`Visual Studio 11 2012` and above) generators. Default value is
+  (:generator:`Visual Studio 12 2013` and above) generators. Default value is
   ``FALSE``.
 
   .. versionadded:: 3.21
@@ -353,7 +353,7 @@
   :ref:`Makefile <Makefile Generators>`,
   :ref:`Ninja <Ninja Generators>`, :generator:`Xcode`, and
   :ref:`Visual Studio <Visual Studio Generators>`
-  (:generator:`Visual Studio 11 2012` and above) generators. Default value is
+  (:generator:`Visual Studio 12 2013` and above) generators. Default value is
   ``FALSE``.
 
   Source file property ``USE_SWIG_DEPENDENCIES``, if not defined, will be
diff --git a/README.rst b/README.rst
index 64e2353..d885740 100644
--- a/README.rst
+++ b/README.rst
@@ -68,8 +68,8 @@
 Or, if you plan to develop CMake or otherwise run the test suite, create
 a separate build tree::
 
-  $ mkdir cmake-build && cd cmake-build
-  $ ../cmake-source/bootstrap && make
+  $ mkdir build && cd build
+  $ ../bootstrap && make
 
 Windows
 ^^^^^^^
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index f022dda..1bc855e 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -128,7 +128,8 @@
   cmCLocaleEnvironmentScope.cxx
   cmCMakePath.h
   cmCMakePath.cxx
-  cmCMakePresetErrors.h
+  cmCMakePresetsErrors.cxx
+  cmCMakePresetsErrors.h
   cmCMakePresetsGraph.cxx
   cmCMakePresetsGraph.h
   cmCMakePresetsGraphInternal.h
@@ -170,6 +171,8 @@
   cmCustomCommandTypes.h
   cmCxxModuleMapper.cxx
   cmCxxModuleMapper.h
+  cmCxxModuleUsageEffects.cxx
+  cmCxxModuleUsageEffects.h
   cmDefinitions.cxx
   cmDefinitions.h
   cmDependencyProvider.h
@@ -298,6 +301,8 @@
   cmGraphAdjacencyList.h
   cmGraphVizWriter.cxx
   cmGraphVizWriter.h
+  cmImportedCxxModuleInfo.cxx
+  cmImportedCxxModuleInfo.h
   cmInstallGenerator.h
   cmInstallGenerator.cxx
   cmInstallGetRuntimeDependenciesGenerator.h
@@ -323,6 +328,7 @@
   cmInstallTargetGenerator.cxx
   cmInstallDirectoryGenerator.h
   cmInstallDirectoryGenerator.cxx
+  cmJSONHelpers.cxx
   cmJSONHelpers.h
   cmJSONState.cxx
   cmJSONState.h
@@ -371,6 +377,8 @@
   cmNewLineStyle.cxx
   cmOrderDirectories.cxx
   cmOrderDirectories.h
+  cmPlistParser.cxx
+  cmPlistParser.h
   cmPolicies.h
   cmPolicies.cxx
   cmProcessOutput.cxx
@@ -409,6 +417,7 @@
   cmSourceFileLocationKind.h
   cmSourceGroup.cxx
   cmSourceGroup.h
+  cmStandardLevel.h
   cmStandardLevelResolver.cxx
   cmStandardLevelResolver.h
   cmState.cxx
@@ -420,6 +429,7 @@
   cmStateTypes.h
   cmStringAlgorithms.cxx
   cmStringAlgorithms.h
+  cmSyntheticTargetCache.h
   cmSystemTools.cxx
   cmSystemTools.h
   cmTarget.cxx
@@ -438,6 +448,7 @@
   cmUVHandlePtr.h
   cmUVProcessChain.cxx
   cmUVProcessChain.h
+  cmUVStream.h
   cmUVStreambuf.h
   cmUVSignalHackRAII.h
   cmVariableWatch.cxx
@@ -450,6 +461,8 @@
   cmWorkerPool.h
   cmWorkingDirectory.cxx
   cmWorkingDirectory.h
+  cmXcFramework.cxx
+  cmXcFramework.h
   cmXMLParser.cxx
   cmXMLParser.h
   cmXMLSafe.cxx
@@ -733,6 +746,8 @@
   cm_utf8.c
   cm_codecvt.hxx
   cm_codecvt.cxx
+  cm_fileno.hxx
+  cm_fileno.cxx
 
   cmDuration.h
   cmDuration.cxx
@@ -774,8 +789,6 @@
       cmDebuggerBreakpointManager.h
       cmDebuggerExceptionManager.cxx
       cmDebuggerExceptionManager.h
-      cmDebuggerPipeConnection.cxx
-      cmDebuggerPipeConnection.h
       cmDebuggerProtocol.cxx
       cmDebuggerProtocol.h
       cmDebuggerSourceBreakpoint.cxx
@@ -793,6 +806,21 @@
       cmDebuggerVariablesManager.cxx
       cmDebuggerVariablesManager.h
     )
+  if(WIN32)
+    target_sources(
+    CMakeLib
+    PRIVATE
+      cmDebuggerWindowsPipeConnection.cxx
+      cmDebuggerWindowsPipeConnection.h
+    )
+  else()
+    target_sources(
+    CMakeLib
+    PRIVATE
+      cmDebuggerPosixPipeConnection.cxx
+      cmDebuggerPosixPipeConnection.h
+    )
+  endif()
   target_link_libraries(CMakeLib PUBLIC cppdap::cppdap)
 endif()
 
@@ -928,6 +956,43 @@
     )
 endif()
 
+if(CMake_BUILD_PCH)
+  target_precompile_headers(CMakeLib PRIVATE
+    "$<$<COMPILE_LANGUAGE:CXX>:<string$<ANGLE-R>>"
+    "$<$<COMPILE_LANGUAGE:CXX>:<iostream$<ANGLE-R>>"
+    "$<$<COMPILE_LANGUAGE:CXX>:<sstream$<ANGLE-R>>"
+    "$<$<COMPILE_LANGUAGE:CXX>:<iomanip$<ANGLE-R>>"
+    "$<$<COMPILE_LANGUAGE:CXX>:<cm/memory$<ANGLE-R>>"
+    "$<$<COMPILE_LANGUAGE:CXX>:<cm3p/cppdap/protocol.h$<ANGLE-R>>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmMakefile.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmGlobalGenerator.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmLocalGenerator.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmGeneratorTarget.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmGeneratorExpression.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmArgumentParser.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmake.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmCMakePath.h>"
+    "$<$<COMPILE_LANGUAGE:CXX>:cmCurl.h>")
+
+    set_source_files_properties(
+      "LexerParser/cmFortranLexer.cxx"
+      PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+
+  if(WIN32)
+    target_precompile_headers(CMakeLib PRIVATE
+      "$<$<COMPILE_LANGUAGE:CXX>:<cm3p/uv.h$<ANGLE-R>>"
+      "$<$<COMPILE_LANGUAGE:CXX>:cmVSSetupHelper.h>")
+    set_source_files_properties("LexerParser/cmFortranParser.cxx" PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+  else()
+    set_source_files_properties(
+      "LexerParser/cmCommandArgumentLexer.cxx"
+      "LexerParser/cmGccDepfileLexer.cxx"
+      "LexerParser/cmExprLexer.cxx"
+      "LexerParser/cmDependsJavaLexer.cxx"
+      PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+  endif()
+endif()
+
 # Temporary variable for tools targets
 set(_tools)
 
@@ -1036,6 +1101,24 @@
   )
 target_link_libraries(CTestLib PUBLIC CMakeLib)
 
+if(CMake_BUILD_PCH)
+  target_precompile_headers(CTestLib PRIVATE
+    "cmDuration.h"
+    "cmMakefile.h"
+    "cmSystemTools.h"
+    "cmGlobalGenerator.h"
+    "cmake.h"
+    "CTest/cmCTestGenericHandler.h"
+    "<sstream>"
+    "<cm3p/uv.h>")
+
+  if(WIN32)
+    target_precompile_headers(CTestLib PRIVATE "cmCurl.h" "CTest/cmCTestMultiProcessHandler.h")
+  else()
+    set_source_files_properties("LexerParser/cmCTestResourceGroupsLexer.cxx" PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
+  endif()
+endif()
+
 #
 # Build CPackLib
 #
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 5fdfce6..34375d5 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,8 +1,8 @@
 # CMake version number components.
 set(CMake_VERSION_MAJOR 3)
-set(CMake_VERSION_MINOR 27)
-set(CMake_VERSION_PATCH 7)
-#set(CMake_VERSION_RC 0)
+set(CMake_VERSION_MINOR 28)
+set(CMake_VERSION_PATCH 0)
+set(CMake_VERSION_RC 1)
 set(CMake_VERSION_IS_DIRTY 0)
 
 # Start with the full version number used in tags.  It has no dev info.
diff --git a/Source/CPack/WiX/cmCPackWIXGenerator.cxx b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
index 1ea78fd..5077596 100644
--- a/Source/CPack/WiX/cmCPackWIXGenerator.cxx
+++ b/Source/CPack/WiX/cmCPackWIXGenerator.cxx
@@ -7,6 +7,7 @@
 #include <cm/memory>
 #include <cm/string_view>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/Encoding.hxx"
@@ -54,7 +55,7 @@
 
 bool cmCPackWIXGenerator::RunWiXCommand(std::string const& command)
 {
-  std::string logFileName = this->CPackTopLevel + "/wix.log";
+  std::string logFileName = cmStrCat(this->CPackTopLevel, "/wix.log");
 
   cmCPackLogger(cmCPackLog::LOG_DEBUG,
                 "Running WiX command: " << command << std::endl);
@@ -62,8 +63,9 @@
   std::string output;
 
   int returnValue = 0;
-  bool status = cmSystemTools::RunSingleCommand(
-    command, &output, &output, &returnValue, 0, cmSystemTools::OUTPUT_NONE);
+  bool status =
+    cmSystemTools::RunSingleCommand(command, &output, &output, &returnValue,
+                                    nullptr, cmSystemTools::OUTPUT_NONE);
 
   cmsys::ofstream logFile(logFileName.c_str(), std::ios::app);
   logFile << command << std::endl;
@@ -102,22 +104,22 @@
   }
 
   std::ostringstream command;
-  command << QuotePath(executable);
-  command << " -nologo";
-  command << " -arch " << arch;
-  command << " -out " << QuotePath(objectFile);
+  command << QuotePath(executable)
+          << " -nologo"
+             " -arch "
+          << arch << " -out " << QuotePath(objectFile);
 
   for (std::string const& ext : CandleExtensions) {
     command << " -ext " << QuotePath(ext);
   }
 
   if (!cmHasSuffix(sourceFile, this->CPackTopLevel)) {
-    command << " " << QuotePath("-I" + this->CPackTopLevel);
+    command << ' ' << QuotePath(cmStrCat("-I", this->CPackTopLevel));
   }
 
   AddCustomFlags("CPACK_WIX_CANDLE_EXTRA_FLAGS", command);
 
-  command << " " << QuotePath(sourceFile);
+  command << ' ' << QuotePath(sourceFile);
 
   return RunWiXCommand(command.str());
 }
@@ -130,9 +132,10 @@
   }
 
   std::ostringstream command;
-  command << QuotePath(executable);
-  command << " -nologo";
-  command << " -out " << QuotePath(CMakeToWixPath(packageFileNames.at(0)));
+  command << QuotePath(executable)
+          << " -nologo"
+             " -out "
+          << QuotePath(CMakeToWixPath(packageFileNames.at(0)));
 
   for (std::string const& ext : this->LightExtensions) {
     command << " -ext " << QuotePath(ext);
@@ -145,7 +148,7 @@
 
   AddCustomFlags("CPACK_WIX_LIGHT_EXTRA_FLAGS", command);
 
-  command << " " << objectFiles;
+  command << ' ' << objectFiles;
 
   return RunWiXCommand(command.str());
 }
@@ -196,7 +199,8 @@
   }
 
   if (!GetOption("CPACK_WIX_LICENSE_RTF")) {
-    std::string licenseFilename = this->CPackTopLevel + "/License.rtf";
+    std::string licenseFilename =
+      cmStrCat(this->CPackTopLevel, "/License.rtf");
     SetOption("CPACK_WIX_LICENSE_RTF", licenseFilename);
 
     if (!CreateLicenseFile()) {
@@ -293,14 +297,14 @@
     usedBaseNames.insert(uniqueBaseName);
 
     std::string objectFilename =
-      this->CPackTopLevel + "/" + uniqueBaseName + ".wixobj";
+      cmStrCat(this->CPackTopLevel, '/', uniqueBaseName, ".wixobj");
 
     if (!RunCandleCommand(CMakeToWixPath(sourceFilename),
                           CMakeToWixPath(objectFilename))) {
       return false;
     }
 
-    objectFiles << " " << QuotePath(CMakeToWixPath(objectFilename));
+    objectFiles << ' ' << QuotePath(CMakeToWixPath(objectFilename));
   }
 
   AppendUserSuppliedExtraObjects(objectFiles);
@@ -311,8 +315,9 @@
 void cmCPackWIXGenerator::AppendUserSuppliedExtraSources()
 {
   cmValue cpackWixExtraSources = GetOption("CPACK_WIX_EXTRA_SOURCES");
-  if (!cpackWixExtraSources)
+  if (!cpackWixExtraSources) {
     return;
+  }
 
   cmExpandList(cpackWixExtraSources, this->WixSources);
 }
@@ -320,8 +325,9 @@
 void cmCPackWIXGenerator::AppendUserSuppliedExtraObjects(std::ostream& stream)
 {
   cmValue cpackWixExtraObjects = GetOption("CPACK_WIX_EXTRA_OBJECTS");
-  if (!cpackWixExtraObjects)
+  if (!cpackWixExtraObjects) {
     return;
+  }
 
   cmList expandedExtraObjects{ cpackWixExtraObjects };
 
@@ -332,7 +338,8 @@
 
 void cmCPackWIXGenerator::CreateWiXVariablesIncludeFile()
 {
-  std::string includeFilename = this->CPackTopLevel + "/cpack_variables.wxi";
+  std::string includeFilename =
+    cmStrCat(this->CPackTopLevel, "/cpack_variables.wxi");
 
   cmWIXSourceWriter includeFile(this->Logger, includeFilename,
                                 this->ComponentGuidType,
@@ -356,7 +363,8 @@
 
 void cmCPackWIXGenerator::CreateWiXPropertiesIncludeFile()
 {
-  std::string includeFilename = this->CPackTopLevel + "/properties.wxi";
+  std::string includeFilename =
+    cmStrCat(this->CPackTopLevel, "/properties.wxi");
 
   cmWIXSourceWriter includeFile(this->Logger, includeFilename,
                                 this->ComponentGuidType,
@@ -405,7 +413,8 @@
 
 void cmCPackWIXGenerator::CreateWiXProductFragmentIncludeFile()
 {
-  std::string includeFilename = this->CPackTopLevel + "/product_fragment.wxi";
+  std::string includeFilename =
+    cmStrCat(this->CPackTopLevel, "/product_fragment.wxi");
 
   cmWIXSourceWriter includeFile(this->Logger, includeFilename,
                                 this->ComponentGuidType,
@@ -444,7 +453,7 @@
   // if install folder is supposed to be set absolutely, the default
   // component guid "*" cannot be used
   std::string directoryDefinitionsFilename =
-    this->CPackTopLevel + "/directories.wxs";
+    cmStrCat(this->CPackTopLevel, "/directories.wxs");
 
   this->WixSources.push_back(directoryDefinitionsFilename);
 
@@ -466,7 +475,8 @@
     directoryDefinitions.BeginInstallationPrefixDirectory(GetRootFolderId(),
                                                           installRoot);
 
-  std::string fileDefinitionsFilename = this->CPackTopLevel + "/files.wxs";
+  std::string fileDefinitionsFilename =
+    cmStrCat(this->CPackTopLevel, "/files.wxs");
 
   this->WixSources.push_back(fileDefinitionsFilename);
 
@@ -477,7 +487,7 @@
   fileDefinitions.BeginElement("Fragment");
 
   std::string featureDefinitionsFilename =
-    this->CPackTopLevel + "/features.wxs";
+    cmStrCat(this->CPackTopLevel, "/features.wxs");
 
   this->WixSources.push_back(featureDefinitionsFilename);
 
@@ -536,7 +546,7 @@
 
       std::string componentPath = cmStrCat(toplevel, '/', component.Name);
 
-      std::string const componentFeatureId = "CM_C_" + component.Name;
+      std::string const componentFeatureId = cmStrCat("CM_C_", component.Name);
 
       cmWIXShortcuts featureShortcuts;
       AddComponentsToFeature(componentPath, componentFeatureId,
@@ -556,7 +566,7 @@
   bool emitUninstallShortcut = true;
   cmValue cpackWixProgramMenuFolder =
     GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
-  if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == ".") {
+  if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == "."_s) {
     emitUninstallShortcut = false;
   } else if (emittedShortcutTypes.find(cmWIXShortcuts::START_MENU) ==
              emittedShortcutTypes.end()) {
@@ -613,7 +623,7 @@
     result = *rootFolderId;
   }
 
-  if (GetArchitecture() == "x86") {
+  if (GetArchitecture() == "x86"_s) {
     cmSystemTools::ReplaceString(result, "<64>", "");
   } else {
     cmSystemTools::ReplaceString(result, "<64>", "64");
@@ -636,7 +646,7 @@
     return false;
   }
 
-  std::string mainSourceFilePath = this->CPackTopLevel + "/main.wxs";
+  std::string mainSourceFilePath = cmStrCat(this->CPackTopLevel, "/main.wxs");
 
   if (!ConfigureFile(wixTemplate, mainSourceFilePath)) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -656,7 +666,7 @@
 {
   for (auto const& i : ComponentGroups) {
     cmCPackComponentGroup const& group = i.second;
-    if (group.ParentGroup == 0) {
+    if (group.ParentGroup == nullptr) {
       featureDefinitions.EmitFeatureForComponentGroup(group, *this->Patch);
     }
   }
@@ -757,7 +767,7 @@
     case cmWIXShortcuts::START_MENU: {
       cmValue cpackWixProgramMenuFolder =
         GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
-      if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == ".") {
+      if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder == "."_s) {
         directoryId = "ProgramMenuFolder";
       } else {
         directoryId = "PROGRAM_MENU_FOLDER";
@@ -788,13 +798,13 @@
 
   std::string idSuffix;
   if (!cpackComponentName.empty()) {
-    idSuffix += "_";
+    idSuffix += '_';
     idSuffix += cpackComponentName;
   }
 
   std::string componentId = "CM_SHORTCUT";
-  if (idPrefix.size()) {
-    componentId += "_" + idPrefix;
+  if (!idPrefix.empty()) {
+    componentId += cmStrCat('_', idPrefix);
   }
 
   componentId += idSuffix;
@@ -810,7 +820,7 @@
   this->Patch->ApplyFragment(componentId, fileDefinitions);
 
   std::string registryKey =
-    std::string("Software\\") + cpackVendor + "\\" + cpackPackageName;
+    cmStrCat("Software\\", cpackVendor, '\\', cpackPackageName);
 
   shortcuts.EmitShortcuts(type, registryKey, cpackComponentName,
                           fileDefinitions);
@@ -818,9 +828,9 @@
   if (type == cmWIXShortcuts::START_MENU) {
     cmValue cpackWixProgramMenuFolder =
       GetOption("CPACK_WIX_PROGRAM_MENU_FOLDER");
-    if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder != ".") {
-      fileDefinitions.EmitRemoveFolder("CM_REMOVE_PROGRAM_MENU_FOLDER" +
-                                       idSuffix);
+    if (cpackWixProgramMenuFolder && cpackWixProgramMenuFolder != "."_s) {
+      fileDefinitions.EmitRemoveFolder(
+        cmStrCat("CM_REMOVE_PROGRAM_MENU_FOLDER", idSuffix));
     }
   }
 
@@ -851,10 +861,10 @@
 
   std::string extension = GetRightmostExtension(licenseSourceFilename);
 
-  if (extension == ".rtf") {
+  if (extension == ".rtf"_s) {
     cmSystemTools::CopyAFile(licenseSourceFilename.c_str(),
                              licenseDestinationFilename.c_str());
-  } else if (extension == ".txt") {
+  } else if (extension == ".txt"_s) {
     cmWIXRichTextFormatWriter rtfWriter(licenseDestinationFilename);
 
     cmsys::ifstream licenseSource(licenseSourceFilename.c_str());
@@ -923,19 +933,18 @@
   for (size_t i = 0; i < dir.GetNumberOfFiles(); ++i) {
     std::string fileName = dir.GetFile(static_cast<unsigned long>(i));
 
-    if (fileName == "." || fileName == "..") {
+    if (fileName == "."_s || fileName == ".."_s) {
       continue;
     }
 
-    std::string fullPath = topdir + "/" + fileName;
+    std::string fullPath = cmStrCat(topdir, '/', fileName);
 
-    std::string relativePath =
-      cmSystemTools::RelativePath(toplevel.c_str(), fullPath.c_str());
+    std::string relativePath = cmSystemTools::RelativePath(toplevel, fullPath);
 
     std::string id = PathToId(relativePath);
 
-    if (cmSystemTools::FileIsDirectory(fullPath.c_str())) {
-      std::string subDirectoryId = std::string("CM_D") + id;
+    if (cmSystemTools::FileIsDirectory(fullPath)) {
+      std::string subDirectoryId = cmStrCat("CM_D", id);
 
       directoryDefinitions.BeginElement("Directory");
       directoryDefinitions.AddAttribute("Id", subDirectoryId);
@@ -965,7 +974,7 @@
         std::string const& textLabel = packageExecutables[j];
 
         if (cmSystemTools::LowerCase(fileName) ==
-            cmSystemTools::LowerCase(executableName) + ".exe") {
+            cmStrCat(cmSystemTools::LowerCase(executableName), ".exe")) {
           cmWIXShortcut shortcut;
           shortcut.label = textLabel;
           shortcut.workingDirectoryId = directoryId;
@@ -988,12 +997,12 @@
     value = *tmp;
 
     return true;
-  } else {
-    cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Required variable " << name << " not set" << std::endl);
-
-    return false;
   }
+
+  cmCPackLogger(cmCPackLog::LOG_ERROR,
+                "Required variable " << name << " not set" << std::endl);
+
+  return false;
 }
 
 std::string cmCPackWIXGenerator::GetArchitecture() const
@@ -1001,11 +1010,10 @@
   std::string void_p_size;
   RequireOption("CPACK_WIX_SIZEOF_VOID_P", void_p_size);
 
-  if (void_p_size == "8") {
+  if (void_p_size == "8"_s) {
     return "x64";
-  } else {
-    return "x86";
   }
+  return "x86";
 }
 
 std::string cmCPackWIXGenerator::GenerateGUID()
@@ -1034,7 +1042,7 @@
 
 std::string cmCPackWIXGenerator::QuotePath(std::string const& path)
 {
-  return std::string("\"") + path + '"';
+  return cmStrCat('"', path, '"');
 }
 
 std::string cmCPackWIXGenerator::GetRightmostExtension(
@@ -1042,7 +1050,7 @@
 {
   std::string extension;
 
-  std::string::size_type i = filename.rfind(".");
+  std::string::size_type i = filename.rfind('.');
   if (i != std::string::npos) {
     extension = filename.substr(i);
   }
@@ -1052,9 +1060,10 @@
 
 std::string cmCPackWIXGenerator::PathToId(std::string const& path)
 {
-  id_map_t::const_iterator i = PathToIdMap.find(path);
-  if (i != PathToIdMap.end())
+  auto i = PathToIdMap.find(path);
+  if (i != PathToIdMap.end()) {
     return i->second;
+  }
 
   std::string id = CreateNewIdForPath(path);
   return id;
@@ -1063,7 +1072,7 @@
 std::string cmCPackWIXGenerator::CreateNewIdForPath(std::string const& path)
 {
   std::vector<std::string> components;
-  cmSystemTools::SplitPath(path.c_str(), components, false);
+  cmSystemTools::SplitPath(path, components, false);
 
   size_t replacementCount = 0;
 
@@ -1071,8 +1080,9 @@
   std::string currentComponent;
 
   for (size_t i = 1; i < components.size(); ++i) {
-    if (i != 1)
+    if (i != 1) {
       identifier += '.';
+    }
 
     currentComponent =
       NormalizeComponentForId(components[i], replacementCount);
@@ -1088,18 +1098,19 @@
   }
 
   std::ostringstream result;
-  result << idPrefix << "_" << identifier;
+  result << idPrefix << '_' << identifier;
 
   size_t ambiguityCount = ++IdAmbiguityCounter[identifier];
 
   if (ambiguityCount > 999) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "Error while trying to generate a unique Id for '"
-                    << path << "'" << std::endl);
+                    << path << '\'' << std::endl);
 
     return std::string();
-  } else if (ambiguityCount > 1) {
-    result << "_" << ambiguityCount;
+  }
+  if (ambiguityCount > 1) {
+    result << '_' << ambiguityCount;
   }
 
   std::string resultString = result.str();
@@ -1157,8 +1168,9 @@
                                             extension_set_t& extensions)
 {
   cmValue variableContent = GetOption(variableName);
-  if (!variableContent)
+  if (!variableContent) {
     return;
+  }
 
   cmList list{ variableContent };
   extensions.insert(list.begin(), list.end());
@@ -1182,7 +1194,8 @@
     } else {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "Invalid element in CPACK_WIX_CUSTOM_XMLNS ignored: "
-                      << "\"" << str << "\"" << std::endl);
+                    "\""
+                      << str << '"' << std::endl);
     }
   }
   std::ostringstream oss;
@@ -1197,13 +1210,14 @@
                                          std::ostream& stream)
 {
   cmValue variableContent = GetOption(variableName);
-  if (!variableContent)
+  if (!variableContent) {
     return;
+  }
 
   cmList list{ variableContent };
 
   for (std::string const& i : list) {
-    stream << " " << QuotePath(i);
+    stream << ' ' << QuotePath(i);
   }
 }
 
@@ -1222,6 +1236,7 @@
 void cmCPackWIXGenerator::InjectXmlNamespaces(cmWIXSourceWriter& sourceWriter)
 {
   for (auto& ns : this->CustomXmlNamespaces) {
-    sourceWriter.AddAttributeUnlessEmpty("xmlns:" + ns.first, ns.second);
+    sourceWriter.AddAttributeUnlessEmpty(cmStrCat("xmlns:", ns.first),
+                                         ns.second);
   }
 }
diff --git a/Source/CPack/WiX/cmWIXAccessControlList.cxx b/Source/CPack/WiX/cmWIXAccessControlList.cxx
index 2261a66..0ebe2f4 100644
--- a/Source/CPack/WiX/cmWIXAccessControlList.cxx
+++ b/Source/CPack/WiX/cmWIXAccessControlList.cxx
@@ -102,13 +102,14 @@
     "Write",
     "WriteAttributes",
     "WriteExtendedAttributes",
-    0
+    nullptr
   };
 
   size_t i = 0;
   while (validAttributes[i]) {
-    if (name == validAttributes[i++])
+    if (name == validAttributes[i++]) {
       return true;
+    }
   }
 
   return false;
@@ -118,9 +119,8 @@
                                                   std::string const& name)
 {
   if (!this->IsBooleanAttribute(name)) {
-    std::ostringstream message;
-    message << "Unknown boolean attribute '" << name << "'";
-    this->ReportError(entry, message.str());
+    this->ReportError(entry,
+                      cmStrCat("Unknown boolean attribute '", name, '\''));
   }
 
   this->SourceWriter.AddAttribute(name, "yes");
diff --git a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
index 0a83ca2..a655d86 100644
--- a/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXDirectoriesSourceWriter.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWIXDirectoriesSourceWriter.h"
 
+#include <cmext/string_view>
+
 cmWIXDirectoriesSourceWriter::cmWIXDirectoriesSourceWriter(
   cmCPackLog* logger, std::string const& filename, GuidType componentGuidType)
   : cmWIXSourceWriter(logger, filename, componentGuidType)
@@ -14,7 +16,7 @@
   BeginElement("Directory");
   AddAttribute("Id", "ProgramMenuFolder");
 
-  if (startMenuFolder != ".") {
+  if (startMenuFolder != "."_s) {
     BeginElement("Directory");
     AddAttribute("Id", "PROGRAM_MENU_FOLDER");
     AddAttribute("Name", startMenuFolder);
@@ -53,7 +55,7 @@
 
   std::vector<std::string> installRoot;
 
-  cmSystemTools::SplitPath(installRootString.c_str(), installRoot);
+  cmSystemTools::SplitPath(installRootString, installRoot);
 
   if (!installRoot.empty() && installRoot.back().empty()) {
     installRoot.pop_back();
diff --git a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx
index a7a0648..78c2208 100644
--- a/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXFeaturesSourceWriter.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmWIXFeaturesSourceWriter.h"
 
+#include "cmStringAlgorithms.h"
+
 cmWIXFeaturesSourceWriter::cmWIXFeaturesSourceWriter(
   cmCPackLog* logger, std::string const& filename, GuidType componentGuidType)
   : cmWIXSourceWriter(logger, filename, componentGuidType)
@@ -17,7 +19,7 @@
   AddAttribute("Guid", CreateGuidFromComponentId("CM_PACKAGE_REGISTRY"));
 
   std::string registryKey =
-    std::string("Software\\Kitware\\CMake\\Packages\\") + package;
+    cmStrCat(R"(Software\Kitware\CMake\Packages\)", package);
 
   BeginElement("RegistryValue");
   AddAttribute("Root", "HKLM");
@@ -35,7 +37,7 @@
   cmCPackComponentGroup const& group, cmWIXPatch& patch)
 {
   BeginElement("Feature");
-  AddAttribute("Id", "CM_G_" + group.Name);
+  AddAttribute("Id", cmStrCat("CM_G_", group.Name));
 
   if (group.IsExpandedByDefault) {
     AddAttribute("Display", "expand");
@@ -44,7 +46,7 @@
   AddAttributeUnlessEmpty("Title", group.DisplayName);
   AddAttributeUnlessEmpty("Description", group.Description);
 
-  patch.ApplyFragment("CM_G_" + group.Name, *this);
+  patch.ApplyFragment(cmStrCat("CM_G_", group.Name), *this);
 
   for (cmCPackComponentGroup* subgroup : group.Subgroups) {
     EmitFeatureForComponentGroup(*subgroup, patch);
@@ -61,7 +63,7 @@
   cmCPackComponent const& component, cmWIXPatch& patch)
 {
   BeginElement("Feature");
-  AddAttribute("Id", "CM_C_" + component.Name);
+  AddAttribute("Id", cmStrCat("CM_C_", component.Name));
 
   AddAttributeUnlessEmpty("Title", component.DisplayName);
   AddAttributeUnlessEmpty("Description", component.Description);
@@ -78,7 +80,7 @@
     AddAttribute("Level", "2");
   }
 
-  patch.ApplyFragment("CM_C_" + component.Name, *this);
+  patch.ApplyFragment(cmStrCat("CM_C_", component.Name), *this);
 
   EndElement("Feature");
 }
diff --git a/Source/CPack/WiX/cmWIXPatch.cxx b/Source/CPack/WiX/cmWIXPatch.cxx
index 122ffaf..c65449c 100644
--- a/Source/CPack/WiX/cmWIXPatch.cxx
+++ b/Source/CPack/WiX/cmWIXPatch.cxx
@@ -14,7 +14,7 @@
   cmWIXPatchParser parser(Fragments, Logger);
   if (!parser.ParseFile(patchFilePath.c_str())) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Failed parsing XML patch file: '" << patchFilePath << "'"
+                  "Failed parsing XML patch file: '" << patchFilePath << '\''
                                                      << std::endl);
     return false;
   }
@@ -25,9 +25,10 @@
 void cmWIXPatch::ApplyFragment(std::string const& id,
                                cmWIXSourceWriter& writer)
 {
-  cmWIXPatchParser::fragment_map_t::iterator i = Fragments.find(id);
-  if (i == Fragments.end())
+  auto i = Fragments.find(id);
+  if (i == Fragments.end()) {
     return;
+  }
 
   const cmWIXPatchElement& fragment = i->second;
   for (auto const& attr : fragment.attributes) {
@@ -75,9 +76,9 @@
       fragmentList += ", ";
     }
 
-    fragmentList += "'";
+    fragmentList += '\'';
     fragmentList += fragment.first;
-    fragmentList += "'";
+    fragmentList += '\'';
   }
 
   if (!fragmentList.empty()) {
diff --git a/Source/CPack/WiX/cmWIXPatchParser.cxx b/Source/CPack/WiX/cmWIXPatchParser.cxx
index 8b26c4e..136eaac 100644
--- a/Source/CPack/WiX/cmWIXPatchParser.cxx
+++ b/Source/CPack/WiX/cmWIXPatchParser.cxx
@@ -5,6 +5,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cmext/string_view>
 
 #include <cm3p/expat.h>
 
@@ -20,9 +21,7 @@
   return cmWIXPatchNode::ELEMENT;
 }
 
-cmWIXPatchNode::~cmWIXPatchNode()
-{
-}
+cmWIXPatchNode::~cmWIXPatchNode() = default;
 
 cmWIXPatchElement::cmWIXPatchElement() = default;
 cmWIXPatchElement::~cmWIXPatchElement() = default;
@@ -39,13 +38,13 @@
 void cmWIXPatchParser::StartElement(const std::string& name, const char** atts)
 {
   if (State == BEGIN_DOCUMENT) {
-    if (name == "CPackWiXPatch") {
+    if (name == "CPackWiXPatch"_s) {
       State = BEGIN_FRAGMENTS;
     } else {
       ReportValidationError("Expected root element 'CPackWiXPatch'");
     }
   } else if (State == BEGIN_FRAGMENTS) {
-    if (name == "CPackWiXFragment") {
+    if (name == "CPackWiXFragment"_s) {
       State = INSIDE_FRAGMENT;
       StartFragment(atts);
     } else {
@@ -78,7 +77,7 @@
     const std::string key = attributes[i];
     const std::string value = attributes[i + 1];
 
-    if (key == "Id") {
+    if (key == "Id"_s) {
       if (Fragments.find(value) != Fragments.end()) {
         std::ostringstream tmp;
         tmp << "Invalid reuse of 'CPackWixFragment' 'Id': " << value;
@@ -98,7 +97,7 @@
       const std::string key = attributes[i];
       const std::string value = attributes[i + 1];
 
-      if (key != "Id") {
+      if (key != "Id"_s) {
         new_element->attributes[key] = value;
       }
     }
@@ -108,7 +107,7 @@
 void cmWIXPatchParser::EndElement(const std::string& name)
 {
   if (State == INSIDE_FRAGMENT) {
-    if (name == "CPackWiXFragment") {
+    if (name == "CPackWiXFragment"_s) {
       State = BEGIN_FRAGMENTS;
       ElementStack.clear();
     } else {
@@ -142,7 +141,7 @@
 {
   cmCPackLogger(cmCPackLog::LOG_ERROR,
                 "Error while processing XML patch file at "
-                  << line << ":" << column << ":  " << msg << std::endl);
+                  << line << ':' << column << ":  " << msg << std::endl);
   Valid = false;
 }
 
diff --git a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
index d7e534a..8a63239 100644
--- a/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
+++ b/Source/CPack/WiX/cmWIXRichTextFormatWriter.cxx
@@ -135,7 +135,7 @@
 
 void cmWIXRichTextFormatWriter::ControlWord(std::string const& keyword)
 {
-  File << "\\" << keyword;
+  File << '\\' << keyword;
 }
 
 void cmWIXRichTextFormatWriter::NewControlWord(std::string const& keyword)
@@ -158,7 +158,8 @@
   // Do not emit byte order mark (BOM)
   if (c == 0xFEFF) {
     return;
-  } else if (c <= 0xFFFF) {
+  }
+  if (c <= 0xFFFF) {
     EmitUnicodeSurrogate(c);
   } else {
     c -= 0x10000;
@@ -175,12 +176,12 @@
   } else {
     File << (c - 65536);
   }
-  File << "?";
+  File << '?';
 }
 
 void cmWIXRichTextFormatWriter::EmitInvalidCodepoint(int c)
 {
   ControlWord("cf1 ");
-  File << "[INVALID-BYTE-" << int(c) << "]";
+  File << "[INVALID-BYTE-" << c << ']';
   ControlWord("cf0 ");
 }
diff --git a/Source/CPack/WiX/cmWIXShortcut.cxx b/Source/CPack/WiX/cmWIXShortcut.cxx
index c3eb219..1cfb6c1 100644
--- a/Source/CPack/WiX/cmWIXShortcut.cxx
+++ b/Source/CPack/WiX/cmWIXShortcut.cxx
@@ -20,7 +20,7 @@
   std::string const& cpackComponentName,
   cmWIXFilesSourceWriter& fileDefinitions) const
 {
-  shortcut_type_map_t::const_iterator i = this->Shortcuts.find(type);
+  auto i = this->Shortcuts.find(type);
 
   if (i == this->Shortcuts.end()) {
     return false;
diff --git a/Source/CPack/WiX/cmWIXSourceWriter.cxx b/Source/CPack/WiX/cmWIXSourceWriter.cxx
index 8e9bfdf..ef6712a 100644
--- a/Source/CPack/WiX/cmWIXSourceWriter.cxx
+++ b/Source/CPack/WiX/cmWIXSourceWriter.cxx
@@ -5,6 +5,7 @@
 #include <windows.h>
 
 #include "cmCPackGenerator.h"
+#include "cmCryptoHash.h"
 #include "cmUuid.h"
 
 cmWIXSourceWriter::cmWIXSourceWriter(cmCPackLog* logger,
@@ -34,7 +35,7 @@
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   Elements.size() - 1
                     << " WiX elements were still open when closing '"
-                    << SourceFilename << "'" << std::endl);
+                    << SourceFilename << '\'' << std::endl);
     return;
   }
 
@@ -44,12 +45,12 @@
 void cmWIXSourceWriter::BeginElement(std::string const& name)
 {
   if (State == BEGIN) {
-    File << ">";
+    File << '>';
   }
 
-  File << "\n";
+  File << '\n';
   Indent(Elements.size());
-  File << "<" << name;
+  File << '<' << name;
 
   Elements.push_back(name);
   State = BEGIN;
@@ -60,7 +61,7 @@
   if (Elements.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "can not end WiX element with no open elements in '"
-                    << SourceFilename << "'" << std::endl);
+                    << SourceFilename << '\'' << std::endl);
     return;
   }
 
@@ -68,14 +69,14 @@
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "WiX element <"
                     << Elements.back() << "> can not be closed by </" << name
-                    << "> in '" << SourceFilename << "'" << std::endl);
+                    << "> in '" << SourceFilename << '\'' << std::endl);
     return;
   }
 
   if (State == DEFAULT) {
-    File << "\n";
+    File << '\n';
     Indent(Elements.size() - 1);
-    File << "</" << Elements.back() << ">";
+    File << "</" << Elements.back() << '>';
   } else {
     File << "/>";
   }
@@ -87,17 +88,17 @@
 void cmWIXSourceWriter::AddTextNode(std::string const& text)
 {
   if (State == BEGIN) {
-    File << ">";
+    File << '>';
   }
 
   if (Elements.empty()) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
                   "can not add text without open WiX element in '"
-                    << SourceFilename << "'" << std::endl);
+                    << SourceFilename << '\'' << std::endl);
     return;
   }
 
-  File << this->EscapeAttributeValue(text);
+  File << cmWIXSourceWriter::EscapeAttributeValue(text);
   State = DEFAULT;
 }
 
@@ -105,12 +106,12 @@
                                                  std::string const& content)
 {
   if (State == BEGIN) {
-    File << ">";
+    File << '>';
   }
 
-  File << "\n";
+  File << '\n';
   Indent(Elements.size());
-  File << "<?" << target << " " << content << "?>";
+  File << "<?" << target << ' ' << content << "?>";
 
   State = DEFAULT;
 }
@@ -118,7 +119,7 @@
 void cmWIXSourceWriter::AddAttribute(std::string const& key,
                                      std::string const& value)
 {
-  File << " " << key << "=\"" << EscapeAttributeValue(value) << '"';
+  File << ' ' << key << "=\"" << EscapeAttributeValue(value) << '"';
 }
 
 void cmWIXSourceWriter::AddAttributeUnlessEmpty(std::string const& key,
@@ -134,7 +135,8 @@
 {
   std::string guid = "*";
   if (this->ComponentGuidType == CMAKE_GENERATED_GUID) {
-    std::string md5 = cmSystemTools::ComputeStringMD5(componentId);
+    cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+    std::string md5 = hasher.HashString(componentId);
     cmUuid uuid;
     std::vector<unsigned char> ns;
     guid = uuid.FromMd5(ns, md5);
@@ -144,7 +146,7 @@
 
 void cmWIXSourceWriter::WriteXMLDeclaration()
 {
-  File << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
+  File << R"(<?xml version="1.0" encoding="UTF-8"?>)" << std::endl;
 }
 
 void cmWIXSourceWriter::Indent(size_t count)
diff --git a/Source/CPack/cmCPackBundleGenerator.cxx b/Source/CPack/cmCPackBundleGenerator.cxx
index 7e6e473..37be798 100644
--- a/Source/CPack/cmCPackBundleGenerator.cxx
+++ b/Source/CPack/cmCPackBundleGenerator.cxx
@@ -86,24 +86,28 @@
   std::string const staging = toplevel;
 
   std::ostringstream contents;
-  contents << staging << "/" << cpack_bundle_name << ".app/"
-           << "Contents";
+  contents << staging << "/" << cpack_bundle_name
+           << ".app/"
+              "Contents";
 
   std::ostringstream application;
-  application << contents.str() << "/"
-              << "MacOS";
+  application << contents.str()
+              << "/"
+                 "MacOS";
 
   std::ostringstream resources;
-  resources << contents.str() << "/"
-            << "Resources";
+  resources << contents.str()
+            << "/"
+               "Resources";
 
   // Install a required, user-provided bundle metadata file ...
   std::ostringstream plist_source;
   plist_source << cpack_bundle_plist;
 
   std::ostringstream plist_target;
-  plist_target << contents.str() << "/"
-               << "Info.plist";
+  plist_target << contents.str()
+               << "/"
+                  "Info.plist";
 
   if (!this->CopyFile(plist_source, plist_target)) {
     cmCPackLogger(
@@ -196,15 +200,11 @@
 
     // sign the files supplied by the user, ie. frameworks.
     for (auto const& file : relFiles) {
-      std::ostringstream temp_sign_file_cmd;
-      temp_sign_file_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
-      temp_sign_file_cmd << " " << sign_parameter << " -s \""
-                         << cpack_apple_cert_app;
-      temp_sign_file_cmd << "\" -i ";
-      temp_sign_file_cmd << this->GetOption("CPACK_APPLE_BUNDLE_ID");
-      temp_sign_file_cmd << " \"";
-      temp_sign_file_cmd << bundle_path;
-      temp_sign_file_cmd << file << "\"";
+      auto temp_sign_file_cmd =
+        cmStrCat(this->GetOption("CPACK_COMMAND_CODESIGN"), ' ',
+                 sign_parameter, " -s \"", cpack_apple_cert_app, "\" -i ",
+                 this->GetOption("CPACK_APPLE_BUNDLE_ID"), " \"", bundle_path,
+                 file, '"');
 
       if (!this->RunCommand(temp_sign_file_cmd, &output)) {
         cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -216,11 +216,9 @@
     }
 
     // sign main binary
-    std::ostringstream temp_sign_binary_cmd;
-    temp_sign_binary_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
-    temp_sign_binary_cmd << " " << sign_parameter << " -s \""
-                         << cpack_apple_cert_app;
-    temp_sign_binary_cmd << "\" \"" << bundle_path << "\"";
+    auto temp_sign_binary_cmd =
+      cmStrCat(this->GetOption("CPACK_COMMAND_CODESIGN"), ' ', sign_parameter,
+               " -s \"", cpack_apple_cert_app, "\" \"", bundle_path, '"');
 
     if (!this->RunCommand(temp_sign_binary_cmd, &output)) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -232,15 +230,15 @@
     }
 
     // sign app bundle
-    std::ostringstream temp_codesign_cmd;
-    temp_codesign_cmd << this->GetOption("CPACK_COMMAND_CODESIGN");
-    temp_codesign_cmd << " " << sign_parameter << " -s \""
-                      << cpack_apple_cert_app << "\"";
+    auto temp_codesign_cmd =
+      cmStrCat(this->GetOption("CPACK_COMMAND_CODESIGN"), ' ', sign_parameter,
+               " -s \"", cpack_apple_cert_app, "\"");
     if (this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS")) {
-      temp_codesign_cmd << " --entitlements ";
-      temp_codesign_cmd << this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS");
+      temp_codesign_cmd +=
+        cmStrCat(" --entitlements ",
+                 this->GetOption("CPACK_BUNDLE_APPLE_ENTITLEMENTS"));
     }
-    temp_codesign_cmd << " \"" << bundle_path << "\"";
+    temp_codesign_cmd += cmStrCat(" \"", bundle_path, '"');
 
     if (!this->RunCommand(temp_codesign_cmd, &output)) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
diff --git a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
index fabf4c5..fcb79a2 100644
--- a/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
+++ b/Source/CPack/cmCPackCygwinBinaryGenerator.cxx
@@ -44,9 +44,9 @@
     cmGeneratedFileStream ofs(manifestFile);
     for (std::string const& file : files) {
       // remove the temp dir and replace with /usr
-      ofs << file.substr(tempdir.size()) << "\n";
+      ofs << file.substr(tempdir.size()) << '\n';
     }
-    ofs << manifest << "\n";
+    ofs << manifest << '\n';
   }
   // add the manifest file to the list of all files
   files.push_back(manifestFile);
@@ -60,7 +60,7 @@
   this->OutputExtension = "-";
   cmValue patchNumber = this->GetOption("CPACK_CYGWIN_PATCH_NUMBER");
   if (!patchNumber) {
-    this->OutputExtension += "1";
+    this->OutputExtension += '1';
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_CYGWIN_PATCH_NUMBER not specified using 1"
                     << std::endl);
diff --git a/Source/CPack/cmCPackCygwinSourceGenerator.cxx b/Source/CPack/cmCPackCygwinSourceGenerator.cxx
index a5863ff..f025a6c 100644
--- a/Source/CPack/cmCPackCygwinSourceGenerator.cxx
+++ b/Source/CPack/cmCPackCygwinSourceGenerator.cxx
@@ -98,7 +98,7 @@
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_CYGWIN_PATCH_NUMBER"
                     << " not specified, defaulting to 1\n");
-    outerTarFile += "1";
+    outerTarFile += '1';
   } else {
     outerTarFile += patch;
   }
@@ -150,7 +150,7 @@
     cmCPackLogger(cmCPackLog::LOG_WARNING,
                   "CPACK_CYGWIN_PATCH_NUMBER"
                     << " not specified, defaulting to 1\n");
-    this->OutputExtension += "1";
+    this->OutputExtension += '1';
   } else {
     this->OutputExtension += patch;
   }
diff --git a/Source/CPack/cmCPackDebGenerator.cxx b/Source/CPack/cmCPackDebGenerator.cxx
index 34c56c9..8d16428 100644
--- a/Source/CPack/cmCPackDebGenerator.cxx
+++ b/Source/CPack/cmCPackDebGenerator.cxx
@@ -290,8 +290,8 @@
       continue;
     }
 
-    std::string output =
-      cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5);
+    cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+    std::string output = hasher.HashFile(file);
     if (output.empty()) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
                     "Problem computing the md5 of " << file << std::endl);
diff --git a/Source/CPack/cmCPackDragNDropGenerator.cxx b/Source/CPack/cmCPackDragNDropGenerator.cxx
index 768bfbe..b4cada3 100644
--- a/Source/CPack/cmCPackDragNDropGenerator.cxx
+++ b/Source/CPack/cmCPackDragNDropGenerator.cxx
@@ -7,6 +7,9 @@
 #include <iomanip>
 #include <map>
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include <CoreFoundation/CoreFoundation.h>
 #include <cm3p/kwiml/abi.h>
 
@@ -136,8 +139,10 @@
       return 0;
     }
     for (auto const& language : languages) {
-      std::string license = slaDirectory + "/" + language + ".license.txt";
-      std::string license_rtf = slaDirectory + "/" + language + ".license.rtf";
+      std::string license =
+        cmStrCat(slaDirectory, '/', language, ".license.txt");
+      std::string license_rtf =
+        cmStrCat(slaDirectory, '/', language, ".license.rtf");
       if (!singleLicense) {
         if (!cmSystemTools::FileExists(license) &&
             !cmSystemTools::FileExists(license_rtf)) {
@@ -148,7 +153,7 @@
           return 0;
         }
       }
-      std::string menu = slaDirectory + "/" + language + ".menu.txt";
+      std::string menu = cmStrCat(slaDirectory, '/', language, ".menu.txt");
       if (!cmSystemTools::FileExists(menu)) {
         cmCPackLogger(cmCPackLog::LOG_ERROR,
                       "Missing menu file " << language << ".menu.txt"
@@ -192,13 +197,13 @@
   // loop to create dmg files
   packageFileNames.clear();
   for (auto const& package_file : package_files) {
-    std::string full_package_name = std::string(toplevel) + std::string("/");
-    if (package_file == "ALL_IN_ONE") {
+    std::string full_package_name = cmStrCat(toplevel, '/');
+    if (package_file == "ALL_IN_ONE"_s) {
       full_package_name += this->GetOption("CPACK_PACKAGE_FILE_NAME");
     } else {
       full_package_name += package_file;
     }
-    full_package_name += std::string(GetOutputExtension());
+    full_package_name += GetOutputExtension();
     packageFileNames.push_back(full_package_name);
 
     std::string src_dir = cmStrCat(toplevel, '/', package_file);
@@ -241,18 +246,18 @@
   return true;
 }
 
-bool cmCPackDragNDropGenerator::RunCommand(std::ostringstream& command,
+bool cmCPackDragNDropGenerator::RunCommand(std::string const& command,
                                            std::string* output)
 {
   int exit_code = 1;
 
   bool result = cmSystemTools::RunSingleCommand(
-    command.str(), output, output, &exit_code, nullptr, this->GeneratorVerbose,
+    command, output, output, &exit_code, nullptr, this->GeneratorVerbose,
     cmDuration::zero());
 
   if (!result || exit_code) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "Error executing: " << command.str() << std::endl);
+                  "Error executing: " << command << std::endl);
 
     return false;
   }
@@ -410,15 +415,21 @@
     cmStrCat(this->GetOption("CPACK_TOPLEVEL_DIRECTORY"), "/temp.dmg");
 
   std::string create_error;
-  std::ostringstream temp_image_command;
-  temp_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
-  temp_image_command << " create";
-  temp_image_command << " -ov";
-  temp_image_command << " -srcfolder \"" << staging.str() << "\"";
-  temp_image_command << " -volname \"" << cpack_dmg_volume_name << "\"";
-  temp_image_command << " -fs \"" << cpack_dmg_filesystem << "\"";
-  temp_image_command << " -format " << temp_image_format;
-  temp_image_command << " \"" << temp_image << "\"";
+  auto temp_image_command =
+    cmStrCat(this->GetOption("CPACK_COMMAND_HDIUTIL"),
+             " create"
+             " -ov"
+             " -srcfolder \"",
+             staging.str(),
+             "\""
+             " -volname \"",
+             cpack_dmg_volume_name,
+             "\""
+             " -fs \"",
+             cpack_dmg_filesystem,
+             "\""
+             " -format ",
+             temp_image_format, " \"", temp_image, '"');
 
   if (!this->RunCommand(temp_image_command, &create_error)) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -434,10 +445,10 @@
     // before we exit.
     bool had_error = false;
 
-    std::ostringstream attach_command;
-    attach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
-    attach_command << " attach";
-    attach_command << " \"" << temp_image << "\"";
+    auto attach_command = cmStrCat(this->GetOption("CPACK_COMMAND_HDIUTIL"),
+                                   " attach"
+                                   " \"",
+                                   temp_image, '"');
 
     std::string attach_output;
     if (!this->RunCommand(attach_command, &attach_output)) {
@@ -466,10 +477,10 @@
     // Optionally set the custom icon flag for the image ...
     if (!had_error && !cpack_package_icon->empty()) {
       std::string error;
-      std::ostringstream setfile_command;
-      setfile_command << this->GetOption("CPACK_COMMAND_SETFILE");
-      setfile_command << " -a C";
-      setfile_command << " \"" << temp_mount << "\"";
+      auto setfile_command = cmStrCat(this->GetOption("CPACK_COMMAND_SETFILE"),
+                                      " -a C"
+                                      " \"",
+                                      temp_mount, '"');
 
       if (!this->RunCommand(setfile_command, &error)) {
         cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -484,10 +495,12 @@
     // Optionally we can execute a custom apple script to generate
     // the .DS_Store for the volume folder ...
     if (!had_error && !cpack_dmg_ds_store_setup_script->empty()) {
-      std::ostringstream setup_script_command;
-      setup_script_command << "osascript"
-                           << " \"" << cpack_dmg_ds_store_setup_script << "\""
-                           << " \"" << temp_mount_name << "\"";
+      auto setup_script_command = cmStrCat("osascript"
+                                           " \"",
+                                           cpack_dmg_ds_store_setup_script,
+                                           "\""
+                                           " \"",
+                                           temp_mount_name, '"');
       std::string error;
       if (!this->RunCommand(setup_script_command, &error)) {
         cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -499,10 +512,10 @@
       }
     }
 
-    std::ostringstream detach_command;
-    detach_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
-    detach_command << " detach";
-    detach_command << " \"" << temp_mount << "\"";
+    auto detach_command = cmStrCat(this->GetOption("CPACK_COMMAND_HDIUTIL"),
+                                   " detach"
+                                   " \"",
+                                   temp_mount, '\"');
 
     if (!this->RunCommand(detach_command)) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -517,14 +530,15 @@
   }
 
   // Create the final compressed read-only disk image ...
-  std::ostringstream final_image_command;
-  final_image_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
-  final_image_command << " convert \"" << temp_image << "\"";
-  final_image_command << " -format ";
-  final_image_command << cpack_dmg_format;
-  final_image_command << " -imagekey";
-  final_image_command << " zlib-level=9";
-  final_image_command << " -o \"" << output_file << "\"";
+  auto final_image_command = cmStrCat(this->GetOption("CPACK_COMMAND_HDIUTIL"),
+                                      " convert \"", temp_image,
+                                      "\""
+                                      " -format ",
+                                      cpack_dmg_format,
+                                      " -imagekey"
+                                      " zlib-level=9"
+                                      " -o \"",
+                                      output_file, '"');
 
   std::string convert_error;
 
@@ -574,16 +588,18 @@
 
       header_data.push_back(0);
       header_data.push_back(languages.size());
+      // NOLINTNEXTLINE(modernize-loop-convert): `HAVE_CoreServices` needs `i`
       for (cmList::size_type i = 0; i < languages.size(); ++i) {
+        auto const& language = languages[i];
         CFStringRef language_cfstring = CFStringCreateWithCString(
-          nullptr, languages[i].c_str(), kCFStringEncodingUTF8);
+          nullptr, language.c_str(), kCFStringEncodingUTF8);
         CFStringRef iso_language =
           CFLocaleCreateCanonicalLanguageIdentifierFromString(
             nullptr, language_cfstring);
         if (!iso_language) {
           cmCPackLogger(cmCPackLog::LOG_ERROR,
-                        languages[i] << " is not a recognized language"
-                                     << std::endl);
+                        language << " is not a recognized language"
+                                 << std::endl);
         }
         char iso_language_cstr[65];
         CFStringGetCString(iso_language, iso_language_cstr,
@@ -663,13 +679,15 @@
     this->WriteRezXML(sla_xml, rez);
 
     // Create the final compressed read-only disk image ...
-    std::ostringstream embed_sla_command;
-    embed_sla_command << this->GetOption("CPACK_COMMAND_HDIUTIL");
-    embed_sla_command << " udifrez";
-    embed_sla_command << " -xml";
-    embed_sla_command << " \"" << sla_xml << "\"";
-    embed_sla_command << " FIXME_WHY_IS_THIS_ARGUMENT_NEEDED";
-    embed_sla_command << " \"" << output_file << "\"";
+    auto embed_sla_command = cmStrCat(this->GetOption("CPACK_COMMAND_HDIUTIL"),
+                                      " udifrez"
+                                      " -xml"
+                                      " \"",
+                                      sla_xml,
+                                      "\""
+                                      " FIXME_WHY_IS_THIS_ARGUMENT_NEEDED"
+                                      " \"",
+                                      output_file, '"');
     std::string embed_error;
     if (!this->RunCommand(embed_sla_command, &embed_error)) {
       cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -709,8 +727,8 @@
   if (this->componentPackageMethod == ONE_PACKAGE_PER_GROUP) {
     // We have to find the name of the COMPONENT GROUP
     // the current COMPONENT belongs to.
-    std::string groupVar =
-      "CPACK_COMPONENT_" + cmSystemTools::UpperCase(componentName) + "_GROUP";
+    std::string groupVar = cmStrCat(
+      "CPACK_COMPONENT_", cmSystemTools::UpperCase(componentName), "_GROUP");
     cmValue _groupName = this->GetOption(groupVar);
     if (_groupName) {
       std::string groupName = _groupName;
@@ -721,8 +739,8 @@
     }
   }
 
-  std::string componentFileName =
-    "CPACK_DMG_" + cmSystemTools::UpperCase(componentName) + "_FILE_NAME";
+  std::string componentFileName = cmStrCat(
+    "CPACK_DMG_", cmSystemTools::UpperCase(componentName), "_FILE_NAME");
   if (this->IsSet(componentFileName)) {
     return this->GetOption(componentFileName);
   }
@@ -806,12 +824,12 @@
     actual_license = licenseFile;
   } else {
     std::string license_wo_ext =
-      slaDirectory + "/" + licenseLanguage + ".license";
-    if (cmSystemTools::FileExists(license_wo_ext + ".txt")) {
-      actual_license = license_wo_ext + ".txt";
+      cmStrCat(slaDirectory, '/', licenseLanguage, ".license");
+    if (cmSystemTools::FileExists(cmStrCat(license_wo_ext, ".txt"))) {
+      actual_license = cmStrCat(license_wo_ext, ".txt");
     } else {
       licenseArray = &rez.RTF;
-      actual_license = license_wo_ext + ".rtf";
+      actual_license = cmStrCat(license_wo_ext, ".rtf");
     }
   }
 
@@ -834,7 +852,7 @@
     } else {
       std::vector<std::string> lines;
       std::string actual_menu =
-        slaDirectory + "/" + licenseLanguage + ".menu.txt";
+        cmStrCat(slaDirectory, '/', licenseLanguage, ".menu.txt");
       if (!this->ReadFile(actual_menu, lines, error)) {
         return false;
       }
diff --git a/Source/CPack/cmCPackDragNDropGenerator.h b/Source/CPack/cmCPackDragNDropGenerator.h
index 6d1267b..6089ae5 100644
--- a/Source/CPack/cmCPackDragNDropGenerator.h
+++ b/Source/CPack/cmCPackDragNDropGenerator.h
@@ -33,7 +33,7 @@
 
   bool CopyFile(std::ostringstream& source, std::ostringstream& target);
   bool CreateEmptyFile(std::ostringstream& target, size_t size);
-  bool RunCommand(std::ostringstream& command, std::string* output = nullptr);
+  bool RunCommand(std::string const& command, std::string* output = nullptr);
 
   std::string GetComponentInstallDirNameSuffix(
     const std::string& componentName) override;
diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx
index afd85cd..11d90c0 100644
--- a/Source/CPack/cmCPackGenerator.cxx
+++ b/Source/CPack/cmCPackGenerator.cxx
@@ -1119,8 +1119,8 @@
   // Run post-build actions
   cmValue postBuildScripts = this->GetOption("CPACK_POST_BUILD_SCRIPTS");
   if (postBuildScripts) {
-    this->MakefileMap->AddDefinition("CPACK_PACKAGE_FILES",
-                                     cmJoin(this->packageFileNames, ";"));
+    this->MakefileMap->AddDefinition(
+      "CPACK_PACKAGE_FILES", cmList::to_string(this->packageFileNames));
 
     const cmList scripts{ postBuildScripts };
     for (const auto& script : scripts) {
diff --git a/Source/CPack/cmCPackInnoSetupGenerator.cxx b/Source/CPack/cmCPackInnoSetupGenerator.cxx
index ada9a5b..b8bf070 100644
--- a/Source/CPack/cmCPackInnoSetupGenerator.cxx
+++ b/Source/CPack/cmCPackInnoSetupGenerator.cxx
@@ -106,7 +106,7 @@
     const cmList extraScripts(GetOption("CPACK_INNOSETUP_EXTRA_SCRIPTS"));
 
     for (const std::string& i : extraScripts) {
-      includeDirectives.push_back(cmStrCat(
+      includeDirectives.emplace_back(cmStrCat(
         "#include ", QuotePath(cmSystemTools::CollapseFullPath(i, toplevel))));
     }
   }
@@ -142,7 +142,7 @@
     const cmList codeFiles(GetOption("CPACK_INNOSETUP_CODE_FILES"));
 
     for (const std::string& i : codeFiles) {
-      codeIncludes.push_back(cmStrCat(
+      codeIncludes.emplace_back(cmStrCat(
         "#include ", QuotePath(cmSystemTools::CollapseFullPath(i, toplevel))));
     }
   }
@@ -781,7 +781,7 @@
   // Create internal variables
   std::vector<std::string> setupSection;
   for (const auto& i : setupDirectives) {
-    setupSection.push_back(cmStrCat(i.first, '=', TranslateBool(i.second)));
+    setupSection.emplace_back(cmStrCat(i.first, '=', TranslateBool(i.second)));
   }
 
   // Also create comments if the sections are empty
@@ -1082,7 +1082,7 @@
   std::vector<std::string> keys;
   for (const char* i : availableKeys) {
     if (params.count(i)) {
-      keys.push_back(cmStrCat(i, ": ", params.at(i)));
+      keys.emplace_back(cmStrCat(i, ": ", params.at(i)));
     }
   }
 
diff --git a/Source/CPack/cmCPackNuGetGenerator.cxx b/Source/CPack/cmCPackNuGetGenerator.cxx
index 5de8179..aa99fb6 100644
--- a/Source/CPack/cmCPackNuGetGenerator.cxx
+++ b/Source/CPack/cmCPackNuGetGenerator.cxx
@@ -12,7 +12,7 @@
 
 #include "cmCPackComponentGroup.h"
 #include "cmCPackLog.h"
-#include "cmStringAlgorithms.h"
+#include "cmList.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
 
@@ -82,10 +82,10 @@
                      std::back_inserter(components),
                      [](cmCPackComponent const* comp) { return comp->Name; });
       this->SetOption("CPACK_NUGET_" + compGUp + "_GROUP_COMPONENTS",
-                      cmJoin(components, ";"));
+                      cmList::to_string(components));
     }
     if (!groups.empty()) {
-      this->SetOption("CPACK_NUGET_GROUPS", cmJoin(groups, ";"));
+      this->SetOption("CPACK_NUGET_GROUPS", cmList::to_string(groups));
     }
 
     // Handle Orphan components (components not belonging to any groups)
@@ -103,7 +103,7 @@
       }
     }
     if (!components.empty()) {
-      this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";"));
+      this->SetOption("CPACK_NUGET_COMPONENTS", cmList::to_string(components));
     }
 
   } else {
@@ -114,7 +114,7 @@
                    [](std::pair<std::string, cmCPackComponent> const& comp) {
                      return comp.first;
                    });
-    this->SetOption("CPACK_NUGET_COMPONENTS", cmJoin(components, ";"));
+    this->SetOption("CPACK_NUGET_COMPONENTS", cmList::to_string(components));
   }
 }
 
diff --git a/Source/CPack/cmCPackPKGGenerator.cxx b/Source/CPack/cmCPackPKGGenerator.cxx
index 76ef091..4d60c6c 100644
--- a/Source/CPack/cmCPackPKGGenerator.cxx
+++ b/Source/CPack/cmCPackPKGGenerator.cxx
@@ -4,6 +4,8 @@
 
 #include <vector>
 
+#include <cmext/string_view>
+
 #include "cmCPackComponentGroup.h"
 #include "cmCPackGenerator.h"
 #include "cmCPackLog.h"
@@ -38,13 +40,12 @@
   if (component.ArchiveFile.empty()) {
     std::string packagesDir =
       cmStrCat(this->GetOption("CPACK_TEMPORARY_DIRECTORY"), ".dummy");
-    std::ostringstream out;
-    out << cmSystemTools::GetFilenameWithoutLastExtension(packagesDir) << "-"
-        << component.Name << ".pkg";
-    return out.str();
+    return cmStrCat(
+      cmSystemTools::GetFilenameWithoutLastExtension(packagesDir), '-',
+      component.Name, ".pkg");
   }
 
-  return component.ArchiveFile + ".pkg";
+  return cmStrCat(component.ArchiveFile, ".pkg");
 }
 
 void cmCPackPKGGenerator::CreateBackground(const char* themeName,
@@ -160,14 +161,15 @@
   for (auto const& comp : this->Components) {
     if (!comp.second.Group) {
       xChoiceOut.StartElement("line");
-      xChoiceOut.Attribute("choice", comp.first + "Choice");
+      xChoiceOut.Attribute("choice", cmStrCat(comp.first, "Choice"));
       xChoiceOut.Content(""); // Avoid self-closing tag.
       xChoiceOut.EndElement();
     }
   }
   if (!this->PostFlightComponent.Name.empty()) {
     xChoiceOut.StartElement("line");
-    xChoiceOut.Attribute("choice", PostFlightComponent.Name + "Choice");
+    xChoiceOut.Attribute("choice",
+                         cmStrCat(PostFlightComponent.Name, "Choice"));
     xChoiceOut.Content(""); // Avoid self-closing tag.
     xChoiceOut.EndElement();
   }
@@ -207,14 +209,14 @@
   const cmCPackComponentGroup& group, cmXMLWriter& xout)
 {
   xout.StartElement("line");
-  xout.Attribute("choice", group.Name + "Choice");
+  xout.Attribute("choice", cmStrCat(group.Name, "Choice"));
   for (cmCPackComponentGroup* subgroup : group.Subgroups) {
     CreateChoiceOutline(*subgroup, xout);
   }
 
   for (cmCPackComponent* comp : group.Components) {
     xout.StartElement("line");
-    xout.Attribute("choice", comp->Name + "Choice");
+    xout.Attribute("choice", cmStrCat(comp->Name, "Choice"));
     xout.Content(""); // Avoid self-closing tag.
     xout.EndElement();
   }
@@ -225,7 +227,7 @@
                                        cmXMLWriter& xout)
 {
   xout.StartElement("choice");
-  xout.Attribute("id", group.Name + "Choice");
+  xout.Attribute("id", cmStrCat(group.Name, "Choice"));
   xout.Attribute("title", group.DisplayName);
   xout.Attribute("start_selected", "true");
   xout.Attribute("start_enabled", "true");
@@ -249,7 +251,7 @@
   }
 
   xout.StartElement("choice");
-  xout.Attribute("id", component.Name + "Choice");
+  xout.Attribute("id", cmStrCat(component.Name, "Choice"));
   xout.Attribute("title", component.DisplayName);
   xout.Attribute(
     "start_selected",
@@ -381,15 +383,14 @@
                                                  const std::string& dirName)
 {
   std::string uname = cmSystemTools::UpperCase(name);
-  std::string cpackVar = "CPACK_RESOURCE_FILE_" + uname;
+  std::string cpackVar = cmStrCat("CPACK_RESOURCE_FILE_", uname);
   cmValue inFileName = this->GetOption(cpackVar);
   if (!inFileName) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
-                  "CPack option: " << cpackVar.c_str()
-                                   << " not specified. It should point to "
-                                   << (!name.empty() ? name : "<empty>")
-                                   << ".rtf, " << name << ".html, or " << name
-                                   << ".txt file" << std::endl);
+                  "CPack option: "
+                    << cpackVar << " not specified. It should point to "
+                    << (!name.empty() ? name : "<empty>") << ".rtf, " << name
+                    << ".html, or " << name << ".txt file" << std::endl);
     return false;
   }
   if (!cmSystemTools::FileExists(inFileName)) {
@@ -400,7 +401,8 @@
     return false;
   }
   std::string ext = cmSystemTools::GetFilenameLastExtension(inFileName);
-  if (ext != ".rtfd" && ext != ".rtf" && ext != ".html" && ext != ".txt") {
+  if (ext != ".rtfd"_s && ext != ".rtf"_s && ext != ".html"_s &&
+      ext != ".txt"_s) {
     cmCPackLogger(
       cmCPackLog::LOG_ERROR,
       "Bad file extension specified: "
@@ -414,7 +416,8 @@
 
   // Set this so that distribution.dist gets the right name (without
   // the path).
-  this->SetOption("CPACK_RESOURCE_FILE_" + uname + "_NOPATH", (name + ext));
+  this->SetOption(cmStrCat("CPACK_RESOURCE_FILE_", uname, "_NOPATH"),
+                  cmStrCat(name, ext));
 
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "Configure file: " << (inFileName ? *inFileName : "(NULL)")
@@ -454,7 +457,7 @@
 {
   std::string dst = cmStrCat(resdir, '/', name);
   cmSystemTools::CopyFileAlways(script, dst);
-  cmSystemTools::SetPermissions(dst.c_str(), 0777);
+  cmSystemTools::SetPermissions(dst, 0777);
   cmCPackLogger(cmCPackLog::LOG_VERBOSE,
                 "copy script : " << script << "\ninto " << dst << std::endl);
 
diff --git a/Source/CPack/cmCPackProductBuildGenerator.cxx b/Source/CPack/cmCPackProductBuildGenerator.cxx
index 4ad616d..ae3c50e 100644
--- a/Source/CPack/cmCPackProductBuildGenerator.cxx
+++ b/Source/CPack/cmCPackProductBuildGenerator.cxx
@@ -58,7 +58,7 @@
     }
   }
 
-  std::string resDir = packageDirFileName + "/Contents";
+  std::string resDir = cmStrCat(packageDirFileName, "/Contents");
 
   if (this->IsSet("CPACK_PRODUCTBUILD_RESOURCES_DIR")) {
     std::string userResDir =
@@ -102,15 +102,24 @@
 
   pkgCmd << productbuild << " --distribution \"" << packageDirFileName
          << "/Contents/distribution.dist\""
-         << " --package-path \"" << packageDirFileName << "/Contents/Packages"
+            " --package-path \""
+         << packageDirFileName
+         << "/Contents/Packages"
+            "\""
+            " --resources \""
+         << resDir
          << "\""
-         << " --resources \"" << resDir << "\""
-         << " --version \"" << version << "\""
-         << (identifier.empty() ? "" : " --identifier \"" + identifier + "\"")
-         << (identityName.empty() ? "" : " --sign \"" + identityName + "\"")
-         << (keychainPath.empty() ? ""
-                                  : " --keychain \"" + keychainPath + "\"")
-         << " \"" << packageFileNames[0] << "\"";
+            " --version \""
+         << version << "\""
+         << (identifier.empty()
+               ? std::string{}
+               : cmStrCat(" --identifier \"", identifier, '"'))
+         << (identityName.empty() ? std::string{}
+                                  : cmStrCat(" --sign \"", identityName, '"'))
+         << (keychainPath.empty()
+               ? std::string{}
+               : cmStrCat(" --keychain \"", keychainPath, '"'))
+         << " \"" << packageFileNames[0] << '"';
 
   // Run ProductBuild
   return RunProductBuild(pkgCmd.str());
@@ -184,10 +193,10 @@
 
   std::string resDir = packageFileDir;
   if (component) {
-    resDir += "/";
+    resDir += '/';
     resDir += component->Name;
   }
-  std::string scriptDir = resDir + "/scripts";
+  std::string scriptDir = cmStrCat(resDir, "/scripts");
 
   if (!cmsys::SystemTools::MakeDirectory(scriptDir.c_str())) {
     cmCPackLogger(cmCPackLog::LOG_ERROR,
@@ -232,15 +241,24 @@
     keychainPath = p;
   }
 
-  pkgCmd << pkgbuild << " --root \"" << packageDir << "\""
-         << " --identifier \"" << pkgId << "\""
-         << " --scripts \"" << scriptDir << "\""
-         << " --version \"" << version << "\""
-         << " --install-location \"/\""
-         << (identityName.empty() ? "" : " --sign \"" + identityName + "\"")
-         << (keychainPath.empty() ? ""
-                                  : " --keychain \"" + keychainPath + "\"")
-         << " \"" << packageFile << "\"";
+  pkgCmd << pkgbuild << " --root \"" << packageDir
+         << "\""
+            " --identifier \""
+         << pkgId
+         << "\""
+            " --scripts \""
+         << scriptDir
+         << "\""
+            " --version \""
+         << version
+         << "\""
+            " --install-location \"/\""
+         << (identityName.empty() ? std::string{}
+                                  : cmStrCat(" --sign \"", identityName, "\""))
+         << (keychainPath.empty()
+               ? std::string{}
+               : cmStrCat(" --keychain \"", keychainPath, "\""))
+         << " \"" << packageFile << '"';
 
   if (component && !component->Plist.empty()) {
     pkgCmd << " --component-plist \"" << component->Plist << "\"";
@@ -253,10 +271,10 @@
 cmValue cmCPackProductBuildGenerator::GetComponentScript(
   const char* script, const char* component_name)
 {
-  std::string scriptname = std::string("CPACK_") + script + "_";
+  std::string scriptname = cmStrCat("CPACK_", script, '_');
   if (component_name) {
     scriptname += cmSystemTools::UpperCase(component_name);
-    scriptname += "_";
+    scriptname += '_';
   }
   scriptname += "SCRIPT";
 
diff --git a/Source/CPack/cmCPackSTGZGenerator.cxx b/Source/CPack/cmCPackSTGZGenerator.cxx
index 6ad3755..1248d17 100644
--- a/Source/CPack/cmCPackSTGZGenerator.cxx
+++ b/Source/CPack/cmCPackSTGZGenerator.cxx
@@ -7,6 +7,8 @@
 #include <string>
 #include <vector>
 
+#include <fcntl.h>
+
 #include "cmsys/FStream.hxx"
 
 #include "cm_sys_stat.h"
diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx
index 90716e6..00c8fa2 100644
--- a/Source/CPack/cpack.cxx
+++ b/Source/CPack/cpack.cxx
@@ -363,11 +363,11 @@
     }
 
     if (!expandedPreset->Generators.empty() && generator.empty()) {
-      generator = cmJoin(expandedPreset->Generators, ";");
+      generator = cmList::to_string(expandedPreset->Generators);
     }
 
     if (!expandedPreset->Configurations.empty() && cpackBuildConfig.empty()) {
-      cpackBuildConfig = cmJoin(expandedPreset->Configurations, ";");
+      cpackBuildConfig = cmList::to_string(expandedPreset->Configurations);
     }
 
     definitions.insert(expandedPreset->Variables.begin(),
diff --git a/Source/CTest/cmCTestBZR.cxx b/Source/CTest/cmCTestBZR.cxx
index 81a866a..36df344 100644
--- a/Source/CTest/cmCTestBZR.cxx
+++ b/Source/CTest/cmCTestBZR.cxx
@@ -16,7 +16,6 @@
 
 #include "cmCTest.h"
 #include "cmCTestVC.h"
-#include "cmProcessTools.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 
@@ -136,14 +135,14 @@
 std::string cmCTestBZR::LoadInfo()
 {
   // Run "bzr info" to get the repository info from the work tree.
-  const char* bzr = this->CommandLineTool.c_str();
-  const char* bzr_info[] = { bzr, "info", nullptr };
+  std::string bzr = this->CommandLineTool;
+  std::vector<std::string> bzr_info = { bzr, "info" };
   InfoParser iout(this, "info-out> ");
   OutputLogger ierr(this->Log, "info-err> ");
   this->RunChild(bzr_info, &iout, &ierr);
 
   // Run "bzr revno" to get the repository revision number from the work tree.
-  const char* bzr_revno[] = { bzr, "revno", nullptr };
+  std::vector<std::string> bzr_revno = { bzr, "revno" };
   std::string rev;
   RevnoParser rout(this, "revno-out> ", rev);
   OutputLogger rerr(this->Log, "revno-err> ");
@@ -373,22 +372,18 @@
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
   // Use "bzr pull" to update the working tree.
-  std::vector<char const*> bzr_update;
-  bzr_update.push_back(this->CommandLineTool.c_str());
+  std::vector<std::string> bzr_update;
+  bzr_update.push_back(this->CommandLineTool);
   bzr_update.push_back("pull");
 
-  for (std::string const& arg : args) {
-    bzr_update.push_back(arg.c_str());
-  }
+  cm::append(bzr_update, args);
 
-  bzr_update.push_back(this->URL.c_str());
-
-  bzr_update.push_back(nullptr);
+  bzr_update.push_back(this->URL);
 
   // For some reason bzr uses stderr to display the update status.
   OutputLogger out(this->Log, "pull-out> ");
   UpdateParser err(this, "pull-err> ");
-  return this->RunUpdateCommand(bzr_update.data(), &out, &err);
+  return this->RunUpdateCommand(bzr_update, &out, &err);
 }
 
 bool cmCTestBZR::LoadRevisions()
@@ -409,10 +404,9 @@
   }
 
   // Run "bzr log" to get all global revisions of interest.
-  const char* bzr = this->CommandLineTool.c_str();
-  const char* bzr_log[] = {
-    bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr
-  };
+  std::string bzr = this->CommandLineTool;
+  std::vector<std::string> bzr_log = { bzr,  "log",   "-v",     "-r",
+                                       revs, "--xml", this->URL };
   {
     LogParser out(this, "log-out> ");
     OutputLogger err(this->Log, "log-err> ");
@@ -468,8 +462,8 @@
 bool cmCTestBZR::LoadModifications()
 {
   // Run "bzr status" which reports local modifications.
-  const char* bzr = this->CommandLineTool.c_str();
-  const char* bzr_status[] = { bzr, "status", "-SV", nullptr };
+  std::string bzr = this->CommandLineTool;
+  std::vector<std::string> bzr_status = { bzr, "status", "-SV" };
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(bzr_status, &out, &err);
diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx
index 5feb953..bb6ccc3 100644
--- a/Source/CTest/cmCTestBuildAndTestHandler.cxx
+++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx
@@ -7,8 +7,6 @@
 #include <cstring>
 #include <ratio>
 
-#include "cmsys/Process.h"
-
 #include "cmBuildOptions.h"
 #include "cmCTest.h"
 #include "cmCTestTestHandler.h"
@@ -308,12 +306,11 @@
     return 1;
   }
 
-  std::vector<const char*> testCommand;
-  testCommand.push_back(fullPath.c_str());
+  std::vector<std::string> testCommand;
+  testCommand.push_back(fullPath);
   for (std::string const& testCommandArg : this->TestCommandArgs) {
-    testCommand.push_back(testCommandArg.c_str());
+    testCommand.push_back(testCommandArg);
   }
-  testCommand.push_back(nullptr);
   std::string outs;
   int retval = 0;
   // run the test from the this->BuildRunDir if set
@@ -349,10 +346,10 @@
     }
   }
 
-  int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
-                                        remainingTime, nullptr);
+  bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
+                                         remainingTime, nullptr);
 
-  if (runTestRes != cmsysProcess_State_Exited || retval != 0) {
+  if (!runTestRes || retval != 0) {
     out << "Test command failed: " << testCommand[0] << "\n";
     retval = 1;
   }
diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx
index 882b579..859798e 100644
--- a/Source/CTest/cmCTestBuildHandler.cxx
+++ b/Source/CTest/cmCTestBuildHandler.cxx
@@ -3,14 +3,17 @@
 #include "cmCTestBuildHandler.h"
 
 #include <cstdlib>
+#include <memory>
+#include <ratio>
 #include <set>
 #include <utility>
 
 #include <cmext/algorithm>
 
+#include <cm3p/uv.h>
+
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestLaunchReporter.h"
@@ -23,6 +26,9 @@
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
@@ -419,7 +425,7 @@
   cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
   this->ColorRemover = &colorRemover;
   int retVal = 0;
-  int res = cmsysProcess_State_Exited;
+  bool res = true;
   if (!this->CTest->GetShowOnly()) {
     res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
                                ofs);
@@ -474,7 +480,7 @@
   }
   this->GenerateXMLFooter(xml, elapsed_build_time);
 
-  if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) {
+  if (!res || retVal || this->TotalErrors > 0) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Error(s) when building project" << std::endl);
   }
@@ -763,10 +769,10 @@
   }
 }
 
-int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
-                                        int* retVal, const char* dir,
-                                        int timeout, std::ostream& ofs,
-                                        Encoding encoding)
+bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
+                                         int* retVal, const char* dir,
+                                         int timeout, std::ostream& ofs,
+                                         Encoding encoding)
 {
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -775,19 +781,9 @@
     return false;
   }
 
-  std::vector<const char*> argv;
-  argv.reserve(args.size() + 1);
-  for (std::string const& arg : args) {
-    argv.push_back(arg.c_str());
-  }
-  argv.push_back(nullptr);
-
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Run command:", this->Quiet);
-  for (char const* arg : argv) {
-    if (!arg) {
-      break;
-    }
+  for (auto const& arg : args) {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        " \"" << arg << "\"", this->Quiet);
   }
@@ -799,21 +795,20 @@
   static_cast<void>(launchHelper);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  cmsysProcess_SetTimeout(cp, timeout);
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(args)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
+  }
+  auto chain = builder.Start();
 
   // Initialize tick's
   std::string::size_type tick = 0;
-  const std::string::size_type tick_len = 1024;
+  static constexpr std::string::size_type tick_len = 1024;
 
-  char* data;
-  int length;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
   cmCTestOptionalLog(
     this->CTest, HANDLER_PROGRESS_OUTPUT,
     "   Each symbol represents "
@@ -835,39 +830,65 @@
   this->WarningQuotaReached = false;
   this->ErrorQuotaReached = false;
 
+  cm::uv_timer_ptr timer;
+  bool timedOut = false;
+  timer.init(chain.GetLoop(), &timedOut);
+  if (timeout > 0) {
+    timer.start(
+      [](uv_timer_t* t) {
+        auto* timedOutPtr = static_cast<bool*>(t->data);
+        *timedOutPtr = true;
+      },
+      timeout * 1000, 0);
+  }
+
   // For every chunk of data
-  int res;
-  while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
-    // Replace '\0' with '\n', since '\0' does not really make sense. This is
-    // for Visual Studio output
-    for (int cc = 0; cc < length; ++cc) {
-      if (data[cc] == 0) {
-        data[cc] = '\n';
-      }
-    }
+  cm::uv_pipe_ptr outputStream;
+  bool outFinished = false;
+  cm::uv_pipe_ptr errorStream;
+  bool errFinished = false;
+  auto startRead = [this, &chain, &processOutput, &tick,
+                    &ofs](cm::uv_pipe_ptr& pipe, int stream,
+                          t_BuildProcessingQueueType& queue, bool& finished,
+                          int id) -> std::unique_ptr<cmUVStreamReadHandle> {
+    pipe.init(chain.GetLoop(), 0);
+    uv_pipe_open(pipe, stream);
+    return cmUVStreamRead(
+      pipe,
+      [this, &processOutput, &queue, id, &tick, &ofs](std::vector<char> data) {
+        // Replace '\0' with '\n', since '\0' does not really make sense. This
+        // is for Visual Studio output
+        for (auto& c : data) {
+          if (c == 0) {
+            c = '\n';
+          }
+        }
 
-    // Process the chunk of data
-    if (res == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 1);
-      this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                          &this->BuildProcessingErrorQueue);
-    } else {
-      processOutput.DecodeText(data, length, strdata, 2);
-      this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                          &this->BuildProcessingQueue);
-    }
-  }
-  processOutput.DecodeText(std::string(), strdata, 1);
-  if (!strdata.empty()) {
-    this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                        &this->BuildProcessingErrorQueue);
-  }
-  processOutput.DecodeText(std::string(), strdata, 2);
-  if (!strdata.empty()) {
-    this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
-                        &this->BuildProcessingQueue);
-  }
+        // Process the chunk of data
+        std::string strdata;
+        processOutput.DecodeText(data.data(), data.size(), strdata, id);
+        this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
+                            ofs, &queue);
+      },
+      [this, &processOutput, &queue, id, &tick, &ofs, &finished]() {
+        std::string strdata;
+        processOutput.DecodeText(std::string(), strdata, id);
+        if (!strdata.empty()) {
+          this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
+                              ofs, &queue);
+        }
+        finished = true;
+      });
+  };
+  auto outputHandle = startRead(outputStream, chain.OutputStream(),
+                                this->BuildProcessingQueue, outFinished, 1);
+  auto errorHandle =
+    startRead(errorStream, chain.ErrorStream(),
+              this->BuildProcessingErrorQueue, errFinished, 2);
 
+  while (!timedOut && !(outFinished && errFinished && chain.Finished())) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
                       &this->BuildProcessingQueue);
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
@@ -878,90 +899,93 @@
                        << std::endl,
                      this->Quiet);
 
-  // Properly handle output of the build command
-  cmsysProcess_WaitForExit(cp, nullptr);
-  int result = cmsysProcess_GetState(cp);
-
-  if (result == cmsysProcess_State_Exited) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitValue(cp);
-      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-                         "Command exited with the value: " << *retVal
-                                                           << std::endl,
-                         this->Quiet);
-      // if a non zero return value
-      if (*retVal) {
-        // If there was an error running command, report that on the
-        // dashboard.
-        if (this->UseCTestLaunch) {
-          // For launchers, do not record this top-level error if other
-          // more granular build errors have already been captured.
-          bool launcherXMLFound = false;
-          cmsys::Directory launchDir;
-          launchDir.Load(this->CTestLaunchDir);
-          unsigned long n = launchDir.GetNumberOfFiles();
-          for (unsigned long i = 0; i < n; ++i) {
-            const char* fname = launchDir.GetFile(i);
-            if (cmHasLiteralSuffix(fname, ".xml")) {
-              launcherXMLFound = true;
-              break;
+  if (chain.Finished()) {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (retVal) {
+          *retVal = static_cast<int>(status.ExitStatus);
+          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                             "Command exited with the value: " << *retVal
+                                                               << std::endl,
+                             this->Quiet);
+          // if a non zero return value
+          if (*retVal) {
+            // If there was an error running command, report that on the
+            // dashboard.
+            if (this->UseCTestLaunch) {
+              // For launchers, do not record this top-level error if other
+              // more granular build errors have already been captured.
+              bool launcherXMLFound = false;
+              cmsys::Directory launchDir;
+              launchDir.Load(this->CTestLaunchDir);
+              unsigned long n = launchDir.GetNumberOfFiles();
+              for (unsigned long i = 0; i < n; ++i) {
+                const char* fname = launchDir.GetFile(i);
+                if (cmHasLiteralSuffix(fname, ".xml")) {
+                  launcherXMLFound = true;
+                  break;
+                }
+              }
+              if (!launcherXMLFound) {
+                cmCTestLaunchReporter reporter;
+                reporter.RealArgs = args;
+                reporter.ComputeFileNames();
+                reporter.ExitCode = *retVal;
+                reporter.Status = status;
+                // Use temporary BuildLog file to populate this error for
+                // CDash.
+                ofs.flush();
+                reporter.LogOut = this->LogFileNames["Build"];
+                reporter.LogOut += ".tmp";
+                reporter.WriteXML();
+              }
+            } else {
+              cmCTestBuildErrorWarning errorwarning;
+              errorwarning.LineNumber = 0;
+              errorwarning.LogLine = 1;
+              errorwarning.Text = cmStrCat(
+                "*** WARNING non-zero return value in ctest from: ", args[0]);
+              errorwarning.PreContext.clear();
+              errorwarning.PostContext.clear();
+              errorwarning.Error = false;
+              this->ErrorsAndWarnings.push_back(std::move(errorwarning));
+              this->TotalWarnings++;
             }
           }
-          if (!launcherXMLFound) {
-            cmCTestLaunchReporter reporter;
-            reporter.RealArgs = args;
-            reporter.ComputeFileNames();
-            reporter.ExitCode = *retVal;
-            reporter.Process = cp;
-            // Use temporary BuildLog file to populate this error for CDash.
-            ofs.flush();
-            reporter.LogOut = this->LogFileNames["Build"];
-            reporter.LogOut += ".tmp";
-            reporter.WriteXML();
-          }
-        } else {
-          cmCTestBuildErrorWarning errorwarning;
-          errorwarning.LineNumber = 0;
-          errorwarning.LogLine = 1;
-          errorwarning.Text = cmStrCat(
-            "*** WARNING non-zero return value in ctest from: ", argv[0]);
-          errorwarning.PreContext.clear();
-          errorwarning.PostContext.clear();
-          errorwarning.Error = false;
-          this->ErrorsAndWarnings.push_back(std::move(errorwarning));
-          this->TotalWarnings++;
         }
-      }
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn: {
+        // If there was an error running command, report that on the dashboard.
+        cmCTestBuildErrorWarning errorwarning;
+        errorwarning.LineNumber = 0;
+        errorwarning.LogLine = 1;
+        errorwarning.Text =
+          cmStrCat("*** ERROR executing: ", exception.second);
+        errorwarning.PreContext.clear();
+        errorwarning.PostContext.clear();
+        errorwarning.Error = true;
+        this->ErrorsAndWarnings.push_back(std::move(errorwarning));
+        this->TotalErrors++;
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "There was an error: " << exception.second << std::endl);
+      } break;
+      default:
+        if (retVal) {
+          *retVal = status.TermSignal;
+          cmCTestOptionalLog(
+            this->CTest, WARNING,
+            "There was an exception: " << *retVal << std::endl, this->Quiet);
+        }
+        break;
     }
-  } else if (result == cmsysProcess_State_Exception) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitException(cp);
-      cmCTestOptionalLog(this->CTest, WARNING,
-                         "There was an exception: " << *retVal << std::endl,
-                         this->Quiet);
-    }
-  } else if (result == cmsysProcess_State_Expired) {
+  } else {
     cmCTestOptionalLog(this->CTest, WARNING,
                        "There was a timeout" << std::endl, this->Quiet);
-  } else if (result == cmsysProcess_State_Error) {
-    // If there was an error running command, report that on the dashboard.
-    cmCTestBuildErrorWarning errorwarning;
-    errorwarning.LineNumber = 0;
-    errorwarning.LogLine = 1;
-    errorwarning.Text =
-      cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
-    errorwarning.PreContext.clear();
-    errorwarning.PostContext.clear();
-    errorwarning.Error = true;
-    this->ErrorsAndWarnings.push_back(std::move(errorwarning));
-    this->TotalErrors++;
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "There was an error: " << cmsysProcess_GetErrorString(cp)
-                                      << std::endl);
   }
 
-  cmsysProcess_Delete(cp);
-  return result;
+  return true;
 }
 
 // ######################################################################
diff --git a/Source/CTest/cmCTestBuildHandler.h b/Source/CTest/cmCTestBuildHandler.h
index e33294d..90945b1 100644
--- a/Source/CTest/cmCTestBuildHandler.h
+++ b/Source/CTest/cmCTestBuildHandler.h
@@ -53,9 +53,9 @@
 
   //! Run command specialized for make and configure. Returns process status
   // and retVal is return value or exception.
-  int RunMakeCommand(const std::string& command, int* retVal, const char* dir,
-                     int timeout, std::ostream& ofs,
-                     Encoding encoding = cmProcessOutput::Auto);
+  bool RunMakeCommand(const std::string& command, int* retVal, const char* dir,
+                      int timeout, std::ostream& ofs,
+                      Encoding encoding = cmProcessOutput::Auto);
 
   enum
   {
diff --git a/Source/CTest/cmCTestCVS.cxx b/Source/CTest/cmCTestCVS.cxx
index 87ab762..ef95b25 100644
--- a/Source/CTest/cmCTestCVS.cxx
+++ b/Source/CTest/cmCTestCVS.cxx
@@ -5,12 +5,12 @@
 #include <utility>
 
 #include <cm/string_view>
+#include <cmext/algorithm>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCTest.h"
-#include "cmProcessTools.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLWriter.h"
@@ -90,18 +90,15 @@
   }
 
   // Run "cvs update" to update the work tree.
-  std::vector<char const*> cvs_update;
-  cvs_update.push_back(this->CommandLineTool.c_str());
+  std::vector<std::string> cvs_update;
+  cvs_update.push_back(this->CommandLineTool);
   cvs_update.push_back("-z3");
   cvs_update.push_back("update");
-  for (std::string const& arg : args) {
-    cvs_update.push_back(arg.c_str());
-  }
-  cvs_update.push_back(nullptr);
+  cm::append(cvs_update, args);
 
   UpdateParser out(this, "up-out> ");
   UpdateParser err(this, "up-err> ");
-  return this->RunUpdateCommand(cvs_update.data(), &out, &err);
+  return this->RunUpdateCommand(cvs_update, &out, &err);
 }
 
 class cmCTestCVS::LogParser : public cmCTestVC::LineParser
@@ -222,10 +219,8 @@
   cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
 
   // Run "cvs log" to get revisions of this file on this branch.
-  const char* cvs = this->CommandLineTool.c_str();
-  const char* cvs_log[] = {
-    cvs, "log", "-N", branchFlag, file.c_str(), nullptr
-  };
+  std::string cvs = this->CommandLineTool;
+  std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file };
 
   LogParser out(this, "log-out> ", revisions);
   OutputLogger err(this->Log, "log-err> ");
diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx
index bae1f54..e0326a9 100644
--- a/Source/CTest/cmCTestConfigureCommand.cxx
+++ b/Source/CTest/cmCTestConfigureCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <cstring>
 #include <sstream>
+#include <vector>
 
 #include <cmext/string_view>
 
diff --git a/Source/CTest/cmCTestConfigureHandler.cxx b/Source/CTest/cmCTestConfigureHandler.cxx
index 914930e..dd8952f 100644
--- a/Source/CTest/cmCTestConfigureHandler.cxx
+++ b/Source/CTest/cmCTestConfigureHandler.cxx
@@ -45,7 +45,7 @@
   auto elapsed_time_start = std::chrono::steady_clock::now();
   std::string output;
   int retVal = 0;
-  int res = 0;
+  bool res = false;
   if (!this->CTest->GetShowOnly()) {
     cmGeneratedFileStream os;
     if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {
diff --git a/Source/CTest/cmCTestCoverageHandler.cxx b/Source/CTest/cmCTestCoverageHandler.cxx
index 5c48cbf..1aa49cf 100644
--- a/Source/CTest/cmCTestCoverageHandler.cxx
+++ b/Source/CTest/cmCTestCoverageHandler.cxx
@@ -9,16 +9,20 @@
 #include <cstring>
 #include <iomanip>
 #include <iterator>
+#include <memory>
+#include <ratio>
 #include <sstream>
+#include <type_traits>
 #include <utility>
 
 #include <cmext/algorithm>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
+#include "cm_fileno.hxx"
+
 #include "cmCTest.h"
 #include "cmDuration.h"
 #include "cmGeneratedFileStream.h"
@@ -31,6 +35,7 @@
 #include "cmParsePHPCoverage.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
 
@@ -38,85 +43,6 @@
 
 #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
 
-class cmCTestRunProcess
-{
-public:
-  cmCTestRunProcess()
-  {
-    this->Process = cmsysProcess_New();
-    this->PipeState = -1;
-    this->TimeOut = cmDuration(-1);
-  }
-  ~cmCTestRunProcess()
-  {
-    if (this->PipeState != -1 && this->PipeState != cmsysProcess_Pipe_None &&
-        this->PipeState != cmsysProcess_Pipe_Timeout) {
-      this->WaitForExit();
-    }
-    cmsysProcess_Delete(this->Process);
-  }
-  cmCTestRunProcess(const cmCTestRunProcess&) = delete;
-  cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete;
-  void SetCommand(const char* command)
-  {
-    this->CommandLineStrings.clear();
-    this->CommandLineStrings.emplace_back(command);
-  }
-  void AddArgument(const char* arg)
-  {
-    if (arg) {
-      this->CommandLineStrings.emplace_back(arg);
-    }
-  }
-  void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; }
-  void SetTimeout(cmDuration t) { this->TimeOut = t; }
-  bool StartProcess()
-  {
-    std::vector<const char*> args;
-    args.reserve(this->CommandLineStrings.size());
-    for (std::string const& cl : this->CommandLineStrings) {
-      args.push_back(cl.c_str());
-    }
-    args.push_back(nullptr); // null terminate
-    cmsysProcess_SetCommand(this->Process, args.data());
-    if (!this->WorkingDirectory.empty()) {
-      cmsysProcess_SetWorkingDirectory(this->Process,
-                                       this->WorkingDirectory.c_str());
-    }
-
-    cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1);
-    if (this->TimeOut >= cmDuration::zero()) {
-      cmsysProcess_SetTimeout(this->Process, this->TimeOut.count());
-    }
-    cmsysProcess_Execute(this->Process);
-    this->PipeState = cmsysProcess_GetState(this->Process);
-    // if the process is running or exited return true
-    return this->PipeState == cmsysProcess_State_Executing ||
-      this->PipeState == cmsysProcess_State_Exited;
-  }
-  void SetStdoutFile(const char* fname)
-  {
-    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
-  }
-  void SetStderrFile(const char* fname)
-  {
-    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
-  }
-  int WaitForExit(double* timeout = nullptr)
-  {
-    this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout);
-    return this->PipeState;
-  }
-  int GetProcessState() const { return this->PipeState; }
-
-private:
-  int PipeState;
-  cmsysProcess* Process;
-  std::vector<std::string> CommandLineStrings;
-  std::string WorkingDirectory;
-  cmDuration TimeOut;
-};
-
 cmCTestCoverageHandler::cmCTestCoverageHandler() = default;
 
 void cmCTestCoverageHandler::Initialize()
@@ -1938,34 +1864,35 @@
     cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
     return 0;
   }
+  std::vector<std::string> args{ cmd };
   if (arg) {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << " " << arg << "\n", this->Quiet);
+    args.emplace_back(arg);
   } else {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << "\n", this->Quiet);
   }
   // create a process object and start it
-  cmCTestRunProcess runCoverageSrc;
-  runCoverageSrc.SetCommand(program.c_str());
-  runCoverageSrc.AddArgument(arg);
+  cmUVProcessChainBuilder builder;
   std::string stdoutFile =
     cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
              this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
   std::string stderrFile = stdoutFile;
   stdoutFile += ".stdout";
   stderrFile += ".stderr";
-  runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
-  runCoverageSrc.SetStderrFile(stderrFile.c_str());
-  if (!runCoverageSrc.StartProcess()) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "Could not run : " << program << " " << arg << "\n"
-                                  << "kwsys process state : "
-                                  << runCoverageSrc.GetProcessState());
-    return 0;
-  }
+  std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle(
+    cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose);
+  std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle(
+    cmsys::SystemTools::Fopen(stderrFile, "w"), fclose);
+  builder.AddCommand(args)
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                       cm_fileno(stdoutHandle.get()))
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                       cm_fileno(stderrHandle.get()));
   // since we set the output file names wait for it to end
-  runCoverageSrc.WaitForExit();
+  auto chain = builder.Start();
+  chain.Wait();
   outputFile = stdoutFile;
   return 1;
 }
diff --git a/Source/CTest/cmCTestGIT.cxx b/Source/CTest/cmCTestGIT.cxx
index a6e7ac5..ca8659e 100644
--- a/Source/CTest/cmCTestGIT.cxx
+++ b/Source/CTest/cmCTestGIT.cxx
@@ -9,16 +9,17 @@
 #include <utility>
 #include <vector>
 
+#include <cmext/algorithm>
+
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestVC.h"
 #include "cmList.h"
 #include "cmProcessOutput.h"
-#include "cmProcessTools.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 
 static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
@@ -59,9 +60,9 @@
 std::string cmCTestGIT::GetWorkingRevision()
 {
   // Run plumbing "git rev-list" to get work tree revision.
-  const char* git = this->CommandLineTool.c_str();
-  const char* git_rev_list[] = { git,    "rev-list", "-n",   "1",
-                                 "HEAD", "--",       nullptr };
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_list = { git, "rev-list", "-n",
+                                            "1", "HEAD",     "--" };
   std::string rev;
   OneLineParser out(this, "rl-out> ", rev);
   OutputLogger err(this->Log, "rl-err> ");
@@ -93,13 +94,13 @@
   std::string git_dir;
 
   // Run "git rev-parse --git-dir" to locate the real .git directory.
-  const char* git = this->CommandLineTool.c_str();
-  char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr };
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" };
   std::string git_dir_line;
   OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line);
   OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
-  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
-                     cmProcessOutput::UTF8)) {
+  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err,
+                     std::string{}, cmProcessOutput::UTF8)) {
     git_dir = git_dir_line;
   }
   if (git_dir.empty()) {
@@ -118,11 +119,10 @@
     std::string cygpath_exe =
       cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe");
     if (cmSystemTools::FileExists(cygpath_exe)) {
-      char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
-                                0 };
+      std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir };
       OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line);
       OutputLogger cygpath_err(this->Log, "cygpath-err> ");
-      if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr,
+      if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{},
                          cmProcessOutput::UTF8)) {
         git_dir = git_dir_line;
       }
@@ -137,12 +137,12 @@
   std::string top_dir = this->SourceDirectory;
 
   // Run "git rev-parse --show-cdup" to locate the top of the tree.
-  const char* git = this->CommandLineTool.c_str();
-  char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr };
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" };
   std::string cdup;
   OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup);
   OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
-  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
+  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "",
                      cmProcessOutput::UTF8) &&
       !cdup.empty()) {
     top_dir += "/";
@@ -154,10 +154,10 @@
 
 bool cmCTestGIT::UpdateByFetchAndReset()
 {
-  const char* git = this->CommandLineTool.c_str();
+  std::string git = this->CommandLineTool;
 
   // Use "git fetch" to get remote commits.
-  std::vector<char const*> git_fetch;
+  std::vector<std::string> git_fetch;
   git_fetch.push_back(git);
   git_fetch.push_back("fetch");
 
@@ -167,17 +167,12 @@
     opts = this->CTest->GetCTestConfiguration("GITUpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  for (std::string const& arg : args) {
-    git_fetch.push_back(arg.c_str());
-  }
-
-  // Sentinel argument.
-  git_fetch.push_back(nullptr);
+  cm::append(git_fetch, args);
 
   // Fetch upstream refs.
   OutputLogger fetch_out(this->Log, "fetch-out> ");
   OutputLogger fetch_err(this->Log, "fetch-err> ");
-  if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) {
+  if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) {
     return false;
   }
 
@@ -208,25 +203,22 @@
   }
 
   // Reset the local branch to point at that tracked from upstream.
-  char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr };
+  std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 };
   OutputLogger reset_out(this->Log, "reset-out> ");
   OutputLogger reset_err(this->Log, "reset-err> ");
-  return this->RunChild(&git_reset[0], &reset_out, &reset_err);
+  return this->RunChild(git_reset, &reset_out, &reset_err);
 }
 
 bool cmCTestGIT::UpdateByCustom(std::string const& custom)
 {
   cmList git_custom_command{ custom, cmList::EmptyElements::Yes };
-  std::vector<char const*> git_custom;
-  git_custom.reserve(git_custom_command.size() + 1);
-  for (std::string const& i : git_custom_command) {
-    git_custom.push_back(i.c_str());
-  }
-  git_custom.push_back(nullptr);
+  std::vector<std::string> git_custom;
+  git_custom.reserve(git_custom_command.size());
+  cm::append(git_custom, git_custom_command);
 
   OutputLogger custom_out(this->Log, "custom-out> ");
   OutputLogger custom_err(this->Log, "custom-err> ");
-  return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err);
+  return this->RunUpdateCommand(git_custom, &custom_out, &custom_err);
 }
 
 bool cmCTestGIT::UpdateInternal()
@@ -245,13 +237,14 @@
   }
 
   std::string top_dir = this->FindTopDir();
-  const char* git = this->CommandLineTool.c_str();
-  const char* recursive = "--recursive";
-  const char* sync_recursive = "--recursive";
+  std::string git = this->CommandLineTool;
+  std::string recursive = "--recursive";
+  std::string sync_recursive = "--recursive";
 
   // Git < 1.6.5 did not support submodule --recursive
+  bool support_recursive = true;
   if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) {
-    recursive = nullptr;
+    support_recursive = false;
     // No need to require >= 1.6.5 if there are no submodules.
     if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
       this->Log << "Git < 1.6.5 cannot update submodules recursively\n";
@@ -259,8 +252,9 @@
   }
 
   // Git < 1.8.1 did not support sync --recursive
+  bool support_sync_recursive = true;
   if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) {
-    sync_recursive = nullptr;
+    support_sync_recursive = false;
     // No need to require >= 1.8.1 if there are no submodules.
     if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
       this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n";
@@ -275,35 +269,39 @@
   std::string init_submodules =
     this->CTest->GetCTestConfiguration("GITInitSubmodules");
   if (cmIsOn(init_submodules)) {
-    char const* git_submodule_init[] = { git, "submodule", "init", nullptr };
+    std::vector<std::string> git_submodule_init = { git, "submodule", "init" };
     ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
-                         top_dir.c_str());
+                         top_dir);
 
     if (!ret) {
       return false;
     }
   }
 
-  char const* git_submodule_sync[] = { git, "submodule", "sync",
-                                       sync_recursive, nullptr };
+  std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" };
+  if (support_sync_recursive) {
+    git_submodule_sync.push_back(sync_recursive);
+  }
   ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err,
-                       top_dir.c_str());
+                       top_dir);
 
   if (!ret) {
     return false;
   }
 
-  char const* git_submodule[] = { git, "submodule", "update", recursive,
-                                  nullptr };
+  std::vector<std::string> git_submodule = { git, "submodule", "update" };
+  if (support_recursive) {
+    git_submodule.push_back(recursive);
+  }
   return this->RunChild(git_submodule, &submodule_out, &submodule_err,
-                        top_dir.c_str());
+                        top_dir);
 }
 
 unsigned int cmCTestGIT::GetGitVersion()
 {
   if (!this->CurrentGitVersion) {
-    const char* git = this->CommandLineTool.c_str();
-    char const* git_version[] = { git, "--version", nullptr };
+    std::string git = this->CommandLineTool;
+    std::vector<std::string> git_version = { git, "--version" };
     std::string version;
     OneLineParser version_out(this, "version-out> ", version);
     OutputLogger version_err(this->Log, "version-err> ");
@@ -606,50 +604,49 @@
 {
   // Use 'git rev-list ... | git diff-tree ...' to get revisions.
   std::string range = this->OldRevision + ".." + this->NewRevision;
-  const char* git = this->CommandLineTool.c_str();
-  const char* git_rev_list[] = { git,           "rev-list", "--reverse",
-                                 range.c_str(), "--",       nullptr };
-  const char* git_diff_tree[] = {
-    git,  "diff-tree",    "--stdin",          "--always", "-z",
-    "-r", "--pretty=raw", "--encoding=utf-8", nullptr
+  std::string git = this->CommandLineTool;
+  std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse",
+                                            range, "--" };
+  std::vector<std::string> git_diff_tree = {
+    git,  "diff-tree", "--stdin",      "--always",
+    "-z", "-r",        "--pretty=raw", "--encoding=utf-8"
   };
   this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | "
             << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n";
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_AddCommand(cp, git_rev_list);
-  cmsysProcess_AddCommand(cp, git_diff_tree);
-  cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str());
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(git_rev_list)
+    .AddCommand(git_diff_tree)
+    .SetWorkingDirectory(this->SourceDirectory);
 
   CommitParser out(this, "dt-out> ");
   OutputLogger err(this->Log, "dt-err> ");
-  cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8);
+  cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8);
 
   // Send one extra zero-byte to terminate the last record.
   out.Process("", 1);
 
-  cmsysProcess_Delete(cp);
   return true;
 }
 
 bool cmCTestGIT::LoadModifications()
 {
-  const char* git = this->CommandLineTool.c_str();
+  std::string git = this->CommandLineTool;
 
   // Use 'git update-index' to refresh the index w.r.t. the work tree.
-  const char* git_update_index[] = { git, "update-index", "--refresh",
-                                     nullptr };
+  std::vector<std::string> git_update_index = { git, "update-index",
+                                                "--refresh" };
   OutputLogger ui_out(this->Log, "ui-out> ");
   OutputLogger ui_err(this->Log, "ui-err> ");
-  this->RunChild(git_update_index, &ui_out, &ui_err, nullptr,
+  this->RunChild(git_update_index, &ui_out, &ui_err, "",
                  cmProcessOutput::UTF8);
 
   // Use 'git diff-index' to get modified files.
-  const char* git_diff_index[] = { git,    "diff-index", "-z",
-                                   "HEAD", "--",         nullptr };
+  std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD",
+                                              "--" };
   DiffParser out(this, "di-out> ");
   OutputLogger err(this->Log, "di-err> ");
-  this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8);
+  this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8);
 
   for (Change const& c : out.Changes) {
     this->DoModification(PathModified, c.Path);
diff --git a/Source/CTest/cmCTestHG.cxx b/Source/CTest/cmCTestHG.cxx
index 97b01ba..e1a945d 100644
--- a/Source/CTest/cmCTestHG.cxx
+++ b/Source/CTest/cmCTestHG.cxx
@@ -11,7 +11,6 @@
 
 #include "cmCTest.h"
 #include "cmCTestVC.h"
-#include "cmProcessTools.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
 
@@ -96,8 +95,8 @@
 std::string cmCTestHG::GetWorkingRevision()
 {
   // Run plumbing "hg identify" to get work tree revision.
-  const char* hg = this->CommandLineTool.c_str();
-  const char* hg_identify[] = { hg, "identify", "-i", nullptr };
+  std::string hg = this->CommandLineTool;
+  std::vector<std::string> hg_identify = { hg, "identify", "-i" };
   std::string rev;
   IdentifyParser out(this, "rev-out> ", rev);
   OutputLogger err(this->Log, "rev-err> ");
@@ -128,16 +127,16 @@
 {
   // Use "hg pull" followed by "hg update" to update the working tree.
   {
-    const char* hg = this->CommandLineTool.c_str();
-    const char* hg_pull[] = { hg, "pull", "-v", nullptr };
+    std::string hg = this->CommandLineTool;
+    std::vector<std::string> hg_pull = { hg, "pull", "-v" };
     OutputLogger out(this->Log, "pull-out> ");
     OutputLogger err(this->Log, "pull-err> ");
-    this->RunChild(&hg_pull[0], &out, &err);
+    this->RunChild(hg_pull, &out, &err);
   }
 
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
-  std::vector<char const*> hg_update;
+  std::vector<std::string> hg_update;
   hg_update.push_back(this->CommandLineTool.c_str());
   hg_update.push_back("update");
   hg_update.push_back("-v");
@@ -148,16 +147,11 @@
     opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  for (std::string const& arg : args) {
-    hg_update.push_back(arg.c_str());
-  }
-
-  // Sentinel argument.
-  hg_update.push_back(nullptr);
+  cm::append(hg_update, args);
 
   OutputLogger out(this->Log, "update-out> ");
   OutputLogger err(this->Log, "update-err> ");
-  return this->RunUpdateCommand(hg_update.data(), &out, &err);
+  return this->RunUpdateCommand(hg_update, &out, &err);
 }
 
 class cmCTestHG::LogParser
@@ -278,8 +272,8 @@
   // the project has spaces in the path.  Also, they may not have
   // proper XML escapes.
   std::string range = this->OldRevision + ":" + this->NewRevision;
-  const char* hg = this->CommandLineTool.c_str();
-  const char* hgXMLTemplate = "<logentry\n"
+  std::string hg = this->CommandLineTool;
+  std::string hgXMLTemplate = "<logentry\n"
                               "   revision=\"{node|short}\">\n"
                               "  <author>{author|person}</author>\n"
                               "  <email>{author|email}</email>\n"
@@ -289,10 +283,8 @@
                               "  <file_adds>{file_adds}</file_adds>\n"
                               "  <file_dels>{file_dels}</file_dels>\n"
                               "</logentry>\n";
-  const char* hg_log[] = {
-    hg,           "log",         "--removed", "-r", range.c_str(),
-    "--template", hgXMLTemplate, nullptr
-  };
+  std::vector<std::string> hg_log = { hg,    "log",        "--removed",  "-r",
+                                      range, "--template", hgXMLTemplate };
 
   LogParser out(this, "log-out> ");
   out.Process("<?xml version=\"1.0\"?>\n"
@@ -306,8 +298,8 @@
 bool cmCTestHG::LoadModifications()
 {
   // Use 'hg status' to get modified files.
-  const char* hg = this->CommandLineTool.c_str();
-  const char* hg_status[] = { hg, "status", nullptr };
+  std::string hg = this->CommandLineTool;
+  std::vector<std::string> hg_status = { hg, "status" };
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(hg_status, &out, &err);
diff --git a/Source/CTest/cmCTestLaunch.cxx b/Source/CTest/cmCTestLaunch.cxx
index 4a33869..6b13ad1 100644
--- a/Source/CTest/cmCTestLaunch.cxx
+++ b/Source/CTest/cmCTestLaunch.cxx
@@ -2,13 +2,19 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunch.h"
 
+#include <cstdio>
 #include <cstring>
 #include <iostream>
+#include <memory>
+#include <utility>
+
+#include <cm3p/uv.h>
 
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
+#include "cm_fileno.hxx"
+
 #include "cmCTestLaunchReporter.h"
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -17,6 +23,9 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmake.h"
 
 #ifdef _WIN32
@@ -28,8 +37,6 @@
 
 cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
 {
-  this->Process = nullptr;
-
   if (!this->ParseArguments(argc, argv)) {
     return;
   }
@@ -40,13 +47,9 @@
   this->ScrapeRulesLoaded = false;
   this->HaveOut = false;
   this->HaveErr = false;
-  this->Process = cmsysProcess_New();
 }
 
-cmCTestLaunch::~cmCTestLaunch()
-{
-  cmsysProcess_Delete(this->Process);
-}
+cmCTestLaunch::~cmCTestLaunch() = default;
 
 bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
 {
@@ -113,15 +116,12 @@
 
   // Extract the real command line.
   if (arg0) {
-    this->RealArgC = argc - arg0;
-    this->RealArgV = argv + arg0;
-    for (int i = 0; i < this->RealArgC; ++i) {
-      this->HandleRealArg(this->RealArgV[i]);
+    for (int i = 0; i < argc - arg0; ++i) {
+      this->RealArgV.emplace_back((argv + arg0)[i]);
+      this->HandleRealArg((argv + arg0)[i]);
     }
     return true;
   }
-  this->RealArgC = 0;
-  this->RealArgV = nullptr;
   std::cerr << "No launch/command separator ('--') found!\n";
   return false;
 }
@@ -151,17 +151,22 @@
   }
 
   // Prepare to run the real command.
-  cmsysProcess* cp = this->Process;
-  cmsysProcess_SetCommand(cp, this->RealArgV);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(this->RealArgV);
 
   cmsys::ofstream fout;
   cmsys::ofstream ferr;
   if (this->Reporter.Passthru) {
     // In passthru mode we just share the output pipes.
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
+    builder
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                         cm_fileno(stdout))
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                         cm_fileno(stderr));
   } else {
     // In full mode we record the child output pipes to log files.
+    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
     fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary);
     ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
   }
@@ -174,51 +179,65 @@
 #endif
 
   // Run the real command.
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
 
   // Record child stdout and stderr if necessary.
+  cm::uv_pipe_ptr outPipe;
+  cm::uv_pipe_ptr errPipe;
+  bool outFinished = true;
+  bool errFinished = true;
+  cmProcessOutput processOutput;
+  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
+  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
   if (!this->Reporter.Passthru) {
-    char* data = nullptr;
-    int length = 0;
-    cmProcessOutput processOutput;
-    std::string strdata;
-    while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
-      if (p == cmsysProcess_Pipe_STDOUT) {
-        processOutput.DecodeText(data, length, strdata, 1);
-        fout.write(strdata.c_str(), strdata.size());
-        std::cout.write(strdata.c_str(), strdata.size());
-        this->HaveOut = true;
-      } else if (p == cmsysProcess_Pipe_STDERR) {
-        processOutput.DecodeText(data, length, strdata, 2);
-        ferr.write(strdata.c_str(), strdata.size());
-        std::cerr.write(strdata.c_str(), strdata.size());
-        this->HaveErr = true;
-      }
-    }
-    processOutput.DecodeText(std::string(), strdata, 1);
-    if (!strdata.empty()) {
-      fout.write(strdata.c_str(), strdata.size());
-      std::cout.write(strdata.c_str(), strdata.size());
-    }
-    processOutput.DecodeText(std::string(), strdata, 2);
-    if (!strdata.empty()) {
-      ferr.write(strdata.c_str(), strdata.size());
-      std::cerr.write(strdata.c_str(), strdata.size());
-    }
+    auto beginRead = [&chain, &processOutput](
+                       cm::uv_pipe_ptr& pipe, int stream, std::ostream& out,
+                       cmsys::ofstream& file, bool& haveData, bool& finished,
+                       int id) -> std::unique_ptr<cmUVStreamReadHandle> {
+      pipe.init(chain.GetLoop(), 0);
+      uv_pipe_open(pipe, stream);
+      finished = false;
+      return cmUVStreamRead(
+        pipe,
+        [&processOutput, &out, &file, id, &haveData](std::vector<char> data) {
+          std::string strdata;
+          processOutput.DecodeText(data.data(), data.size(), strdata, id);
+          file.write(strdata.c_str(), strdata.size());
+          out.write(strdata.c_str(), strdata.size());
+          haveData = true;
+        },
+        [&processOutput, &out, &file, &finished, id]() {
+          std::string strdata;
+          processOutput.DecodeText(std::string(), strdata, id);
+          if (!strdata.empty()) {
+            file.write(strdata.c_str(), strdata.size());
+            out.write(strdata.c_str(), strdata.size());
+          }
+          finished = true;
+        });
+    };
+    outputHandle = beginRead(outPipe, chain.OutputStream(), std::cout, fout,
+                             this->HaveOut, outFinished, 1);
+    errorHandle = beginRead(errPipe, chain.ErrorStream(), std::cerr, ferr,
+                            this->HaveErr, errFinished, 2);
   }
 
   // Wait for the real command to finish.
-  cmsysProcess_WaitForExit(cp, nullptr);
-  this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp);
+  while (!(chain.Finished() && outFinished && errFinished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
+  this->Reporter.Status = chain.GetStatus(0);
+  if (this->Reporter.Status.GetException().first ==
+      cmUVProcessChain::ExceptionCode::Spawn) {
+    this->Reporter.ExitCode = 1;
+  } else {
+    this->Reporter.ExitCode =
+      static_cast<int>(this->Reporter.Status.ExitStatus);
+  }
 }
 
 int cmCTestLaunch::Run()
 {
-  if (!this->Process) {
-    std::cerr << "Could not allocate cmsysProcess instance!\n";
-    return -1;
-  }
-
   this->RunChild();
 
   if (this->CheckResults()) {
@@ -226,7 +245,6 @@
   }
 
   this->LoadConfig();
-  this->Reporter.Process = this->Process;
   this->Reporter.WriteXML();
 
   return this->Reporter.ExitCode;
diff --git a/Source/CTest/cmCTestLaunch.h b/Source/CTest/cmCTestLaunch.h
index c5a6476..ef21a26 100644
--- a/Source/CTest/cmCTestLaunch.h
+++ b/Source/CTest/cmCTestLaunch.h
@@ -43,15 +43,12 @@
   bool ParseArguments(int argc, const char* const* argv);
 
   // The real command line appearing after launcher arguments.
-  int RealArgC;
-  const char* const* RealArgV;
+  std::vector<std::string> RealArgV;
 
   // The real command line after response file expansion.
   std::vector<std::string> RealArgs;
   void HandleRealArg(const char* arg);
 
-  struct cmsysProcess_s* Process;
-
   // Whether or not any data have been written to stdout or stderr.
   bool HaveOut;
   bool HaveErr;
diff --git a/Source/CTest/cmCTestLaunchReporter.cxx b/Source/CTest/cmCTestLaunchReporter.cxx
index 149ba5d..4b4e5c5 100644
--- a/Source/CTest/cmCTestLaunchReporter.cxx
+++ b/Source/CTest/cmCTestLaunchReporter.cxx
@@ -2,8 +2,9 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunchReporter.h"
 
+#include <utility>
+
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCryptoHash.h"
@@ -22,6 +23,7 @@
 cmCTestLaunchReporter::cmCTestLaunchReporter()
 {
   this->Passthru = true;
+  this->Status.Finished = true;
   this->ExitCode = 1;
   this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
 
@@ -231,35 +233,23 @@
 
   // ExitCondition
   cmXMLElement e4(e3, "ExitCondition");
-  cmsysProcess* cp = this->Process;
-  switch (cmsysProcess_GetState(cp)) {
-    case cmsysProcess_State_Starting:
-      e4.Content("No process has been executed");
-      break;
-    case cmsysProcess_State_Executing:
-      e4.Content("The process is still executing");
-      break;
-    case cmsysProcess_State_Disowned:
-      e4.Content("Disowned");
-      break;
-    case cmsysProcess_State_Killed:
-      e4.Content("Killed by parent");
-      break;
-
-    case cmsysProcess_State_Expired:
-      e4.Content("Killed when timeout expired");
-      break;
-    case cmsysProcess_State_Exited:
-      e4.Content(this->ExitCode);
-      break;
-    case cmsysProcess_State_Exception:
-      e4.Content("Terminated abnormally: ");
-      e4.Content(cmsysProcess_GetExceptionString(cp));
-      break;
-    case cmsysProcess_State_Error:
-      e4.Content("Error administrating child process: ");
-      e4.Content(cmsysProcess_GetErrorString(cp));
-      break;
+  if (this->Status.Finished) {
+    auto exception = this->Status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        e4.Content(this->ExitCode);
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn:
+        e4.Content("Error administrating child process: ");
+        e4.Content(exception.second);
+        break;
+      default:
+        e4.Content("Terminated abnormally: ");
+        e4.Content(exception.second);
+        break;
+    }
+  } else {
+    e4.Content("Killed when timeout expired");
   }
 }
 
diff --git a/Source/CTest/cmCTestLaunchReporter.h b/Source/CTest/cmCTestLaunchReporter.h
index 4be0d9b..2bb78f8 100644
--- a/Source/CTest/cmCTestLaunchReporter.h
+++ b/Source/CTest/cmCTestLaunchReporter.h
@@ -10,6 +10,8 @@
 
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmUVProcessChain.h"
+
 class cmXMLElement;
 
 /** \class cmCTestLaunchReporter
@@ -48,7 +50,7 @@
   void ComputeFileNames();
 
   bool Passthru;
-  struct cmsysProcess_s* Process;
+  cmUVProcessChain::Status Status;
   int ExitCode;
 
   // Temporary log files for stdout and stderr of real command.
diff --git a/Source/CTest/cmCTestMemCheckHandler.cxx b/Source/CTest/cmCTestMemCheckHandler.cxx
index 6f6a642..b51de84 100644
--- a/Source/CTest/cmCTestMemCheckHandler.cxx
+++ b/Source/CTest/cmCTestMemCheckHandler.cxx
@@ -7,6 +7,7 @@
 #include <cstring>
 #include <iostream>
 #include <iterator>
+#include <ratio>
 #include <sstream>
 #include <utility>
 
diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx
index 7d22a87..ca07a08 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.cxx
+++ b/Source/CTest/cmCTestMultiProcessHandler.cxx
@@ -35,6 +35,7 @@
 #include "cmCTestRunTest.h"
 #include "cmCTestTestHandler.h"
 #include "cmDuration.h"
+#include "cmJSONState.h"
 #include "cmListFileCache.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
@@ -75,6 +76,7 @@
   this->ProcessorsAvailable = cmAffinity::GetProcessorsAvailable();
   this->HaveAffinity = this->ProcessorsAvailable.size();
   this->HasCycles = false;
+  this->HasInvalidGeneratedResourceSpec = false;
   this->SerialTestRunning = false;
 }
 
@@ -95,7 +97,9 @@
   if (!this->CTest->GetShowOnly()) {
     this->ReadCostData();
     this->HasCycles = !this->CheckCycles();
-    if (this->HasCycles) {
+    this->HasInvalidGeneratedResourceSpec =
+      !this->CheckGeneratedResourceSpec();
+    if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
       return;
     }
     this->CreateTestCostList();
@@ -125,7 +129,7 @@
 void cmCTestMultiProcessHandler::RunTests()
 {
   this->CheckResume();
-  if (this->HasCycles) {
+  if (this->HasCycles || this->HasInvalidGeneratedResourceSpec) {
     return;
   }
 #ifdef CMAKE_UV_SIGNAL_HACK
@@ -180,7 +184,7 @@
   }
   testRun->SetIndex(test);
   testRun->SetTestProperties(this->Properties[test]);
-  if (this->TestHandler->UseResourceSpec) {
+  if (this->UseResourceSpec) {
     testRun->SetUseAllocatedResources(true);
     testRun->SetAllocatedResources(this->AllocatedResources[test]);
   }
@@ -229,15 +233,15 @@
       }
       e << "\n";
     }
-    e << "Resource spec file:\n\n  " << this->TestHandler->ResourceSpecFile;
-    cmCTestRunTest::StartFailure(std::move(testRun), e.str(),
+    e << "Resource spec file:\n\n  " << this->ResourceSpecFile;
+    cmCTestRunTest::StartFailure(std::move(testRun), this->Total, e.str(),
                                  "Insufficient resources");
     return false;
   }
 
   cmWorkingDirectory workdir(this->Properties[test]->Directory);
   if (workdir.Failed()) {
-    cmCTestRunTest::StartFailure(std::move(testRun),
+    cmCTestRunTest::StartFailure(std::move(testRun), this->Total,
                                  "Failed to change working directory to " +
                                    this->Properties[test]->Directory + " : " +
                                    std::strerror(workdir.GetLastResult()),
@@ -253,7 +257,7 @@
 
 bool cmCTestMultiProcessHandler::AllocateResources(int index)
 {
-  if (!this->TestHandler->UseResourceSpec) {
+  if (!this->UseResourceSpec) {
     return true;
   }
 
@@ -322,7 +326,7 @@
 
 void cmCTestMultiProcessHandler::DeallocateResources(int index)
 {
-  if (!this->TestHandler->UseResourceSpec) {
+  if (!this->UseResourceSpec) {
     return;
   }
 
@@ -358,7 +362,7 @@
 
 void cmCTestMultiProcessHandler::CheckResourcesAvailable()
 {
-  if (this->TestHandler->UseResourceSpec) {
+  if (this->UseResourceSpec) {
     for (auto test : this->SortedTests) {
       std::map<std::string, std::vector<cmCTestBinPackerAllocation>>
         allocations;
@@ -1445,3 +1449,81 @@
                      this->Quiet);
   return true;
 }
+
+bool cmCTestMultiProcessHandler::CheckGeneratedResourceSpec()
+{
+  for (auto& test : this->Properties) {
+    if (!test.second->GeneratedResourceSpecFile.empty()) {
+      if (this->ResourceSpecSetupTest) {
+        cmCTestLog(
+          this->CTest, ERROR_MESSAGE,
+          "Only one test may define the GENERATED_RESOURCE_SPEC_FILE property"
+            << std::endl);
+        return false;
+      }
+
+      if (test.second->FixturesSetup.size() != 1) {
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "Test that defines GENERATED_RESOURCE_SPEC_FILE must have "
+                   "exactly one FIXTURES_SETUP"
+                     << std::endl);
+        return false;
+      }
+
+      if (!cmSystemTools::FileIsFullPath(
+            test.second->GeneratedResourceSpecFile)) {
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "GENERATED_RESOURCE_SPEC_FILE must be an absolute path"
+                     << std::endl);
+        return false;
+      }
+
+      this->ResourceSpecSetupTest = test.first;
+      this->ResourceSpecSetupFixture = *test.second->FixturesSetup.begin();
+    }
+  }
+
+  if (!this->ResourceSpecSetupFixture.empty()) {
+    for (auto& test : this->Properties) {
+      if (!test.second->ResourceGroups.empty() &&
+          !test.second->FixturesRequired.count(
+            this->ResourceSpecSetupFixture)) {
+        cmCTestLog(this->CTest, ERROR_MESSAGE,
+                   "All tests that have RESOURCE_GROUPS must include the "
+                   "resource spec generator fixture in their FIXTURES_REQUIRED"
+                     << std::endl);
+        return false;
+      }
+    }
+  }
+
+  if (!this->ResourceSpecFile.empty()) {
+    if (this->ResourceSpecSetupTest) {
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "GENERATED_RESOURCE_SPEC_FILE test property cannot be used "
+                 "in conjunction with ResourceSpecFile option"
+                   << std::endl);
+      return false;
+    }
+    std::string error;
+    if (!this->InitResourceAllocator(error)) {
+      cmCTestLog(this->CTest, ERROR_MESSAGE, error << std::endl);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool cmCTestMultiProcessHandler::InitResourceAllocator(std::string& error)
+{
+  if (!this->ResourceSpec.ReadFromJSONFile(this->ResourceSpecFile)) {
+    error = cmStrCat("Could not read/parse resource spec file ",
+                     this->ResourceSpecFile, ": ",
+                     this->ResourceSpec.parseState.GetErrorMessage());
+    return false;
+  }
+  this->UseResourceSpec = true;
+  this->ResourceAllocator.InitializeFromResourceSpec(this->ResourceSpec);
+  return true;
+}
diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h
index 2f5ad40..3b4e9c5 100644
--- a/Source/CTest/cmCTestMultiProcessHandler.h
+++ b/Source/CTest/cmCTestMultiProcessHandler.h
@@ -11,15 +11,17 @@
 #include <string>
 #include <vector>
 
+#include <cm/optional>
+
 #include <cm3p/uv.h>
 
 #include "cmCTest.h"
 #include "cmCTestResourceAllocator.h"
+#include "cmCTestResourceSpec.h"
 #include "cmCTestTestHandler.h"
 #include "cmUVHandlePtr.h"
 
 struct cmCTestBinPackerAllocation;
-class cmCTestResourceSpec;
 class cmCTestRunTest;
 
 /** \class cmCTestMultiProcessHandler
@@ -90,13 +92,13 @@
     this->RepeatCount = count;
   }
 
-  void SetQuiet(bool b) { this->Quiet = b; }
-
-  void InitResourceAllocator(const cmCTestResourceSpec& spec)
+  void SetResourceSpecFile(const std::string& resourceSpecFile)
   {
-    this->ResourceAllocator.InitializeFromResourceSpec(spec);
+    this->ResourceSpecFile = resourceSpecFile;
   }
 
+  void SetQuiet(bool b) { this->Quiet = b; }
+
   void CheckResourcesAvailable();
 
 protected:
@@ -158,6 +160,15 @@
     std::map<std::string, ResourceAllocationError>* errors = nullptr);
   void DeallocateResources(int index);
   bool AllResourcesAvailable();
+  bool InitResourceAllocator(std::string& error);
+  bool CheckGeneratedResourceSpec();
+
+  bool UseResourceSpec = false;
+  cmCTestResourceSpec ResourceSpec;
+  std::string ResourceSpecFile;
+  std::string ResourceSpecSetupFixture;
+  cm::optional<std::size_t> ResourceSpecSetupTest;
+  bool HasInvalidGeneratedResourceSpec;
 
   // map from test number to set of depend tests
   TestMap Tests;
diff --git a/Source/CTest/cmCTestP4.cxx b/Source/CTest/cmCTestP4.cxx
index e22ec4b..5d71b84 100644
--- a/Source/CTest/cmCTestP4.cxx
+++ b/Source/CTest/cmCTestP4.cxx
@@ -14,7 +14,6 @@
 #include "cmCTest.h"
 #include "cmCTestVC.h"
 #include "cmList.h"
-#include "cmProcessTools.h"
 #include "cmRange.h"
 #include "cmSystemTools.h"
 
@@ -150,17 +149,16 @@
   auto it = this->Users.find(username);
 
   if (it == this->Users.end()) {
-    std::vector<char const*> p4_users;
+    std::vector<std::string> p4_users;
     this->SetP4Options(p4_users);
     p4_users.push_back("users");
     p4_users.push_back("-m");
     p4_users.push_back("1");
-    p4_users.push_back(username.c_str());
-    p4_users.push_back(nullptr);
+    p4_users.push_back(username);
 
     UserParser out(this, "users-out> ");
     OutputLogger err(this->Log, "users-err> ");
-    this->RunChild(p4_users.data(), &out, &err);
+    this->RunChild(p4_users, &out, &err);
 
     // The user should now be added to the map. Search again.
     it = this->Users.find(username);
@@ -304,10 +302,10 @@
   }
 };
 
-void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions)
+void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions)
 {
   if (this->P4Options.empty()) {
-    const char* p4 = this->CommandLineTool.c_str();
+    std::string p4 = this->CommandLineTool;
     this->P4Options.emplace_back(p4);
 
     // The CTEST_P4_CLIENT variable sets the P4 client used when issuing
@@ -329,15 +327,12 @@
     cm::append(this->P4Options, cmSystemTools::ParseArguments(opts));
   }
 
-  CommandOptions.clear();
-  for (std::string const& o : this->P4Options) {
-    CommandOptions.push_back(o.c_str());
-  }
+  CommandOptions = this->P4Options;
 }
 
 std::string cmCTestP4::GetWorkingRevision()
 {
-  std::vector<char const*> p4_identify;
+  std::vector<std::string> p4_identify;
   this->SetP4Options(p4_identify);
 
   p4_identify.push_back("changes");
@@ -346,14 +341,13 @@
   p4_identify.push_back("-t");
 
   std::string source = this->SourceDirectory + "/...#have";
-  p4_identify.push_back(source.c_str());
-  p4_identify.push_back(nullptr);
+  p4_identify.push_back(source);
 
   std::string rev;
   IdentifyParser out(this, "p4_changes-out> ", rev);
   OutputLogger err(this->Log, "p4_changes-err> ");
 
-  bool result = this->RunChild(p4_identify.data(), &out, &err);
+  bool result = this->RunChild(p4_identify, &out, &err);
 
   // If there was a problem contacting the server return "<unknown>"
   if (!result) {
@@ -389,7 +383,7 @@
 
 bool cmCTestP4::LoadRevisions()
 {
-  std::vector<char const*> p4_changes;
+  std::vector<std::string> p4_changes;
   this->SetP4Options(p4_changes);
 
   // Use 'p4 changes ...@old,new' to get a list of changelists
@@ -410,38 +404,36 @@
     .append(this->NewRevision);
 
   p4_changes.push_back("changes");
-  p4_changes.push_back(range.c_str());
-  p4_changes.push_back(nullptr);
+  p4_changes.push_back(range);
 
   ChangesParser out(this, "p4_changes-out> ");
   OutputLogger err(this->Log, "p4_changes-err> ");
 
   this->ChangeLists.clear();
-  this->RunChild(p4_changes.data(), &out, &err);
+  this->RunChild(p4_changes, &out, &err);
 
   if (this->ChangeLists.empty()) {
     return true;
   }
 
   // p4 describe -s ...@1111111,2222222
-  std::vector<char const*> p4_describe;
+  std::vector<std::string> p4_describe;
   for (std::string const& i : cmReverseRange(this->ChangeLists)) {
     this->SetP4Options(p4_describe);
     p4_describe.push_back("describe");
     p4_describe.push_back("-s");
-    p4_describe.push_back(i.c_str());
-    p4_describe.push_back(nullptr);
+    p4_describe.push_back(i);
 
     DescribeParser outDescribe(this, "p4_describe-out> ");
     OutputLogger errDescribe(this->Log, "p4_describe-err> ");
-    this->RunChild(p4_describe.data(), &outDescribe, &errDescribe);
+    this->RunChild(p4_describe, &outDescribe, &errDescribe);
   }
   return true;
 }
 
 bool cmCTestP4::LoadModifications()
 {
-  std::vector<char const*> p4_diff;
+  std::vector<std::string> p4_diff;
   this->SetP4Options(p4_diff);
 
   p4_diff.push_back("diff");
@@ -449,12 +441,11 @@
   // Ideally we would use -Od but not all clients support it
   p4_diff.push_back("-dn");
   std::string source = this->SourceDirectory + "/...";
-  p4_diff.push_back(source.c_str());
-  p4_diff.push_back(nullptr);
+  p4_diff.push_back(source);
 
   DiffParser out(this, "p4_diff-out> ");
   OutputLogger err(this->Log, "p4_diff-err> ");
-  this->RunChild(p4_diff.data(), &out, &err);
+  this->RunChild(p4_diff, &out, &err);
   return true;
 }
 
@@ -462,17 +453,14 @@
 {
   cmList p4_custom_command{ custom, cmList::EmptyElements::Yes };
 
-  std::vector<char const*> p4_custom;
-  p4_custom.reserve(p4_custom_command.size() + 1);
-  for (std::string const& i : p4_custom_command) {
-    p4_custom.push_back(i.c_str());
-  }
-  p4_custom.push_back(nullptr);
+  std::vector<std::string> p4_custom;
+  p4_custom.reserve(p4_custom_command.size());
+  cm::append(p4_custom, p4_custom_command);
 
   OutputLogger custom_out(this->Log, "p4_customsync-out> ");
   OutputLogger custom_err(this->Log, "p4_customsync-err> ");
 
-  return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err);
+  return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err);
 }
 
 bool cmCTestP4::UpdateImpl()
@@ -489,7 +477,7 @@
     return false;
   }
 
-  std::vector<char const*> p4_sync;
+  std::vector<std::string> p4_sync;
   this->SetP4Options(p4_sync);
 
   p4_sync.push_back("sync");
@@ -500,9 +488,7 @@
     opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  for (std::string const& arg : args) {
-    p4_sync.push_back(arg.c_str());
-  }
+  cm::append(p4_sync, args);
 
   std::string source = this->SourceDirectory + "/...";
 
@@ -516,11 +502,10 @@
     source.append("@\"").append(date).append("\"");
   }
 
-  p4_sync.push_back(source.c_str());
-  p4_sync.push_back(nullptr);
+  p4_sync.push_back(source);
 
   OutputLogger out(this->Log, "p4_sync-out> ");
   OutputLogger err(this->Log, "p4_sync-err> ");
 
-  return this->RunUpdateCommand(p4_sync.data(), &out, &err);
+  return this->RunUpdateCommand(p4_sync, &out, &err);
 }
diff --git a/Source/CTest/cmCTestP4.h b/Source/CTest/cmCTestP4.h
index 1889520..827caa1 100644
--- a/Source/CTest/cmCTestP4.h
+++ b/Source/CTest/cmCTestP4.h
@@ -39,7 +39,7 @@
   std::vector<std::string> P4Options;
 
   User GetUserData(const std::string& username);
-  void SetP4Options(std::vector<char const*>& options);
+  void SetP4Options(std::vector<std::string>& options);
 
   std::string GetWorkingRevision();
   bool NoteOldRevision() override;
diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx
index 19e505f..8ceb9db 100644
--- a/Source/CTest/cmCTestRunTest.cxx
+++ b/Source/CTest/cmCTestRunTest.cxx
@@ -152,6 +152,18 @@
       }
     }
   }
+  std::string resourceSpecParseError;
+  if (!this->TestProperties->GeneratedResourceSpecFile.empty()) {
+    this->MultiTestHandler.ResourceSpecFile =
+      this->TestProperties->GeneratedResourceSpecFile;
+    if (!this->MultiTestHandler.InitResourceAllocator(
+          resourceSpecParseError)) {
+      reason = "Invalid resource spec file";
+      forceFail = true;
+    } else {
+      this->MultiTestHandler.CheckResourcesAvailable();
+    }
+  }
   std::ostringstream outputStream;
   if (res == cmProcess::State::Exited) {
     bool success = !forceFail &&
@@ -260,6 +272,16 @@
     cmCTestLog(this->CTest, HANDLER_OUTPUT, this->ProcessOutput << std::endl);
   }
 
+  if (!resourceSpecParseError.empty()) {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               resourceSpecParseError << std::endl);
+  } else if (!this->TestProperties->GeneratedResourceSpecFile.empty()) {
+    cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+               "Using generated resource spec file "
+                 << this->TestProperties->GeneratedResourceSpecFile
+                 << std::endl);
+  }
+
   if (this->TestHandler->LogFile) {
     *this->TestHandler->LogFile << "Test time = " << buf << std::endl;
   }
@@ -372,7 +394,8 @@
   // change to tests directory
   cmWorkingDirectory workdir(testRun->TestProperties->Directory);
   if (workdir.Failed()) {
-    testRun->StartFailure("Failed to change working directory to " +
+    testRun->StartFailure(testRun->TotalNumberOfTests,
+                          "Failed to change working directory to " +
                             testRun->TestProperties->Directory + " : " +
                             std::strerror(workdir.GetLastResult()),
                           "Failed to change working directory");
@@ -437,25 +460,25 @@
 }
 
 void cmCTestRunTest::StartFailure(std::unique_ptr<cmCTestRunTest> runner,
-                                  std::string const& output,
+                                  size_t total, std::string const& output,
                                   std::string const& detail)
 {
   auto* testRun = runner.get();
 
   testRun->TestProcess = cm::make_unique<cmProcess>(std::move(runner));
-  testRun->StartFailure(output, detail);
+  testRun->StartFailure(total, output, detail);
 
   testRun->FinalizeTest(false);
 }
 
-void cmCTestRunTest::StartFailure(std::string const& output,
+void cmCTestRunTest::StartFailure(size_t total, std::string const& output,
                                   std::string const& detail)
 {
   // Still need to log the Start message so the test summary records our
   // attempt to start this test
   if (!this->CTest->GetTestProgressOutput()) {
     cmCTestLog(this->CTest, HANDLER_OUTPUT,
-               std::setw(2 * getNumWidth(this->TotalNumberOfTests) + 8)
+               std::setw(2 * getNumWidth(total) + 8)
                  << "Start "
                  << std::setw(getNumWidth(this->TestHandler->GetMaxIndex()))
                  << this->TestProperties->Index << ": "
diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h
index fed7296..34f23c4 100644
--- a/Source/CTest/cmCTestRunTest.h
+++ b/Source/CTest/cmCTestRunTest.h
@@ -68,7 +68,7 @@
                          size_t completed);
 
   static void StartFailure(std::unique_ptr<cmCTestRunTest> runner,
-                           std::string const& output,
+                           size_t total, std::string const& output,
                            std::string const& detail);
 
   struct EndTestResult
@@ -86,7 +86,8 @@
 
   void ComputeWeightedCost();
 
-  void StartFailure(std::string const& output, std::string const& detail);
+  void StartFailure(size_t total, std::string const& output,
+                    std::string const& detail);
 
   cmCTest* GetCTest() const { return this->CTest; }
 
diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx
index 4c98fdf..14bc510 100644
--- a/Source/CTest/cmCTestSVN.cxx
+++ b/Source/CTest/cmCTestSVN.cxx
@@ -13,7 +13,6 @@
 
 #include "cmCTest.h"
 #include "cmCTestVC.h"
-#include "cmProcessTools.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmXMLParser.h"
@@ -34,7 +33,7 @@
 
 void cmCTestSVN::CleanupImpl()
 {
-  std::vector<const char*> svn_cleanup;
+  std::vector<std::string> svn_cleanup;
   svn_cleanup.push_back("cleanup");
   OutputLogger out(this->Log, "cleanup-out> ");
   OutputLogger err(this->Log, "cleanup-err> ");
@@ -89,9 +88,9 @@
 std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
 {
   // Run "svn info" to get the repository info from the work tree.
-  std::vector<const char*> svn_info;
+  std::vector<std::string> svn_info;
   svn_info.push_back("info");
-  svn_info.push_back(svninfo.LocalPath.c_str());
+  svn_info.push_back(svninfo.LocalPath);
   std::string rev;
   InfoParser out(this, "info-out> ", rev, svninfo);
   OutputLogger err(this->Log, "info-err> ");
@@ -252,26 +251,24 @@
     args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
   }
 
-  std::vector<char const*> svn_update;
+  std::vector<std::string> svn_update;
   svn_update.push_back("update");
-  for (std::string const& arg : args) {
-    svn_update.push_back(arg.c_str());
-  }
+  cm::append(svn_update, args);
 
   UpdateParser out(this, "up-out> ");
   OutputLogger err(this->Log, "up-err> ");
   return this->RunSVNCommand(svn_update, &out, &err);
 }
 
-bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
+bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
                                OutputParser* out, OutputParser* err)
 {
   if (parameters.empty()) {
     return false;
   }
 
-  std::vector<char const*> args;
-  args.push_back(this->CommandLineTool.c_str());
+  std::vector<std::string> args;
+  args.push_back(this->CommandLineTool);
   cm::append(args, parameters);
   args.push_back("--non-interactive");
 
@@ -279,16 +276,12 @@
 
   std::vector<std::string> parsedUserOptions =
     cmSystemTools::ParseArguments(userOptions);
-  for (std::string const& opt : parsedUserOptions) {
-    args.push_back(opt.c_str());
-  }
+  cm::append(args, parsedUserOptions);
 
-  args.push_back(nullptr);
-
-  if (strcmp(parameters[0], "update") == 0) {
-    return this->RunUpdateCommand(args.data(), out, err);
+  if (parameters[0] == "update") {
+    return this->RunUpdateCommand(args, out, err);
   }
-  return this->RunChild(args.data(), out, err);
+  return this->RunChild(args, out, err);
 }
 
 class cmCTestSVN::LogParser
@@ -394,7 +387,7 @@
   }
 
   // Run "svn log" to get all global revisions of interest.
-  std::vector<const char*> svn_log;
+  std::vector<std::string> svn_log;
   svn_log.push_back("log");
   svn_log.push_back("--xml");
   svn_log.push_back("-v");
@@ -473,7 +466,7 @@
 bool cmCTestSVN::LoadModifications()
 {
   // Run "svn status" which reports local modifications.
-  std::vector<const char*> svn_status;
+  std::vector<std::string> svn_status;
   svn_status.push_back("status");
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
@@ -535,7 +528,7 @@
   this->RootInfo = &(this->Repositories.back());
 
   // Run "svn status" to get the list of external repositories
-  std::vector<const char*> svn_status;
+  std::vector<std::string> svn_status;
   svn_status.push_back("status");
   ExternalParser out(this, "external-out> ");
   OutputLogger err(this->Log, "external-err> ");
diff --git a/Source/CTest/cmCTestSVN.h b/Source/CTest/cmCTestSVN.h
index 370d176..1485dc0 100644
--- a/Source/CTest/cmCTestSVN.h
+++ b/Source/CTest/cmCTestSVN.h
@@ -33,7 +33,7 @@
   bool NoteNewRevision() override;
   bool UpdateImpl() override;
 
-  bool RunSVNCommand(std::vector<char const*> const& parameters,
+  bool RunSVNCommand(std::vector<std::string> const& parameters,
                      OutputParser* out, OutputParser* err);
 
   // Information about an SVN repository (root repository or external)
diff --git a/Source/CTest/cmCTestScriptHandler.cxx b/Source/CTest/cmCTestScriptHandler.cxx
index 461ad1a..48f8f6d 100644
--- a/Source/CTest/cmCTestScriptHandler.cxx
+++ b/Source/CTest/cmCTestScriptHandler.cxx
@@ -11,8 +11,9 @@
 
 #include <cm/memory>
 
+#include <cm3p/uv.h>
+
 #include "cmsys/Directory.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestBuildCommand.h"
@@ -40,6 +41,8 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -148,66 +151,65 @@
   // now pass through all the other arguments
   std::vector<std::string>& initArgs =
     this->CTest->GetInitialCommandLineArguments();
-  //*** need to make sure this does not have the current script ***
-  for (size_t i = 1; i < initArgs.size(); ++i) {
-    argv.push_back(initArgs[i].c_str());
-  }
-  argv.push_back(nullptr);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  // cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  // cmsysProcess_SetTimeout(cp, timeout);
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(initArgs)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  auto process = builder.Start();
+  cm::uv_pipe_ptr outPipe;
+  outPipe.init(process.GetLoop(), 0);
+  uv_pipe_open(outPipe, process.OutputStream());
+  cm::uv_pipe_ptr errPipe;
+  errPipe.init(process.GetLoop(), 0);
+  uv_pipe_open(errPipe, process.ErrorStream());
 
   std::vector<char> out;
   std::vector<char> err;
   std::string line;
-  int pipe =
-    cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
-  while (pipe != cmsysProcess_Pipe_None) {
+  auto pipe =
+    cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
+                               std::chrono::seconds(100), out, err);
+  while (pipe != cmSystemTools::WaitForLineResult::None) {
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                "Output: " << line << "\n");
-    if (pipe == cmsysProcess_Pipe_STDERR) {
+    if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
       cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
-    } else if (pipe == cmsysProcess_Pipe_STDOUT) {
+    } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) {
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
     }
-    pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
-                                      err);
+    pipe =
+      cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
+                                 std::chrono::seconds(100), out, err);
   }
 
   // Properly handle output of the build command
-  cmsysProcess_WaitForExit(cp, nullptr);
-  int result = cmsysProcess_GetState(cp);
+  process.Wait();
+  auto const& status = process.GetStatus(0);
+  auto result = status.GetException();
   int retVal = 0;
   bool failed = false;
-  if (result == cmsysProcess_State_Exited) {
-    retVal = cmsysProcess_GetExitValue(cp);
-  } else if (result == cmsysProcess_State_Exception) {
-    retVal = cmsysProcess_GetExitException(cp);
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tThere was an exception: "
-                 << cmsysProcess_GetExceptionString(cp) << " " << retVal
-                 << std::endl);
-    failed = true;
-  } else if (result == cmsysProcess_State_Expired) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tThere was a timeout" << std::endl);
-    failed = true;
-  } else if (result == cmsysProcess_State_Error) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tError executing ctest: " << cmsysProcess_GetErrorString(cp)
-                                           << std::endl);
-    failed = true;
+  switch (result.first) {
+    case cmUVProcessChain::ExceptionCode::None:
+      retVal = static_cast<int>(status.ExitStatus);
+      break;
+    case cmUVProcessChain::ExceptionCode::Spawn:
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "\tError executing ctest: " << result.second << std::endl);
+      failed = true;
+      break;
+    default:
+      retVal = status.TermSignal;
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "\tThere was an exception: " << result.second << " " << retVal
+                                              << std::endl);
+      failed = true;
   }
-  cmsysProcess_Delete(cp);
   if (failed) {
     std::ostringstream message;
     message << "Error running command: [";
-    message << result << "] ";
+    message << static_cast<int>(result.first) << "] ";
     for (const char* arg : argv) {
       if (arg) {
         message << arg << " ";
diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx
index c04f23a..77af889 100644
--- a/Source/CTest/cmCTestSubmitHandler.cxx
+++ b/Source/CTest/cmCTestSubmitHandler.cxx
@@ -285,8 +285,8 @@
       if (cmIsOn(this->GetOption("InternalTest"))) {
         upload_as += "ffffffffffffffffffffffffffffffff";
       } else {
-        upload_as +=
-          cmSystemTools::ComputeFileHash(local_file, cmCryptoHash::AlgoMD5);
+        cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+        upload_as += hasher.HashFile(local_file);
       }
 
       if (!cmSystemTools::FileExists(local_file)) {
@@ -552,8 +552,8 @@
     }
   }
 
-  std::string md5sum =
-    cmSystemTools::ComputeFileHash(file, cmCryptoHash::AlgoMD5);
+  cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+  std::string md5sum = hasher.HashFile(file);
   // 1. request the buildid and check to see if the file
   //    has already been uploaded
   // TODO I added support for subproject. You would need to add
diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx
index 5488388..c717868 100644
--- a/Source/CTest/cmCTestTestCommand.cxx
+++ b/Source/CTest/cmCTestTestCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <chrono>
 #include <cstdlib>
+#include <ratio>
 #include <sstream>
 
 #include <cmext/string_view>
diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx
index 6b02a5e..eb3b4dd 100644
--- a/Source/CTest/cmCTestTestHandler.cxx
+++ b/Source/CTest/cmCTestTestHandler.cxx
@@ -13,6 +13,7 @@
 #include <functional>
 #include <iomanip>
 #include <iterator>
+#include <ratio>
 #include <set>
 #include <sstream>
 #include <utility>
@@ -41,7 +42,6 @@
 #include "cmExecutionStatus.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGlobalGenerator.h"
-#include "cmJSONState.h"
 #include "cmList.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -283,7 +283,6 @@
   this->UseIncludeRegExpFlag = false;
   this->UseExcludeRegExpFlag = false;
   this->UseExcludeRegExpFirst = false;
-  this->UseResourceSpec = false;
 
   this->CustomMaximumPassedTestOutputSize = 1 * 1024;
   this->CustomMaximumFailedTestOutputSize = 300 * 1024;
@@ -890,8 +889,7 @@
   }
 
   if (this->RerunFailed) {
-    this->ComputeTestListForRerunFailed();
-    return true;
+    return this->ComputeTestListForRerunFailed();
   }
 
   cmCTestTestHandler::ListOfTests::size_type tmsize = this->TestList.size();
@@ -950,7 +948,7 @@
   return true;
 }
 
-void cmCTestTestHandler::ComputeTestListForRerunFailed()
+bool cmCTestTestHandler::ComputeTestListForRerunFailed()
 {
   this->ExpandTestsToRunInformationForRerunFailed();
 
@@ -977,6 +975,8 @@
   this->TestList = finalList;
 
   this->UpdateMaxTestNameWidth();
+
+  return true;
 }
 
 void cmCTestTestHandler::UpdateForFixtures(ListOfTests& tests) const
@@ -1350,18 +1350,6 @@
   } else {
     parallel->SetTestLoad(this->CTest->GetTestLoad());
   }
-  if (!this->ResourceSpecFile.empty()) {
-    this->UseResourceSpec = true;
-    if (!this->ResourceSpec.ReadFromJSONFile(this->ResourceSpecFile)) {
-      cmCTestLog(this->CTest, ERROR_MESSAGE,
-                 "Could not read/parse resource spec file "
-                   << this->ResourceSpecFile << ": "
-                   << this->ResourceSpec.parseState.GetErrorMessage()
-                   << std::endl);
-      return false;
-    }
-    parallel->InitResourceAllocator(this->ResourceSpec);
-  }
 
   *this->LogFile
     << "Start testing: " << this->CTest->CurrentTime() << std::endl
@@ -1396,6 +1384,7 @@
     tests[p.Index] = depends;
     properties[p.Index] = &p;
   }
+  parallel->SetResourceSpecFile(this->ResourceSpecFile);
   parallel->SetTests(tests, properties);
   parallel->SetPassFailVectors(&passed, &failed);
   this->TestResults.clear();
@@ -1722,8 +1711,7 @@
   // now look in the paths we specified above
   for (unsigned int ai = 0; ai < attempted.size() && fullPath.empty(); ++ai) {
     // first check without exe extension
-    if (cmSystemTools::FileExists(attempted[ai]) &&
-        !cmSystemTools::FileIsDirectory(attempted[ai])) {
+    if (cmSystemTools::FileExists(attempted[ai], true)) {
       fullPath = cmSystemTools::CollapseFullPath(attempted[ai]);
       resultingConfig = attemptedConfigs[ai];
     }
@@ -1732,8 +1720,7 @@
       failed.push_back(attempted[ai]);
       tempPath =
         cmStrCat(attempted[ai], cmSystemTools::GetExecutableExtension());
-      if (cmSystemTools::FileExists(tempPath) &&
-          !cmSystemTools::FileIsDirectory(tempPath)) {
+      if (cmSystemTools::FileExists(tempPath, true)) {
         fullPath = cmSystemTools::CollapseFullPath(tempPath);
         resultingConfig = attemptedConfigs[ai];
       } else {
@@ -2335,6 +2322,8 @@
             if (!ParseResourceGroupsProperty(val, rt.ResourceGroups)) {
               return false;
             }
+          } else if (key == "GENERATED_RESOURCE_SPEC_FILE"_s) {
+            rt.GeneratedResourceSpecFile = val;
           } else if (key == "SKIP_RETURN_CODE"_s) {
             rt.SkipReturnCode = atoi(val.c_str());
             if (rt.SkipReturnCode < 0 || rt.SkipReturnCode > 255) {
@@ -2617,6 +2606,21 @@
       xml.EndElement(); // </failure>
     }
 
+    xml.StartElement("properties");
+    if ((result.Properties) && (!result.Properties->Labels.empty())) {
+      xml.StartElement("property");
+      xml.Attribute("name", "cmake_labels");
+      // Pass the property as a cmake-formatted list, consumers will know
+      // anyway that this information is coming from cmake, so it should
+      // be ok to put it here as a cmake-list.
+      xml.Attribute("value", cmList::to_string(result.Properties->Labels));
+      // if we export more properties, this should be done the same way,
+      // i.e. prefix the property name with "cmake_", and it it can be
+      // a list, write it cmake-formatted.
+      xml.EndElement(); // </property>
+    }
+    xml.EndElement(); // </properties>
+
     // Note: compressed test output is unconditionally disabled when
     // --output-junit is specified.
     xml.Element("system-out", result.Output);
diff --git a/Source/CTest/cmCTestTestHandler.h b/Source/CTest/cmCTestTestHandler.h
index 315a5b7..23f0a76 100644
--- a/Source/CTest/cmCTestTestHandler.h
+++ b/Source/CTest/cmCTestTestHandler.h
@@ -21,8 +21,7 @@
 
 #include "cmCTest.h"
 #include "cmCTestGenericHandler.h"
-#include "cmCTestResourceSpec.h"
-#include "cmCTestTypes.h"
+#include "cmCTestTypes.h" // IWYU pragma: keep
 #include "cmDuration.h"
 #include "cmListFileCache.h"
 #include "cmValue.h"
@@ -172,6 +171,7 @@
     std::set<std::string> FixturesRequired;
     std::set<std::string> RequireSuccessDepends;
     std::vector<std::vector<cmCTestTestResourceRequirement>> ResourceGroups;
+    std::string GeneratedResourceSpecFile;
     // Private test generator properties used to track backtraces
     cmListFileBacktrace Backtrace;
   };
@@ -319,7 +319,7 @@
 
   // compute the lists of tests that will actually run
   // based on LastTestFailed.log
-  void ComputeTestListForRerunFailed();
+  bool ComputeTestListForRerunFailed();
 
   // add required setup/cleanup tests not already in the
   // list of tests to be run and update dependencies between
@@ -360,8 +360,6 @@
   cmsys::RegularExpression IncludeTestsRegularExpression;
   cmsys::RegularExpression ExcludeTestsRegularExpression;
 
-  bool UseResourceSpec;
-  cmCTestResourceSpec ResourceSpec;
   std::string ResourceSpecFile;
 
   void RecordCustomTestMeasurements(cmXMLWriter& xml, std::string content);
diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx
index 609ccba..cbbb5a5 100644
--- a/Source/CTest/cmCTestVC.cxx
+++ b/Source/CTest/cmCTestVC.cxx
@@ -7,10 +7,9 @@
 #include <sstream>
 #include <vector>
 
-#include "cmsys/Process.h"
-
 #include "cmCTest.h"
 #include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
@@ -55,18 +54,12 @@
 
   // Construct the initial checkout command line.
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
-  std::vector<char const*> vc_co;
-  vc_co.reserve(args.size() + 1);
-  for (std::string const& arg : args) {
-    vc_co.push_back(arg.c_str());
-  }
-  vc_co.push_back(nullptr);
 
   // Run the initial checkout command and log its output.
   this->Log << "--- Begin Initial Checkout ---\n";
   OutputLogger out(this->Log, "co-out> ");
   OutputLogger err(this->Log, "co-err> ");
-  bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str());
+  bool result = this->RunChild(args, &out, &err, parent);
   this->Log << "--- End Initial Checkout ---\n";
   if (!result) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
@@ -75,35 +68,35 @@
   return result;
 }
 
-bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out,
-                         OutputParser* err, const char* workDir,
-                         Encoding encoding)
+bool cmCTestVC::RunChild(const std::vector<std::string>& cmd,
+                         OutputParser* out, OutputParser* err,
+                         std::string workDir, Encoding encoding)
 {
   this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n";
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, cmd);
-  workDir = workDir ? workDir : this->SourceDirectory.c_str();
-  cmsysProcess_SetWorkingDirectory(cp, workDir);
-  cmCTestVC::RunProcess(cp, out, err, encoding);
-  int result = cmsysProcess_GetExitValue(cp);
-  cmsysProcess_Delete(cp);
-  return result == 0;
+  cmUVProcessChainBuilder builder;
+  if (workDir.empty()) {
+    workDir = this->SourceDirectory;
+  }
+  builder.AddCommand(cmd).SetWorkingDirectory(workDir);
+  auto status = cmCTestVC::RunProcess(builder, out, err, encoding);
+  return status.front().SpawnResult == 0 && status.front().ExitStatus == 0;
 }
 
-std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
+std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd)
 {
   std::ostringstream line;
   const char* sep = "";
-  for (const char* const* arg = cmd; *arg; ++arg) {
-    line << sep << "\"" << *arg << "\"";
+  for (auto const& arg : cmd) {
+    line << sep << "\"" << arg << "\"";
     sep = " ";
   }
   return line.str();
 }
 
-bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
-                                 OutputParser* err, Encoding encoding)
+bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
+                                 OutputParser* out, OutputParser* err,
+                                 Encoding encoding)
 {
   // Report the command line.
   this->UpdateCommandLine = this->ComputeCommandLine(cmd);
@@ -113,7 +106,7 @@
   }
 
   // Run the command.
-  return this->RunChild(cmd, out, err, nullptr, encoding);
+  return this->RunChild(cmd, out, err, "", encoding);
 }
 
 std::string cmCTestVC::GetNightlyTime()
diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h
index 7b03d10..dd5456d 100644
--- a/Source/CTest/cmCTestVC.h
+++ b/Source/CTest/cmCTestVC.h
@@ -6,6 +6,7 @@
 
 #include <iosfwd>
 #include <string>
+#include <vector>
 
 #include "cmProcessOutput.h"
 #include "cmProcessTools.h"
@@ -108,15 +109,15 @@
   };
 
   /** Convert a list of arguments to a human-readable command line.  */
-  static std::string ComputeCommandLine(char const* const* cmd);
+  static std::string ComputeCommandLine(const std::vector<std::string>& cmd);
 
   /** Run a command line and send output to given parsers.  */
-  bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err,
-                const char* workDir = nullptr,
+  bool RunChild(const std::vector<std::string>& cmd, OutputParser* out,
+                OutputParser* err, std::string workDir = {},
                 Encoding encoding = cmProcessOutput::Auto);
 
   /** Run VC update command line and send output to given parsers.  */
-  bool RunUpdateCommand(char const* const* cmd, OutputParser* out,
+  bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out,
                         OutputParser* err = nullptr,
                         Encoding encoding = cmProcessOutput::Auto);
 
diff --git a/Source/CTest/cmParseGTMCoverage.cxx b/Source/CTest/cmParseGTMCoverage.cxx
index 14417cc..c63b496 100644
--- a/Source/CTest/cmParseGTMCoverage.cxx
+++ b/Source/CTest/cmParseGTMCoverage.cxx
@@ -2,7 +2,6 @@
 
 #include <cstdio>
 #include <cstdlib>
-#include <map>
 #include <vector>
 
 #include "cmsys/Directory.hxx"
diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx
index 269b92c..2c809b6 100644
--- a/Source/CTest/cmProcess.cxx
+++ b/Source/CTest/cmProcess.cxx
@@ -4,6 +4,7 @@
 
 #include <csignal>
 #include <iostream>
+#include <ratio>
 #include <string>
 #include <utility>
 
diff --git a/Source/Checks/Curses/CMakeLists.txt b/Source/Checks/Curses/CMakeLists.txt
index 0fee7ac..bc6b906 100644
--- a/Source/Checks/Curses/CMakeLists.txt
+++ b/Source/Checks/Curses/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.13...3.24 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
 project(CheckCurses C)
 
 set(CURSES_NEED_NCURSES TRUE)
diff --git a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
index fc5450e..0b104ea 100644
--- a/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
+++ b/Source/CursesDialog/cmCursesCacheEntryComposite.cxx
@@ -4,6 +4,7 @@
 
 #include <cassert>
 #include <utility>
+#include <vector>
 
 #include <cm/memory>
 
diff --git a/Source/CursesDialog/cmCursesMainForm.cxx b/Source/CursesDialog/cmCursesMainForm.cxx
index a1b2149..77a0048 100644
--- a/Source/CursesDialog/cmCursesMainForm.cxx
+++ b/Source/CursesDialog/cmCursesMainForm.cxx
@@ -458,6 +458,14 @@
   }
 }
 
+void cmCursesMainForm::Write()
+{
+  this->FillCacheManagerFromUI();
+  this->CMakeInstance->SaveCache(
+    this->CMakeInstance->GetHomeOutputDirectory());
+  this->LoadCache(nullptr);
+}
+
 int cmCursesMainForm::Configure(int noconfigure)
 {
   this->ResetOutputs();
@@ -471,10 +479,7 @@
   }
 
   // always save the current gui values to disk
-  this->FillCacheManagerFromUI();
-  this->CMakeInstance->SaveCache(
-    this->CMakeInstance->GetHomeOutputDirectory());
-  this->LoadCache(nullptr);
+  this->Write();
 
   // run the generate process
   this->OkToGenerate = true;
@@ -794,6 +799,21 @@
       else if (key == KEY_PPAGE || key == ctrl('u')) {
         form_driver(this->Form, REQ_PREV_PAGE);
       }
+      // first entry
+      else if (key == KEY_HOME) {
+        form_driver(this->Form, REQ_FIRST_PAGE);
+        form_driver(this->Form, REQ_FIRST_FIELD);
+      }
+      // last entry
+      else if (key == KEY_END) {
+        form_driver(this->Form, REQ_LAST_PAGE);
+        form_driver(this->Form, REQ_LAST_FIELD);
+      }
+      // write and quit
+      else if (key == 'w') {
+        this->Write();
+        break;
+      }
       // configure
       else if (key == 'c') {
         this->Configure();
@@ -854,6 +874,10 @@
         if (!this->OldSearchString.empty()) {
           this->JumpToCacheEntry(this->OldSearchString.c_str());
         }
+      } else if (key == 'N') {
+        if (!this->OldSearchString.empty()) {
+          this->JumpToCacheEntry(this->OldSearchString.c_str(), true);
+        }
       }
       // switch advanced on/off
       else if (key == 't') {
@@ -945,6 +969,11 @@
 
 void cmCursesMainForm::JumpToCacheEntry(const char* astr)
 {
+  this->JumpToCacheEntry(astr, false);
+}
+
+void cmCursesMainForm::JumpToCacheEntry(const char* astr, bool reverse)
+{
   std::string str;
   if (astr) {
     str = cmSystemTools::LowerCase(astr);
@@ -973,12 +1002,21 @@
         }
       }
     }
-    if (static_cast<size_t>(findex) >= 3 * this->NumberOfVisibleEntries - 1) {
-      set_current_field(this->Form, this->Fields[2]);
-    } else if (new_page(this->Fields[findex + 1])) {
-      form_driver(this->Form, REQ_NEXT_PAGE);
+    if (!reverse &&
+        static_cast<size_t>(findex) >= 3 * this->NumberOfVisibleEntries - 1) {
+      form_driver(this->Form, REQ_FIRST_PAGE);
+      form_driver(this->Form, REQ_FIRST_FIELD);
+    } else if (reverse && static_cast<size_t>(findex) < 3) {
+      form_driver(this->Form, REQ_LAST_PAGE);
+      form_driver(this->Form, REQ_LAST_FIELD);
+    } else if (this->Fields[findex + (reverse ? -3 : 1)]->page !=
+               this->Fields[findex]->page) {
+      form_driver(this->Form, reverse ? REQ_PREV_PAGE : REQ_NEXT_PAGE);
+      if (reverse) {
+        form_driver(this->Form, REQ_LAST_FIELD);
+      }
     } else {
-      form_driver(this->Form, REQ_NEXT_FIELD);
+      form_driver(this->Form, reverse ? REQ_PREV_FIELD : REQ_NEXT_FIELD);
     }
     cur = current_field(this->Form);
     findex = field_index(cur);
@@ -1040,15 +1078,21 @@
   "hit 'g' to have CMake generate all the build files (i.e. makefiles or "
   "project files) and exit. "
   "At any point during the process, you can exit ccmake with 'q'. However, "
-  "this will not generate/change any build files.\n\n"
+  "this will not generate/change any build files. Additionally, you can exit "
+  "ccmake with 'w' to write changes to the cache file without generating or "
+  "changing the build files.\n\n"
   "ccmake KEYS:\n\n"
   "Navigation: "
   "You can use the arrow keys and page up, down to navigate the options. "
-  "Alternatively, you can use the following keys: \n"
+  "Additionally, you can use the following keys: \n"
   " C-n or j : next option\n"
   " C-p or k : previous options\n"
   " C-d : down one page\n"
-  " C-u : up one page\n\n"
+  " C-u : up one page\n"
+  " Home : jump to first option\n"
+  " End : jump to last option\n"
+  " n : next search result\n"
+  " N : previous search result\n\n"
   "Editing options: "
   "To change an option  press enter or return. If the current options is a "
   "boolean, this will toggle its value. "
diff --git a/Source/CursesDialog/cmCursesMainForm.h b/Source/CursesDialog/cmCursesMainForm.h
index 112b7e8..1ce75e7 100644
--- a/Source/CursesDialog/cmCursesMainForm.h
+++ b/Source/CursesDialog/cmCursesMainForm.h
@@ -87,6 +87,11 @@
   void AddError(const std::string& message, const char* title) override;
 
   /**
+   * Write files to cache file without reconfiguring.
+   */
+  void Write();
+
+  /**
    * Used to do a configure. If argument is specified, it does only the check
    * and not configure.
    */
@@ -123,6 +128,7 @@
 
   // Jump to the cache entry whose name matches the string.
   void JumpToCacheEntry(const char* str);
+  void JumpToCacheEntry(const char* str, bool reverse);
 
   // Clear and reset the output log and state
   void ResetOutputs();
diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx
index 85b379b..f1d351a 100644
--- a/Source/LexerParser/cmCTestResourceGroupsLexer.cxx
+++ b/Source/LexerParser/cmCTestResourceGroupsLexer.cxx
@@ -667,6 +667,10 @@
 
 #include <cstddef>
 
+#ifndef _WIN32
+#  include <termios.h>
+#endif
+
 /*--------------------------------------------------------------------------*/
 
 #define INITIAL 0
diff --git a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l
index 2befa85..ac9cbaf 100644
--- a/Source/LexerParser/cmCTestResourceGroupsLexer.in.l
+++ b/Source/LexerParser/cmCTestResourceGroupsLexer.in.l
@@ -26,6 +26,10 @@
 
 #include <cstddef>
 
+#ifndef _WIN32
+#  include <termios.h>
+#endif
+
 /*--------------------------------------------------------------------------*/
 %}
 
diff --git a/Source/LexerParser/cmCommandArgumentParser.cxx b/Source/LexerParser/cmCommandArgumentParser.cxx
index 4c49e0f..7ea0b15 100644
--- a/Source/LexerParser/cmCommandArgumentParser.cxx
+++ b/Source/LexerParser/cmCommandArgumentParser.cxx
@@ -97,7 +97,6 @@
 # include <malloc.h>
 #endif
 
-#include <stdint.h>
 /* Make sure the parser uses standard memory allocation.  The default
    generated parser malloc/free declarations do not work on all
    platforms.  */
@@ -137,7 +136,7 @@
 # endif
 #endif
 
-#line 141 "cmCommandArgumentParser.cxx"
+#line 140 "cmCommandArgumentParser.cxx"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -576,9 +575,9 @@
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint8 yyrline[] =
 {
-       0,   102,   102,   108,   111,   116,   119,   124,   127,   132,
-     135,   138,   141,   144,   147,   152,   155,   158,   161,   166,
-     169,   174,   177,   182,   185
+       0,   101,   101,   107,   110,   115,   118,   123,   126,   131,
+     134,   137,   140,   143,   146,   151,   154,   157,   160,   165,
+     168,   173,   176,   181,   184
 };
 #endif
 
@@ -1437,192 +1436,192 @@
   switch (yyn)
     {
   case 2: /* Start: GoalWithOptionalBackSlash  */
-#line 102 "cmCommandArgumentParser.y"
+#line 101 "cmCommandArgumentParser.y"
                             {
     (yyval.str) = 0;
     yyGetParser->SetResult((yyvsp[0].str));
   }
-#line 1446 "cmCommandArgumentParser.cxx"
+#line 1445 "cmCommandArgumentParser.cxx"
     break;
 
   case 3: /* GoalWithOptionalBackSlash: Goal  */
-#line 108 "cmCommandArgumentParser.y"
+#line 107 "cmCommandArgumentParser.y"
        {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1454 "cmCommandArgumentParser.cxx"
+#line 1453 "cmCommandArgumentParser.cxx"
     break;
 
   case 4: /* GoalWithOptionalBackSlash: Goal "\\"  */
-#line 111 "cmCommandArgumentParser.y"
+#line 110 "cmCommandArgumentParser.y"
                   {
     (yyval.str) = yyGetParser->CombineUnions((yyvsp[-1].str), (yyvsp[0].str));
   }
-#line 1462 "cmCommandArgumentParser.cxx"
+#line 1461 "cmCommandArgumentParser.cxx"
     break;
 
   case 5: /* Goal: %empty  */
-#line 116 "cmCommandArgumentParser.y"
+#line 115 "cmCommandArgumentParser.y"
   {
     (yyval.str) = 0;
   }
-#line 1470 "cmCommandArgumentParser.cxx"
+#line 1469 "cmCommandArgumentParser.cxx"
     break;
 
   case 6: /* Goal: String Goal  */
-#line 119 "cmCommandArgumentParser.y"
+#line 118 "cmCommandArgumentParser.y"
               {
     (yyval.str) = yyGetParser->CombineUnions((yyvsp[-1].str), (yyvsp[0].str));
   }
-#line 1478 "cmCommandArgumentParser.cxx"
+#line 1477 "cmCommandArgumentParser.cxx"
     break;
 
   case 7: /* String: OuterText  */
-#line 124 "cmCommandArgumentParser.y"
+#line 123 "cmCommandArgumentParser.y"
             {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1486 "cmCommandArgumentParser.cxx"
+#line 1485 "cmCommandArgumentParser.cxx"
     break;
 
   case 8: /* String: Variable  */
-#line 127 "cmCommandArgumentParser.y"
+#line 126 "cmCommandArgumentParser.y"
            {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1494 "cmCommandArgumentParser.cxx"
+#line 1493 "cmCommandArgumentParser.cxx"
     break;
 
   case 9: /* OuterText: cal_NAME  */
-#line 132 "cmCommandArgumentParser.y"
+#line 131 "cmCommandArgumentParser.y"
            {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1502 "cmCommandArgumentParser.cxx"
+#line 1501 "cmCommandArgumentParser.cxx"
     break;
 
   case 10: /* OuterText: "@"  */
-#line 135 "cmCommandArgumentParser.y"
+#line 134 "cmCommandArgumentParser.y"
          {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1510 "cmCommandArgumentParser.cxx"
+#line 1509 "cmCommandArgumentParser.cxx"
     break;
 
   case 11: /* OuterText: "$"  */
-#line 138 "cmCommandArgumentParser.y"
+#line 137 "cmCommandArgumentParser.y"
              {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1518 "cmCommandArgumentParser.cxx"
+#line 1517 "cmCommandArgumentParser.cxx"
     break;
 
   case 12: /* OuterText: "{"  */
-#line 141 "cmCommandArgumentParser.y"
+#line 140 "cmCommandArgumentParser.y"
              {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1526 "cmCommandArgumentParser.cxx"
+#line 1525 "cmCommandArgumentParser.cxx"
     break;
 
   case 13: /* OuterText: "}"  */
-#line 144 "cmCommandArgumentParser.y"
+#line 143 "cmCommandArgumentParser.y"
              {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1534 "cmCommandArgumentParser.cxx"
+#line 1533 "cmCommandArgumentParser.cxx"
     break;
 
   case 14: /* OuterText: cal_SYMBOL  */
-#line 147 "cmCommandArgumentParser.y"
+#line 146 "cmCommandArgumentParser.y"
              {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1542 "cmCommandArgumentParser.cxx"
+#line 1541 "cmCommandArgumentParser.cxx"
     break;
 
   case 15: /* Variable: cal_ENVCURLY EnvVarName "}"  */
-#line 152 "cmCommandArgumentParser.y"
+#line 151 "cmCommandArgumentParser.y"
                                      {
     (yyval.str) = yyGetParser->ExpandSpecialVariable((yyvsp[-2].str), (yyvsp[-1].str));
   }
-#line 1550 "cmCommandArgumentParser.cxx"
+#line 1549 "cmCommandArgumentParser.cxx"
     break;
 
   case 16: /* Variable: cal_NCURLY MultipleIds "}"  */
-#line 155 "cmCommandArgumentParser.y"
+#line 154 "cmCommandArgumentParser.y"
                                     {
     (yyval.str) = yyGetParser->ExpandSpecialVariable((yyvsp[-2].str), (yyvsp[-1].str));
   }
-#line 1558 "cmCommandArgumentParser.cxx"
+#line 1557 "cmCommandArgumentParser.cxx"
     break;
 
   case 17: /* Variable: cal_DCURLY MultipleIds "}"  */
-#line 158 "cmCommandArgumentParser.y"
+#line 157 "cmCommandArgumentParser.y"
                                     {
     (yyval.str) = yyGetParser->ExpandVariable((yyvsp[-1].str));
   }
-#line 1566 "cmCommandArgumentParser.cxx"
+#line 1565 "cmCommandArgumentParser.cxx"
     break;
 
   case 18: /* Variable: cal_ATNAME  */
-#line 161 "cmCommandArgumentParser.y"
+#line 160 "cmCommandArgumentParser.y"
              {
     (yyval.str) = yyGetParser->ExpandVariableForAt((yyvsp[0].str));
   }
-#line 1574 "cmCommandArgumentParser.cxx"
+#line 1573 "cmCommandArgumentParser.cxx"
     break;
 
   case 19: /* EnvVarName: MultipleIds  */
-#line 166 "cmCommandArgumentParser.y"
+#line 165 "cmCommandArgumentParser.y"
               {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1582 "cmCommandArgumentParser.cxx"
+#line 1581 "cmCommandArgumentParser.cxx"
     break;
 
   case 20: /* EnvVarName: cal_SYMBOL EnvVarName  */
-#line 169 "cmCommandArgumentParser.y"
+#line 168 "cmCommandArgumentParser.y"
                         {
     (yyval.str) = (yyvsp[-1].str);
   }
-#line 1590 "cmCommandArgumentParser.cxx"
+#line 1589 "cmCommandArgumentParser.cxx"
     break;
 
   case 21: /* MultipleIds: %empty  */
-#line 174 "cmCommandArgumentParser.y"
+#line 173 "cmCommandArgumentParser.y"
   {
     (yyval.str) = 0;
   }
-#line 1598 "cmCommandArgumentParser.cxx"
+#line 1597 "cmCommandArgumentParser.cxx"
     break;
 
   case 22: /* MultipleIds: ID MultipleIds  */
-#line 177 "cmCommandArgumentParser.y"
+#line 176 "cmCommandArgumentParser.y"
                  {
     (yyval.str) = yyGetParser->CombineUnions((yyvsp[-1].str), (yyvsp[0].str));
   }
-#line 1606 "cmCommandArgumentParser.cxx"
+#line 1605 "cmCommandArgumentParser.cxx"
     break;
 
   case 23: /* ID: cal_NAME  */
-#line 182 "cmCommandArgumentParser.y"
+#line 181 "cmCommandArgumentParser.y"
            {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1614 "cmCommandArgumentParser.cxx"
+#line 1613 "cmCommandArgumentParser.cxx"
     break;
 
   case 24: /* ID: Variable  */
-#line 185 "cmCommandArgumentParser.y"
+#line 184 "cmCommandArgumentParser.y"
            {
     (yyval.str) = (yyvsp[0].str);
   }
-#line 1622 "cmCommandArgumentParser.cxx"
+#line 1621 "cmCommandArgumentParser.cxx"
     break;
 
 
-#line 1626 "cmCommandArgumentParser.cxx"
+#line 1625 "cmCommandArgumentParser.cxx"
 
       default: break;
     }
@@ -1846,7 +1845,7 @@
   return yyresult;
 }
 
-#line 190 "cmCommandArgumentParser.y"
+#line 189 "cmCommandArgumentParser.y"
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmCommandArgumentParser.y b/Source/LexerParser/cmCommandArgumentParser.y
index 602e1c3..59f4b32 100644
--- a/Source/LexerParser/cmCommandArgumentParser.y
+++ b/Source/LexerParser/cmCommandArgumentParser.y
@@ -22,7 +22,6 @@
 # include <malloc.h>
 #endif
 
-#include <stdint.h>
 /* Make sure the parser uses standard memory allocation.  The default
    generated parser malloc/free declarations do not work on all
    platforms.  */
diff --git a/Source/LexerParser/cmDependsJavaParser.cxx b/Source/LexerParser/cmDependsJavaParser.cxx
index 59cf1be..6755c70 100644
--- a/Source/LexerParser/cmDependsJavaParser.cxx
+++ b/Source/LexerParser/cmDependsJavaParser.cxx
@@ -88,7 +88,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string>
@@ -126,7 +125,7 @@
 # endif
 #endif
 
-#line 130 "cmDependsJavaParser.cxx"
+#line 129 "cmDependsJavaParser.cxx"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -814,42 +813,42 @@
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_int16 yyrline[] =
 {
-       0,   185,   185,   194,   202,   210,   218,   226,   234,   243,
-     251,   260,   268,   277,   282,   287,   292,   297,   302,   307,
-     312,   318,   326,   335,   345,   354,   363,   371,   381,   387,
-     394,   401,   407,   414,   423,   433,   443,   452,   460,   469,
-     478,   484,   493,   499,   508,   514,   523,   535,   543,   552,
-     564,   577,   585,   593,   602,   610,   619,   619,   619,   620,
-     621,   621,   621,   621,   621,   621,   622,   625,   635,   644,
-     653,   662,   672,   678,   687,   696,   705,   713,   722,   731,
-     737,   746,   754,   762,   770,   779,   787,   796,   802,   810,
-     819,   827,   836,   845,   854,   862,   871,   879,   887,   896,
-     905,   915,   922,   932,   942,   949,   956,   959,   965,   975,
-     985,   995,  1001,  1011,  1021,  1031,  1040,  1050,  1061,  1071,
-    1078,  1088,  1097,  1107,  1116,  1126,  1132,  1142,  1151,  1161,
-    1171,  1178,  1187,  1196,  1205,  1214,  1222,  1231,  1240,  1250,
-    1260,  1269,  1279,  1289,  1296,  1305,  1315,  1324,  1334,  1343,
-    1350,  1360,  1369,  1379,  1388,  1397,  1407,  1417,  1426,  1436,
-    1445,  1454,  1463,  1472,  1481,  1491,  1500,  1509,  1518,  1527,
-    1537,  1546,  1555,  1564,  1573,  1582,  1591,  1600,  1609,  1618,
-    1627,  1636,  1646,  1656,  1667,  1677,  1687,  1696,  1705,  1714,
-    1723,  1732,  1741,  1751,  1761,  1771,  1781,  1788,  1795,  1802,
-    1812,  1819,  1829,  1839,  1848,  1858,  1867,  1877,  1884,  1891,
-    1898,  1906,  1913,  1923,  1930,  1940,  1950,  1957,  1967,  1976,
-    1986,  1996,  2005,  2015,  2024,  2034,  2045,  2052,  2059,  2070,
-    2080,  2090,  2100,  2109,  2119,  2126,  2136,  2145,  2155,  2162,
-    2172,  2181,  2191,  2200,  2206,  2215,  2224,  2233,  2242,  2252,
-    2262,  2269,  2279,  2286,  2296,  2305,  2315,  2324,  2333,  2342,
-    2352,  2359,  2369,  2378,  2388,  2398,  2404,  2411,  2421,  2431,
-    2441,  2452,  2462,  2473,  2483,  2494,  2504,  2514,  2523,  2532,
-    2541,  2550,  2560,  2570,  2580,  2589,  2598,  2607,  2616,  2626,
-    2636,  2646,  2655,  2664,  2673,  2683,  2692,  2701,  2708,  2717,
-    2726,  2735,  2745,  2754,  2763,  2773,  2782,  2791,  2800,  2810,
-    2819,  2828,  2837,  2846,  2855,  2865,  2874,  2883,  2893,  2902,
-    2912,  2921,  2931,  2940,  2950,  2959,  2969,  2978,  2988,  2997,
-    3007,  3016,  3026,  3036,  3046,  3055,  3065,  3074,  3083,  3092,
-    3101,  3110,  3119,  3128,  3137,  3146,  3155,  3164,  3174,  3184,
-    3194,  3203
+       0,   184,   184,   193,   201,   209,   217,   225,   233,   242,
+     250,   259,   267,   276,   281,   286,   291,   296,   301,   306,
+     311,   317,   325,   334,   344,   353,   362,   370,   380,   386,
+     393,   400,   406,   413,   422,   432,   442,   451,   459,   468,
+     477,   483,   492,   498,   507,   513,   522,   534,   542,   551,
+     563,   576,   584,   592,   601,   609,   618,   618,   618,   619,
+     620,   620,   620,   620,   620,   620,   621,   624,   634,   643,
+     652,   661,   671,   677,   686,   695,   704,   712,   721,   730,
+     736,   745,   753,   761,   769,   778,   786,   795,   801,   809,
+     818,   826,   835,   844,   853,   861,   870,   878,   886,   895,
+     904,   914,   921,   931,   941,   948,   955,   958,   964,   974,
+     984,   994,  1000,  1010,  1020,  1030,  1039,  1049,  1060,  1070,
+    1077,  1087,  1096,  1106,  1115,  1125,  1131,  1141,  1150,  1160,
+    1170,  1177,  1186,  1195,  1204,  1213,  1221,  1230,  1239,  1249,
+    1259,  1268,  1278,  1288,  1295,  1304,  1314,  1323,  1333,  1342,
+    1349,  1359,  1368,  1378,  1387,  1396,  1406,  1416,  1425,  1435,
+    1444,  1453,  1462,  1471,  1480,  1490,  1499,  1508,  1517,  1526,
+    1536,  1545,  1554,  1563,  1572,  1581,  1590,  1599,  1608,  1617,
+    1626,  1635,  1645,  1655,  1666,  1676,  1686,  1695,  1704,  1713,
+    1722,  1731,  1740,  1750,  1760,  1770,  1780,  1787,  1794,  1801,
+    1811,  1818,  1828,  1838,  1847,  1857,  1866,  1876,  1883,  1890,
+    1897,  1905,  1912,  1922,  1929,  1939,  1949,  1956,  1966,  1975,
+    1985,  1995,  2004,  2014,  2023,  2033,  2044,  2051,  2058,  2069,
+    2079,  2089,  2099,  2108,  2118,  2125,  2135,  2144,  2154,  2161,
+    2171,  2180,  2190,  2199,  2205,  2214,  2223,  2232,  2241,  2251,
+    2261,  2268,  2278,  2285,  2295,  2304,  2314,  2323,  2332,  2341,
+    2351,  2358,  2368,  2377,  2387,  2397,  2403,  2410,  2420,  2430,
+    2440,  2451,  2461,  2472,  2482,  2493,  2503,  2513,  2522,  2531,
+    2540,  2549,  2559,  2569,  2579,  2588,  2597,  2606,  2615,  2625,
+    2635,  2645,  2654,  2663,  2672,  2682,  2691,  2700,  2707,  2716,
+    2725,  2734,  2744,  2753,  2762,  2772,  2781,  2790,  2799,  2809,
+    2818,  2827,  2836,  2845,  2854,  2864,  2873,  2882,  2892,  2901,
+    2911,  2920,  2930,  2939,  2949,  2958,  2968,  2977,  2987,  2996,
+    3006,  3015,  3025,  3035,  3045,  3054,  3064,  3073,  3082,  3091,
+    3100,  3109,  3118,  3127,  3136,  3145,  3154,  3163,  3173,  3183,
+    3193,  3202
 };
 #endif
 
@@ -2470,214 +2469,214 @@
   switch (yyn)
     {
   case 2: /* Goal: CompilationUnit  */
-#line 186 "cmDependsJavaParser.y"
+#line 185 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2481 "cmDependsJavaParser.cxx"
+#line 2480 "cmDependsJavaParser.cxx"
     break;
 
   case 3: /* Literal: IntegerLiteral  */
-#line 195 "cmDependsJavaParser.y"
+#line 194 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2492 "cmDependsJavaParser.cxx"
+#line 2491 "cmDependsJavaParser.cxx"
     break;
 
   case 4: /* Literal: jp_FLOATINGPOINTLITERAL  */
-#line 203 "cmDependsJavaParser.y"
+#line 202 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2503 "cmDependsJavaParser.cxx"
+#line 2502 "cmDependsJavaParser.cxx"
     break;
 
   case 5: /* Literal: jp_BOOLEANLITERAL  */
-#line 211 "cmDependsJavaParser.y"
+#line 210 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2514 "cmDependsJavaParser.cxx"
+#line 2513 "cmDependsJavaParser.cxx"
     break;
 
   case 6: /* Literal: jp_CHARACTERLITERAL  */
-#line 219 "cmDependsJavaParser.y"
+#line 218 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2525 "cmDependsJavaParser.cxx"
+#line 2524 "cmDependsJavaParser.cxx"
     break;
 
   case 7: /* Literal: jp_STRINGLITERAL  */
-#line 227 "cmDependsJavaParser.y"
+#line 226 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2536 "cmDependsJavaParser.cxx"
+#line 2535 "cmDependsJavaParser.cxx"
     break;
 
   case 8: /* Literal: jp_NULLLITERAL  */
-#line 235 "cmDependsJavaParser.y"
+#line 234 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2547 "cmDependsJavaParser.cxx"
+#line 2546 "cmDependsJavaParser.cxx"
     break;
 
   case 9: /* IntegerLiteral: jp_DECIMALINTEGERLITERAL  */
-#line 244 "cmDependsJavaParser.y"
+#line 243 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2558 "cmDependsJavaParser.cxx"
+#line 2557 "cmDependsJavaParser.cxx"
     break;
 
   case 10: /* IntegerLiteral: jp_HEXINTEGERLITERAL  */
-#line 252 "cmDependsJavaParser.y"
+#line 251 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2569 "cmDependsJavaParser.cxx"
+#line 2568 "cmDependsJavaParser.cxx"
     break;
 
   case 11: /* Type: PrimitiveType  */
-#line 261 "cmDependsJavaParser.y"
+#line 260 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2580 "cmDependsJavaParser.cxx"
+#line 2579 "cmDependsJavaParser.cxx"
     break;
 
   case 12: /* Type: ReferenceType  */
-#line 269 "cmDependsJavaParser.y"
+#line 268 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2591 "cmDependsJavaParser.cxx"
+#line 2590 "cmDependsJavaParser.cxx"
     break;
 
   case 13: /* PrimitiveType: jp_BYTE_TYPE  */
-#line 278 "cmDependsJavaParser.y"
+#line 277 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2599 "cmDependsJavaParser.cxx"
+#line 2598 "cmDependsJavaParser.cxx"
     break;
 
   case 14: /* PrimitiveType: jp_SHORT_TYPE  */
-#line 283 "cmDependsJavaParser.y"
+#line 282 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2607 "cmDependsJavaParser.cxx"
+#line 2606 "cmDependsJavaParser.cxx"
     break;
 
   case 15: /* PrimitiveType: jp_INT_TYPE  */
-#line 288 "cmDependsJavaParser.y"
+#line 287 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2615 "cmDependsJavaParser.cxx"
+#line 2614 "cmDependsJavaParser.cxx"
     break;
 
   case 16: /* PrimitiveType: jp_LONG_TYPE  */
-#line 293 "cmDependsJavaParser.y"
+#line 292 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2623 "cmDependsJavaParser.cxx"
+#line 2622 "cmDependsJavaParser.cxx"
     break;
 
   case 17: /* PrimitiveType: jp_CHAR_TYPE  */
-#line 298 "cmDependsJavaParser.y"
+#line 297 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2631 "cmDependsJavaParser.cxx"
+#line 2630 "cmDependsJavaParser.cxx"
     break;
 
   case 18: /* PrimitiveType: jp_FLOAT_TYPE  */
-#line 303 "cmDependsJavaParser.y"
+#line 302 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2639 "cmDependsJavaParser.cxx"
+#line 2638 "cmDependsJavaParser.cxx"
     break;
 
   case 19: /* PrimitiveType: jp_DOUBLE_TYPE  */
-#line 308 "cmDependsJavaParser.y"
+#line 307 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2647 "cmDependsJavaParser.cxx"
+#line 2646 "cmDependsJavaParser.cxx"
     break;
 
   case 20: /* PrimitiveType: jp_BOOLEAN_TYPE  */
-#line 313 "cmDependsJavaParser.y"
+#line 312 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
 }
-#line 2655 "cmDependsJavaParser.cxx"
+#line 2654 "cmDependsJavaParser.cxx"
     break;
 
   case 21: /* ReferenceType: ClassOrInterfaceType  */
-#line 319 "cmDependsJavaParser.y"
+#line 318 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2666 "cmDependsJavaParser.cxx"
+#line 2665 "cmDependsJavaParser.cxx"
     break;
 
   case 22: /* ReferenceType: ArrayType  */
-#line 327 "cmDependsJavaParser.y"
+#line 326 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2677 "cmDependsJavaParser.cxx"
+#line 2676 "cmDependsJavaParser.cxx"
     break;
 
   case 23: /* ClassOrInterfaceType: Name  */
-#line 336 "cmDependsJavaParser.y"
+#line 335 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpStoreClass((yyvsp[0].str));
@@ -2685,44 +2684,44 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2689 "cmDependsJavaParser.cxx"
+#line 2688 "cmDependsJavaParser.cxx"
     break;
 
   case 24: /* ClassType: ClassOrInterfaceType  */
-#line 346 "cmDependsJavaParser.y"
+#line 345 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2700 "cmDependsJavaParser.cxx"
+#line 2699 "cmDependsJavaParser.cxx"
     break;
 
   case 25: /* InterfaceType: ClassOrInterfaceType  */
-#line 355 "cmDependsJavaParser.y"
+#line 354 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2711 "cmDependsJavaParser.cxx"
+#line 2710 "cmDependsJavaParser.cxx"
     break;
 
   case 26: /* ArrayType: PrimitiveType Dims  */
-#line 364 "cmDependsJavaParser.y"
+#line 363 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2722 "cmDependsJavaParser.cxx"
+#line 2721 "cmDependsJavaParser.cxx"
     break;
 
   case 27: /* ArrayType: Name Dims  */
-#line 372 "cmDependsJavaParser.y"
+#line 371 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpStoreClass((yyvsp[-1].str));
@@ -2730,56 +2729,56 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2734 "cmDependsJavaParser.cxx"
+#line 2733 "cmDependsJavaParser.cxx"
     break;
 
   case 28: /* Name: SimpleName  */
-#line 382 "cmDependsJavaParser.y"
+#line 381 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2743 "cmDependsJavaParser.cxx"
+#line 2742 "cmDependsJavaParser.cxx"
     break;
 
   case 29: /* Name: QualifiedName  */
-#line 388 "cmDependsJavaParser.y"
+#line 387 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2752 "cmDependsJavaParser.cxx"
+#line 2751 "cmDependsJavaParser.cxx"
     break;
 
   case 30: /* SimpleName: Identifier  */
-#line 395 "cmDependsJavaParser.y"
+#line 394 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2761 "cmDependsJavaParser.cxx"
+#line 2760 "cmDependsJavaParser.cxx"
     break;
 
   case 31: /* Identifier: jp_NAME  */
-#line 402 "cmDependsJavaParser.y"
+#line 401 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2770 "cmDependsJavaParser.cxx"
+#line 2769 "cmDependsJavaParser.cxx"
     break;
 
   case 32: /* Identifier: jp_DOLLAR jp_NAME  */
-#line 408 "cmDependsJavaParser.y"
+#line 407 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = (yyvsp[0].str);
 }
-#line 2779 "cmDependsJavaParser.cxx"
+#line 2778 "cmDependsJavaParser.cxx"
     break;
 
   case 33: /* QualifiedName: Name jp_DOT Identifier  */
-#line 415 "cmDependsJavaParser.y"
+#line 414 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->AddClassFound((yyvsp[-2].str));
@@ -2787,11 +2786,11 @@
   yyGetParser->DeallocateParserType(&((yyvsp[-2].str)));
   (yyval.str) = const_cast<char*>(yyGetParser->GetCurrentCombine());
 }
-#line 2791 "cmDependsJavaParser.cxx"
+#line 2790 "cmDependsJavaParser.cxx"
     break;
 
   case 34: /* QualifiedName: Name jp_DOT jp_CLASS  */
-#line 424 "cmDependsJavaParser.y"
+#line 423 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -2800,11 +2799,11 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2804 "cmDependsJavaParser.cxx"
+#line 2803 "cmDependsJavaParser.cxx"
     break;
 
   case 35: /* QualifiedName: Name jp_DOT jp_THIS  */
-#line 434 "cmDependsJavaParser.y"
+#line 433 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -2813,118 +2812,118 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2817 "cmDependsJavaParser.cxx"
+#line 2816 "cmDependsJavaParser.cxx"
     break;
 
   case 36: /* QualifiedName: SimpleType jp_DOT jp_CLASS  */
-#line 444 "cmDependsJavaParser.y"
+#line 443 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2828 "cmDependsJavaParser.cxx"
+#line 2827 "cmDependsJavaParser.cxx"
     break;
 
   case 37: /* SimpleType: PrimitiveType  */
-#line 453 "cmDependsJavaParser.y"
+#line 452 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2839 "cmDependsJavaParser.cxx"
+#line 2838 "cmDependsJavaParser.cxx"
     break;
 
   case 38: /* SimpleType: jp_VOID  */
-#line 461 "cmDependsJavaParser.y"
+#line 460 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2850 "cmDependsJavaParser.cxx"
+#line 2849 "cmDependsJavaParser.cxx"
     break;
 
   case 39: /* CompilationUnit: PackageDeclarationopt ImportDeclarations TypeDeclarations  */
-#line 470 "cmDependsJavaParser.y"
+#line 469 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2861 "cmDependsJavaParser.cxx"
+#line 2860 "cmDependsJavaParser.cxx"
     break;
 
   case 40: /* PackageDeclarationopt: %empty  */
-#line 478 "cmDependsJavaParser.y"
+#line 477 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2871 "cmDependsJavaParser.cxx"
+#line 2870 "cmDependsJavaParser.cxx"
     break;
 
   case 41: /* PackageDeclarationopt: PackageDeclaration  */
-#line 485 "cmDependsJavaParser.y"
+#line 484 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2882 "cmDependsJavaParser.cxx"
+#line 2881 "cmDependsJavaParser.cxx"
     break;
 
   case 42: /* ImportDeclarations: %empty  */
-#line 493 "cmDependsJavaParser.y"
+#line 492 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2892 "cmDependsJavaParser.cxx"
+#line 2891 "cmDependsJavaParser.cxx"
     break;
 
   case 43: /* ImportDeclarations: ImportDeclarations ImportDeclaration  */
-#line 500 "cmDependsJavaParser.y"
+#line 499 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2903 "cmDependsJavaParser.cxx"
+#line 2902 "cmDependsJavaParser.cxx"
     break;
 
   case 44: /* TypeDeclarations: %empty  */
-#line 508 "cmDependsJavaParser.y"
+#line 507 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2913 "cmDependsJavaParser.cxx"
+#line 2912 "cmDependsJavaParser.cxx"
     break;
 
   case 45: /* TypeDeclarations: TypeDeclarations TypeDeclaration  */
-#line 515 "cmDependsJavaParser.y"
+#line 514 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2924 "cmDependsJavaParser.cxx"
+#line 2923 "cmDependsJavaParser.cxx"
     break;
 
   case 46: /* PackageDeclaration: jp_PACKAGE Name jp_SEMICOL  */
-#line 524 "cmDependsJavaParser.y"
+#line 523 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->SetCurrentPackage((yyvsp[-1].str));
@@ -2934,33 +2933,33 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2938 "cmDependsJavaParser.cxx"
+#line 2937 "cmDependsJavaParser.cxx"
     break;
 
   case 47: /* ImportDeclaration: SingleTypeImportDeclaration  */
-#line 536 "cmDependsJavaParser.y"
+#line 535 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2949 "cmDependsJavaParser.cxx"
+#line 2948 "cmDependsJavaParser.cxx"
     break;
 
   case 48: /* ImportDeclaration: TypeImportOnDemandDeclaration  */
-#line 544 "cmDependsJavaParser.y"
+#line 543 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2960 "cmDependsJavaParser.cxx"
+#line 2959 "cmDependsJavaParser.cxx"
     break;
 
   case 49: /* SingleTypeImportDeclaration: jp_IMPORT Name jp_SEMICOL  */
-#line 553 "cmDependsJavaParser.y"
+#line 552 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->AddPackagesImport((yyvsp[-1].str));
@@ -2970,11 +2969,11 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2974 "cmDependsJavaParser.cxx"
+#line 2973 "cmDependsJavaParser.cxx"
     break;
 
   case 50: /* TypeImportOnDemandDeclaration: jp_IMPORT Name jp_DOT jp_TIMES jp_SEMICOL  */
-#line 565 "cmDependsJavaParser.y"
+#line 564 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   std::string str = (yyvsp[-3].str);
@@ -2985,77 +2984,77 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 2989 "cmDependsJavaParser.cxx"
+#line 2988 "cmDependsJavaParser.cxx"
     break;
 
   case 51: /* TypeDeclaration: ClassDeclaration  */
-#line 578 "cmDependsJavaParser.y"
+#line 577 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3000 "cmDependsJavaParser.cxx"
+#line 2999 "cmDependsJavaParser.cxx"
     break;
 
   case 52: /* TypeDeclaration: InterfaceDeclaration  */
-#line 586 "cmDependsJavaParser.y"
+#line 585 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3011 "cmDependsJavaParser.cxx"
+#line 3010 "cmDependsJavaParser.cxx"
     break;
 
   case 53: /* TypeDeclaration: jp_SEMICOL  */
-#line 594 "cmDependsJavaParser.y"
+#line 593 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3022 "cmDependsJavaParser.cxx"
+#line 3021 "cmDependsJavaParser.cxx"
     break;
 
   case 54: /* Modifiers: Modifier  */
-#line 603 "cmDependsJavaParser.y"
+#line 602 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3033 "cmDependsJavaParser.cxx"
+#line 3032 "cmDependsJavaParser.cxx"
     break;
 
   case 55: /* Modifiers: Modifiers Modifier  */
-#line 611 "cmDependsJavaParser.y"
+#line 610 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3044 "cmDependsJavaParser.cxx"
+#line 3043 "cmDependsJavaParser.cxx"
     break;
 
   case 67: /* ClassHeader: Modifiersopt jp_CLASS Identifier  */
-#line 626 "cmDependsJavaParser.y"
+#line 625 "cmDependsJavaParser.y"
 {
   yyGetParser->StartClass((yyvsp[0].str));
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
   jpCheckEmpty(3);
 }
-#line 3055 "cmDependsJavaParser.cxx"
+#line 3054 "cmDependsJavaParser.cxx"
     break;
 
   case 68: /* ClassDeclaration: ClassHeader ClassBody  */
-#line 636 "cmDependsJavaParser.y"
+#line 635 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3063,11 +3062,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3067 "cmDependsJavaParser.cxx"
+#line 3066 "cmDependsJavaParser.cxx"
     break;
 
   case 69: /* ClassDeclaration: ClassHeader Interfaces ClassBody  */
-#line 645 "cmDependsJavaParser.y"
+#line 644 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(2);
@@ -3075,11 +3074,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3079 "cmDependsJavaParser.cxx"
+#line 3078 "cmDependsJavaParser.cxx"
     break;
 
   case 70: /* ClassDeclaration: ClassHeader Super ClassBody  */
-#line 654 "cmDependsJavaParser.y"
+#line 653 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3087,11 +3086,11 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3091 "cmDependsJavaParser.cxx"
+#line 3090 "cmDependsJavaParser.cxx"
     break;
 
   case 71: /* ClassDeclaration: ClassHeader Super Interfaces ClassBody  */
-#line 663 "cmDependsJavaParser.y"
+#line 662 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3099,226 +3098,226 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3103 "cmDependsJavaParser.cxx"
+#line 3102 "cmDependsJavaParser.cxx"
     break;
 
   case 72: /* Modifiersopt: %empty  */
-#line 672 "cmDependsJavaParser.y"
+#line 671 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3113 "cmDependsJavaParser.cxx"
+#line 3112 "cmDependsJavaParser.cxx"
     break;
 
   case 73: /* Modifiersopt: Modifiers  */
-#line 679 "cmDependsJavaParser.y"
+#line 678 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3124 "cmDependsJavaParser.cxx"
+#line 3123 "cmDependsJavaParser.cxx"
     break;
 
   case 74: /* Super: jp_EXTENDS ClassType  */
-#line 688 "cmDependsJavaParser.y"
+#line 687 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3135 "cmDependsJavaParser.cxx"
+#line 3134 "cmDependsJavaParser.cxx"
     break;
 
   case 75: /* Interfaces: jp_IMPLEMENTS InterfaceTypeList  */
-#line 697 "cmDependsJavaParser.y"
+#line 696 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3146 "cmDependsJavaParser.cxx"
+#line 3145 "cmDependsJavaParser.cxx"
     break;
 
   case 76: /* InterfaceTypeList: InterfaceType  */
-#line 706 "cmDependsJavaParser.y"
+#line 705 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3157 "cmDependsJavaParser.cxx"
+#line 3156 "cmDependsJavaParser.cxx"
     break;
 
   case 77: /* InterfaceTypeList: InterfaceTypeList jp_COMMA InterfaceType  */
-#line 714 "cmDependsJavaParser.y"
+#line 713 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3168 "cmDependsJavaParser.cxx"
+#line 3167 "cmDependsJavaParser.cxx"
     break;
 
   case 78: /* ClassBody: jp_CURLYSTART ClassBodyDeclarations jp_CURLYEND  */
-#line 723 "cmDependsJavaParser.y"
+#line 722 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3179 "cmDependsJavaParser.cxx"
+#line 3178 "cmDependsJavaParser.cxx"
     break;
 
   case 79: /* ClassBodyDeclarations: %empty  */
-#line 731 "cmDependsJavaParser.y"
+#line 730 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3189 "cmDependsJavaParser.cxx"
+#line 3188 "cmDependsJavaParser.cxx"
     break;
 
   case 80: /* ClassBodyDeclarations: ClassBodyDeclarations ClassBodyDeclaration  */
-#line 738 "cmDependsJavaParser.y"
+#line 737 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3200 "cmDependsJavaParser.cxx"
+#line 3199 "cmDependsJavaParser.cxx"
     break;
 
   case 81: /* ClassBodyDeclaration: ClassMemberDeclaration  */
-#line 747 "cmDependsJavaParser.y"
+#line 746 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3211 "cmDependsJavaParser.cxx"
+#line 3210 "cmDependsJavaParser.cxx"
     break;
 
   case 82: /* ClassBodyDeclaration: StaticInitializer  */
-#line 755 "cmDependsJavaParser.y"
+#line 754 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3222 "cmDependsJavaParser.cxx"
+#line 3221 "cmDependsJavaParser.cxx"
     break;
 
   case 83: /* ClassBodyDeclaration: ConstructorDeclaration  */
-#line 763 "cmDependsJavaParser.y"
+#line 762 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3233 "cmDependsJavaParser.cxx"
+#line 3232 "cmDependsJavaParser.cxx"
     break;
 
   case 84: /* ClassBodyDeclaration: TypeDeclaration  */
-#line 771 "cmDependsJavaParser.y"
+#line 770 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3244 "cmDependsJavaParser.cxx"
+#line 3243 "cmDependsJavaParser.cxx"
     break;
 
   case 85: /* ClassMemberDeclaration: FieldDeclaration  */
-#line 780 "cmDependsJavaParser.y"
+#line 779 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3255 "cmDependsJavaParser.cxx"
+#line 3254 "cmDependsJavaParser.cxx"
     break;
 
   case 86: /* ClassMemberDeclaration: MethodDeclaration  */
-#line 788 "cmDependsJavaParser.y"
+#line 787 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3266 "cmDependsJavaParser.cxx"
+#line 3265 "cmDependsJavaParser.cxx"
     break;
 
   case 87: /* FieldDeclaration: Modifiersopt Type VariableDeclarators jp_SEMICOL  */
-#line 797 "cmDependsJavaParser.y"
+#line 796 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
 }
-#line 3274 "cmDependsJavaParser.cxx"
+#line 3273 "cmDependsJavaParser.cxx"
     break;
 
   case 88: /* VariableDeclarators: VariableDeclarator  */
-#line 803 "cmDependsJavaParser.y"
+#line 802 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3285 "cmDependsJavaParser.cxx"
+#line 3284 "cmDependsJavaParser.cxx"
     break;
 
   case 89: /* VariableDeclarators: VariableDeclarators jp_COMMA VariableDeclarator  */
-#line 811 "cmDependsJavaParser.y"
+#line 810 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3296 "cmDependsJavaParser.cxx"
+#line 3295 "cmDependsJavaParser.cxx"
     break;
 
   case 90: /* VariableDeclarator: VariableDeclaratorId  */
-#line 820 "cmDependsJavaParser.y"
+#line 819 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3307 "cmDependsJavaParser.cxx"
+#line 3306 "cmDependsJavaParser.cxx"
     break;
 
   case 91: /* VariableDeclarator: VariableDeclaratorId jp_EQUALS VariableInitializer  */
-#line 828 "cmDependsJavaParser.y"
+#line 827 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3318 "cmDependsJavaParser.cxx"
+#line 3317 "cmDependsJavaParser.cxx"
     break;
 
   case 92: /* VariableDeclaratorId: Identifier  */
-#line 837 "cmDependsJavaParser.y"
+#line 836 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -3326,77 +3325,77 @@
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3330 "cmDependsJavaParser.cxx"
+#line 3329 "cmDependsJavaParser.cxx"
     break;
 
   case 93: /* VariableDeclaratorId: VariableDeclaratorId jp_BRACKETSTART jp_BRACKETEND  */
-#line 846 "cmDependsJavaParser.y"
+#line 845 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3341 "cmDependsJavaParser.cxx"
+#line 3340 "cmDependsJavaParser.cxx"
     break;
 
   case 94: /* VariableInitializer: Expression  */
-#line 855 "cmDependsJavaParser.y"
+#line 854 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3352 "cmDependsJavaParser.cxx"
+#line 3351 "cmDependsJavaParser.cxx"
     break;
 
   case 95: /* VariableInitializer: ArrayInitializer  */
-#line 863 "cmDependsJavaParser.y"
+#line 862 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3363 "cmDependsJavaParser.cxx"
+#line 3362 "cmDependsJavaParser.cxx"
     break;
 
   case 96: /* MethodDeclaration: MethodHeader jp_SEMICOL  */
-#line 872 "cmDependsJavaParser.y"
+#line 871 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3374 "cmDependsJavaParser.cxx"
+#line 3373 "cmDependsJavaParser.cxx"
     break;
 
   case 97: /* MethodDeclaration: MethodHeader MethodBody  */
-#line 880 "cmDependsJavaParser.y"
+#line 879 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3385 "cmDependsJavaParser.cxx"
+#line 3384 "cmDependsJavaParser.cxx"
     break;
 
   case 98: /* MethodDeclaration: MethodHeader MethodBody jp_SEMICOL  */
-#line 888 "cmDependsJavaParser.y"
+#line 887 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3396 "cmDependsJavaParser.cxx"
+#line 3395 "cmDependsJavaParser.cxx"
     break;
 
   case 99: /* MethodHeader: Modifiersopt Type MethodDeclarator Throwsopt  */
-#line 897 "cmDependsJavaParser.y"
+#line 896 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3404,11 +3403,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3408 "cmDependsJavaParser.cxx"
+#line 3407 "cmDependsJavaParser.cxx"
     break;
 
   case 100: /* MethodHeader: Modifiersopt jp_VOID MethodDeclarator Throwsopt  */
-#line 906 "cmDependsJavaParser.y"
+#line 905 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3416,22 +3415,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3420 "cmDependsJavaParser.cxx"
+#line 3419 "cmDependsJavaParser.cxx"
     break;
 
   case 101: /* Throwsopt: %empty  */
-#line 915 "cmDependsJavaParser.y"
+#line 914 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3431 "cmDependsJavaParser.cxx"
+#line 3430 "cmDependsJavaParser.cxx"
     break;
 
   case 102: /* Throwsopt: Throws  */
-#line 923 "cmDependsJavaParser.y"
+#line 922 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3439,11 +3438,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3443 "cmDependsJavaParser.cxx"
+#line 3442 "cmDependsJavaParser.cxx"
     break;
 
   case 103: /* MethodDeclarator: Identifier jp_PARESTART FormalParameterListopt jp_PAREEND  */
-#line 933 "cmDependsJavaParser.y"
+#line 932 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -3452,40 +3451,40 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3456 "cmDependsJavaParser.cxx"
+#line 3455 "cmDependsJavaParser.cxx"
     break;
 
   case 104: /* MethodDeclarator: MethodDeclarator jp_BRACKETSTART jp_BRACKETEND  */
-#line 943 "cmDependsJavaParser.y"
+#line 942 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
 
 }
-#line 3465 "cmDependsJavaParser.cxx"
+#line 3464 "cmDependsJavaParser.cxx"
     break;
 
   case 105: /* FormalParameterListopt: %empty  */
-#line 949 "cmDependsJavaParser.y"
+#line 948 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3476 "cmDependsJavaParser.cxx"
+#line 3475 "cmDependsJavaParser.cxx"
     break;
 
   case 107: /* FormalParameterList: FormalParameter  */
-#line 960 "cmDependsJavaParser.y"
+#line 959 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 3485 "cmDependsJavaParser.cxx"
+#line 3484 "cmDependsJavaParser.cxx"
     break;
 
   case 108: /* FormalParameterList: FormalParameterList jp_COMMA FormalParameter  */
-#line 966 "cmDependsJavaParser.y"
+#line 965 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3493,11 +3492,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3497 "cmDependsJavaParser.cxx"
+#line 3496 "cmDependsJavaParser.cxx"
     break;
 
   case 109: /* FormalParameter: Modifiersopt Type VariableDeclaratorId  */
-#line 976 "cmDependsJavaParser.y"
+#line 975 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3505,11 +3504,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3509 "cmDependsJavaParser.cxx"
+#line 3508 "cmDependsJavaParser.cxx"
     break;
 
   case 110: /* Throws: jp_THROWS ClassTypeList  */
-#line 986 "cmDependsJavaParser.y"
+#line 985 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3517,20 +3516,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3521 "cmDependsJavaParser.cxx"
+#line 3520 "cmDependsJavaParser.cxx"
     break;
 
   case 111: /* ClassTypeList: ClassType  */
-#line 996 "cmDependsJavaParser.y"
+#line 995 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 3530 "cmDependsJavaParser.cxx"
+#line 3529 "cmDependsJavaParser.cxx"
     break;
 
   case 112: /* ClassTypeList: ClassTypeList jp_COMMA ClassType  */
-#line 1002 "cmDependsJavaParser.y"
+#line 1001 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3538,11 +3537,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3542 "cmDependsJavaParser.cxx"
+#line 3541 "cmDependsJavaParser.cxx"
     break;
 
   case 113: /* MethodBody: Block  */
-#line 1012 "cmDependsJavaParser.y"
+#line 1011 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3550,11 +3549,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3554 "cmDependsJavaParser.cxx"
+#line 3553 "cmDependsJavaParser.cxx"
     break;
 
   case 114: /* StaticInitializer: jp_STATIC Block  */
-#line 1022 "cmDependsJavaParser.y"
+#line 1021 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3562,11 +3561,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3566 "cmDependsJavaParser.cxx"
+#line 3565 "cmDependsJavaParser.cxx"
     break;
 
   case 115: /* ConstructorDeclaration: Modifiersopt ConstructorDeclarator Throwsopt ConstructorBody  */
-#line 1032 "cmDependsJavaParser.y"
+#line 1031 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3574,11 +3573,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3578 "cmDependsJavaParser.cxx"
+#line 3577 "cmDependsJavaParser.cxx"
     break;
 
   case 116: /* ConstructorDeclaration: Modifiersopt ConstructorDeclarator Throwsopt ConstructorBody jp_SEMICOL  */
-#line 1041 "cmDependsJavaParser.y"
+#line 1040 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3586,11 +3585,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3590 "cmDependsJavaParser.cxx"
+#line 3589 "cmDependsJavaParser.cxx"
     break;
 
   case 117: /* ConstructorDeclarator: SimpleName jp_PARESTART FormalParameterListopt jp_PAREEND  */
-#line 1051 "cmDependsJavaParser.y"
+#line 1050 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -3599,11 +3598,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3603 "cmDependsJavaParser.cxx"
+#line 3602 "cmDependsJavaParser.cxx"
     break;
 
   case 118: /* ConstructorBody: jp_CURLYSTART ExplicitConstructorInvocationopt BlockStatementsopt jp_CURLYEND  */
-#line 1062 "cmDependsJavaParser.y"
+#line 1061 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -3611,22 +3610,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3615 "cmDependsJavaParser.cxx"
+#line 3614 "cmDependsJavaParser.cxx"
     break;
 
   case 119: /* ExplicitConstructorInvocationopt: %empty  */
-#line 1071 "cmDependsJavaParser.y"
+#line 1070 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3626 "cmDependsJavaParser.cxx"
+#line 3625 "cmDependsJavaParser.cxx"
     break;
 
   case 120: /* ExplicitConstructorInvocationopt: ExplicitConstructorInvocationopt ExplicitConstructorInvocation  */
-#line 1079 "cmDependsJavaParser.y"
+#line 1078 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3634,11 +3633,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3638 "cmDependsJavaParser.cxx"
+#line 3637 "cmDependsJavaParser.cxx"
     break;
 
   case 121: /* ExplicitConstructorInvocation: jp_THIS jp_PARESTART ArgumentListopt jp_PAREEND jp_SEMICOL  */
-#line 1089 "cmDependsJavaParser.y"
+#line 1088 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3646,11 +3645,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3650 "cmDependsJavaParser.cxx"
+#line 3649 "cmDependsJavaParser.cxx"
     break;
 
   case 122: /* ExplicitConstructorInvocation: jp_SUPER jp_PARESTART ArgumentListopt jp_PAREEND jp_SEMICOL  */
-#line 1098 "cmDependsJavaParser.y"
+#line 1097 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -3658,22 +3657,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3662 "cmDependsJavaParser.cxx"
+#line 3661 "cmDependsJavaParser.cxx"
     break;
 
   case 123: /* InterfaceHeader: Modifiersopt jp_INTERFACE Identifier  */
-#line 1108 "cmDependsJavaParser.y"
+#line 1107 "cmDependsJavaParser.y"
 {
   yyGetParser->StartClass((yyvsp[0].str));
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
   jpCheckEmpty(3);
 }
-#line 3673 "cmDependsJavaParser.cxx"
+#line 3672 "cmDependsJavaParser.cxx"
     break;
 
   case 124: /* InterfaceDeclaration: InterfaceHeader ExtendsInterfacesopt InterfaceBody  */
-#line 1117 "cmDependsJavaParser.y"
+#line 1116 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3681,21 +3680,21 @@
   yyGetParser->SetCurrentCombine("");
   yyGetParser->EndClass();
 }
-#line 3685 "cmDependsJavaParser.cxx"
+#line 3684 "cmDependsJavaParser.cxx"
     break;
 
   case 125: /* ExtendsInterfacesopt: %empty  */
-#line 1126 "cmDependsJavaParser.y"
+#line 1125 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 }
-#line 3695 "cmDependsJavaParser.cxx"
+#line 3694 "cmDependsJavaParser.cxx"
     break;
 
   case 126: /* ExtendsInterfacesopt: ExtendsInterfaces  */
-#line 1133 "cmDependsJavaParser.y"
+#line 1132 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3703,11 +3702,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3707 "cmDependsJavaParser.cxx"
+#line 3706 "cmDependsJavaParser.cxx"
     break;
 
   case 127: /* ExtendsInterfaces: jp_EXTENDS InterfaceType  */
-#line 1143 "cmDependsJavaParser.y"
+#line 1142 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3715,11 +3714,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3719 "cmDependsJavaParser.cxx"
+#line 3718 "cmDependsJavaParser.cxx"
     break;
 
   case 128: /* ExtendsInterfaces: ExtendsInterfaces jp_COMMA InterfaceType  */
-#line 1152 "cmDependsJavaParser.y"
+#line 1151 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3727,11 +3726,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3731 "cmDependsJavaParser.cxx"
+#line 3730 "cmDependsJavaParser.cxx"
     break;
 
   case 129: /* InterfaceBody: jp_CURLYSTART InterfaceMemberDeclarations jp_CURLYEND  */
-#line 1162 "cmDependsJavaParser.y"
+#line 1161 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3739,33 +3738,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3743 "cmDependsJavaParser.cxx"
+#line 3742 "cmDependsJavaParser.cxx"
     break;
 
   case 130: /* InterfaceMemberDeclarations: %empty  */
-#line 1171 "cmDependsJavaParser.y"
+#line 1170 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3754 "cmDependsJavaParser.cxx"
+#line 3753 "cmDependsJavaParser.cxx"
     break;
 
   case 131: /* InterfaceMemberDeclarations: InterfaceMemberDeclarations InterfaceMemberDeclaration  */
-#line 1179 "cmDependsJavaParser.y"
+#line 1178 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3765 "cmDependsJavaParser.cxx"
+#line 3764 "cmDependsJavaParser.cxx"
     break;
 
   case 132: /* InterfaceMemberDeclaration: ConstantDeclaration  */
-#line 1188 "cmDependsJavaParser.y"
+#line 1187 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3773,11 +3772,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3777 "cmDependsJavaParser.cxx"
+#line 3776 "cmDependsJavaParser.cxx"
     break;
 
   case 133: /* InterfaceMemberDeclaration: AbstractMethodDeclaration  */
-#line 1197 "cmDependsJavaParser.y"
+#line 1196 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3785,11 +3784,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3789 "cmDependsJavaParser.cxx"
+#line 3788 "cmDependsJavaParser.cxx"
     break;
 
   case 134: /* InterfaceMemberDeclaration: ClassDeclaration  */
-#line 1206 "cmDependsJavaParser.y"
+#line 1205 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3797,22 +3796,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3801 "cmDependsJavaParser.cxx"
+#line 3800 "cmDependsJavaParser.cxx"
     break;
 
   case 135: /* InterfaceMemberDeclaration: ClassDeclaration jp_SEMICOL  */
-#line 1215 "cmDependsJavaParser.y"
+#line 1214 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3812 "cmDependsJavaParser.cxx"
+#line 3811 "cmDependsJavaParser.cxx"
     break;
 
   case 136: /* InterfaceMemberDeclaration: InterfaceDeclaration  */
-#line 1223 "cmDependsJavaParser.y"
+#line 1222 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3820,22 +3819,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3824 "cmDependsJavaParser.cxx"
+#line 3823 "cmDependsJavaParser.cxx"
     break;
 
   case 137: /* InterfaceMemberDeclaration: InterfaceDeclaration jp_SEMICOL  */
-#line 1232 "cmDependsJavaParser.y"
+#line 1231 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3835 "cmDependsJavaParser.cxx"
+#line 3834 "cmDependsJavaParser.cxx"
     break;
 
   case 138: /* ConstantDeclaration: FieldDeclaration  */
-#line 1241 "cmDependsJavaParser.y"
+#line 1240 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3843,11 +3842,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3847 "cmDependsJavaParser.cxx"
+#line 3846 "cmDependsJavaParser.cxx"
     break;
 
   case 139: /* AbstractMethodDeclaration: MethodHeader Semicols  */
-#line 1251 "cmDependsJavaParser.y"
+#line 1250 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3855,11 +3854,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3859 "cmDependsJavaParser.cxx"
+#line 3858 "cmDependsJavaParser.cxx"
     break;
 
   case 140: /* Semicols: jp_SEMICOL  */
-#line 1261 "cmDependsJavaParser.y"
+#line 1260 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3867,11 +3866,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3871 "cmDependsJavaParser.cxx"
+#line 3870 "cmDependsJavaParser.cxx"
     break;
 
   case 141: /* Semicols: Semicols jp_SEMICOL  */
-#line 1270 "cmDependsJavaParser.y"
+#line 1269 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3879,11 +3878,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3883 "cmDependsJavaParser.cxx"
+#line 3882 "cmDependsJavaParser.cxx"
     break;
 
   case 142: /* ArrayInitializer: jp_CURLYSTART VariableInitializersOptional jp_CURLYEND  */
-#line 1280 "cmDependsJavaParser.y"
+#line 1279 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3891,22 +3890,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3895 "cmDependsJavaParser.cxx"
+#line 3894 "cmDependsJavaParser.cxx"
     break;
 
   case 143: /* VariableInitializersOptional: %empty  */
-#line 1289 "cmDependsJavaParser.y"
+#line 1288 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3906 "cmDependsJavaParser.cxx"
+#line 3905 "cmDependsJavaParser.cxx"
     break;
 
   case 144: /* VariableInitializersOptional: VariableInitializers  */
-#line 1297 "cmDependsJavaParser.y"
+#line 1296 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3914,11 +3913,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3918 "cmDependsJavaParser.cxx"
+#line 3917 "cmDependsJavaParser.cxx"
     break;
 
   case 145: /* VariableInitializersOptional: VariableInitializers jp_COMMA  */
-#line 1306 "cmDependsJavaParser.y"
+#line 1305 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -3926,11 +3925,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3930 "cmDependsJavaParser.cxx"
+#line 3929 "cmDependsJavaParser.cxx"
     break;
 
   case 146: /* VariableInitializers: VariableInitializer  */
-#line 1316 "cmDependsJavaParser.y"
+#line 1315 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3938,11 +3937,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3942 "cmDependsJavaParser.cxx"
+#line 3941 "cmDependsJavaParser.cxx"
     break;
 
   case 147: /* VariableInitializers: VariableInitializers jp_COMMA VariableInitializer  */
-#line 1325 "cmDependsJavaParser.y"
+#line 1324 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -3950,33 +3949,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3954 "cmDependsJavaParser.cxx"
+#line 3953 "cmDependsJavaParser.cxx"
     break;
 
   case 148: /* Block: jp_CURLYSTART BlockStatementsopt jp_CURLYEND  */
-#line 1335 "cmDependsJavaParser.y"
+#line 1334 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3965 "cmDependsJavaParser.cxx"
+#line 3964 "cmDependsJavaParser.cxx"
     break;
 
   case 149: /* BlockStatementsopt: %empty  */
-#line 1343 "cmDependsJavaParser.y"
+#line 1342 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3976 "cmDependsJavaParser.cxx"
+#line 3975 "cmDependsJavaParser.cxx"
     break;
 
   case 150: /* BlockStatementsopt: BlockStatements  */
-#line 1351 "cmDependsJavaParser.y"
+#line 1350 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3984,11 +3983,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 3988 "cmDependsJavaParser.cxx"
+#line 3987 "cmDependsJavaParser.cxx"
     break;
 
   case 151: /* BlockStatements: BlockStatement  */
-#line 1361 "cmDependsJavaParser.y"
+#line 1360 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -3996,11 +3995,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4000 "cmDependsJavaParser.cxx"
+#line 3999 "cmDependsJavaParser.cxx"
     break;
 
   case 152: /* BlockStatements: BlockStatements BlockStatement  */
-#line 1370 "cmDependsJavaParser.y"
+#line 1369 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -4008,11 +4007,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4012 "cmDependsJavaParser.cxx"
+#line 4011 "cmDependsJavaParser.cxx"
     break;
 
   case 153: /* BlockStatement: LocalVariableDeclarationStatement  */
-#line 1380 "cmDependsJavaParser.y"
+#line 1379 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4020,11 +4019,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4024 "cmDependsJavaParser.cxx"
+#line 4023 "cmDependsJavaParser.cxx"
     break;
 
   case 154: /* BlockStatement: Statement  */
-#line 1389 "cmDependsJavaParser.y"
+#line 1388 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4032,11 +4031,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4036 "cmDependsJavaParser.cxx"
+#line 4035 "cmDependsJavaParser.cxx"
     break;
 
   case 155: /* BlockStatement: ClassDeclaration  */
-#line 1398 "cmDependsJavaParser.y"
+#line 1397 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4044,11 +4043,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4048 "cmDependsJavaParser.cxx"
+#line 4047 "cmDependsJavaParser.cxx"
     break;
 
   case 156: /* LocalVariableDeclarationStatement: LocalVariableDeclaration jp_SEMICOL  */
-#line 1408 "cmDependsJavaParser.y"
+#line 1407 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -4056,11 +4055,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4060 "cmDependsJavaParser.cxx"
+#line 4059 "cmDependsJavaParser.cxx"
     break;
 
   case 157: /* LocalVariableDeclaration: Modifiers Type VariableDeclarators  */
-#line 1418 "cmDependsJavaParser.y"
+#line 1417 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(3);
@@ -4068,11 +4067,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4072 "cmDependsJavaParser.cxx"
+#line 4071 "cmDependsJavaParser.cxx"
     break;
 
   case 158: /* LocalVariableDeclaration: Type VariableDeclarators  */
-#line 1427 "cmDependsJavaParser.y"
+#line 1426 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(2);
@@ -4080,11 +4079,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4084 "cmDependsJavaParser.cxx"
+#line 4083 "cmDependsJavaParser.cxx"
     break;
 
   case 159: /* Statement: StatementWithoutTrailingSubstatement  */
-#line 1437 "cmDependsJavaParser.y"
+#line 1436 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4092,11 +4091,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4096 "cmDependsJavaParser.cxx"
+#line 4095 "cmDependsJavaParser.cxx"
     break;
 
   case 160: /* Statement: LabeledStatement  */
-#line 1446 "cmDependsJavaParser.y"
+#line 1445 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4104,11 +4103,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4108 "cmDependsJavaParser.cxx"
+#line 4107 "cmDependsJavaParser.cxx"
     break;
 
   case 161: /* Statement: IfThenStatement  */
-#line 1455 "cmDependsJavaParser.y"
+#line 1454 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4116,11 +4115,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4120 "cmDependsJavaParser.cxx"
+#line 4119 "cmDependsJavaParser.cxx"
     break;
 
   case 162: /* Statement: IfThenElseStatement  */
-#line 1464 "cmDependsJavaParser.y"
+#line 1463 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4128,11 +4127,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4132 "cmDependsJavaParser.cxx"
+#line 4131 "cmDependsJavaParser.cxx"
     break;
 
   case 163: /* Statement: WhileStatement  */
-#line 1473 "cmDependsJavaParser.y"
+#line 1472 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4140,11 +4139,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4144 "cmDependsJavaParser.cxx"
+#line 4143 "cmDependsJavaParser.cxx"
     break;
 
   case 164: /* Statement: ForStatement  */
-#line 1482 "cmDependsJavaParser.y"
+#line 1481 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4152,11 +4151,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4156 "cmDependsJavaParser.cxx"
+#line 4155 "cmDependsJavaParser.cxx"
     break;
 
   case 165: /* StatementNoShortIf: StatementWithoutTrailingSubstatement  */
-#line 1492 "cmDependsJavaParser.y"
+#line 1491 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4164,11 +4163,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4168 "cmDependsJavaParser.cxx"
+#line 4167 "cmDependsJavaParser.cxx"
     break;
 
   case 166: /* StatementNoShortIf: LabeledStatementNoShortIf  */
-#line 1501 "cmDependsJavaParser.y"
+#line 1500 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4176,11 +4175,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4180 "cmDependsJavaParser.cxx"
+#line 4179 "cmDependsJavaParser.cxx"
     break;
 
   case 167: /* StatementNoShortIf: IfThenElseStatementNoShortIf  */
-#line 1510 "cmDependsJavaParser.y"
+#line 1509 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4188,11 +4187,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4192 "cmDependsJavaParser.cxx"
+#line 4191 "cmDependsJavaParser.cxx"
     break;
 
   case 168: /* StatementNoShortIf: WhileStatementNoShortIf  */
-#line 1519 "cmDependsJavaParser.y"
+#line 1518 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4200,11 +4199,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4204 "cmDependsJavaParser.cxx"
+#line 4203 "cmDependsJavaParser.cxx"
     break;
 
   case 169: /* StatementNoShortIf: ForStatementNoShortIf  */
-#line 1528 "cmDependsJavaParser.y"
+#line 1527 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4212,11 +4211,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4216 "cmDependsJavaParser.cxx"
+#line 4215 "cmDependsJavaParser.cxx"
     break;
 
   case 170: /* StatementWithoutTrailingSubstatement: Block  */
-#line 1538 "cmDependsJavaParser.y"
+#line 1537 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4224,11 +4223,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4228 "cmDependsJavaParser.cxx"
+#line 4227 "cmDependsJavaParser.cxx"
     break;
 
   case 171: /* StatementWithoutTrailingSubstatement: EmptyStatement  */
-#line 1547 "cmDependsJavaParser.y"
+#line 1546 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4236,11 +4235,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4240 "cmDependsJavaParser.cxx"
+#line 4239 "cmDependsJavaParser.cxx"
     break;
 
   case 172: /* StatementWithoutTrailingSubstatement: ExpressionStatement  */
-#line 1556 "cmDependsJavaParser.y"
+#line 1555 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4248,11 +4247,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4252 "cmDependsJavaParser.cxx"
+#line 4251 "cmDependsJavaParser.cxx"
     break;
 
   case 173: /* StatementWithoutTrailingSubstatement: SwitchStatement  */
-#line 1565 "cmDependsJavaParser.y"
+#line 1564 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4260,11 +4259,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4264 "cmDependsJavaParser.cxx"
+#line 4263 "cmDependsJavaParser.cxx"
     break;
 
   case 174: /* StatementWithoutTrailingSubstatement: DoStatement  */
-#line 1574 "cmDependsJavaParser.y"
+#line 1573 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4272,11 +4271,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4276 "cmDependsJavaParser.cxx"
+#line 4275 "cmDependsJavaParser.cxx"
     break;
 
   case 175: /* StatementWithoutTrailingSubstatement: BreakStatement  */
-#line 1583 "cmDependsJavaParser.y"
+#line 1582 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4284,11 +4283,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4288 "cmDependsJavaParser.cxx"
+#line 4287 "cmDependsJavaParser.cxx"
     break;
 
   case 176: /* StatementWithoutTrailingSubstatement: ContinueStatement  */
-#line 1592 "cmDependsJavaParser.y"
+#line 1591 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4296,11 +4295,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4300 "cmDependsJavaParser.cxx"
+#line 4299 "cmDependsJavaParser.cxx"
     break;
 
   case 177: /* StatementWithoutTrailingSubstatement: ReturnStatement  */
-#line 1601 "cmDependsJavaParser.y"
+#line 1600 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4308,11 +4307,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4312 "cmDependsJavaParser.cxx"
+#line 4311 "cmDependsJavaParser.cxx"
     break;
 
   case 178: /* StatementWithoutTrailingSubstatement: SynchronizedStatement  */
-#line 1610 "cmDependsJavaParser.y"
+#line 1609 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4320,11 +4319,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4324 "cmDependsJavaParser.cxx"
+#line 4323 "cmDependsJavaParser.cxx"
     break;
 
   case 179: /* StatementWithoutTrailingSubstatement: ThrowStatement  */
-#line 1619 "cmDependsJavaParser.y"
+#line 1618 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4332,11 +4331,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4336 "cmDependsJavaParser.cxx"
+#line 4335 "cmDependsJavaParser.cxx"
     break;
 
   case 180: /* StatementWithoutTrailingSubstatement: TryStatement  */
-#line 1628 "cmDependsJavaParser.y"
+#line 1627 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4344,11 +4343,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4348 "cmDependsJavaParser.cxx"
+#line 4347 "cmDependsJavaParser.cxx"
     break;
 
   case 181: /* StatementWithoutTrailingSubstatement: AssertStatement  */
-#line 1637 "cmDependsJavaParser.y"
+#line 1636 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4356,11 +4355,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4360 "cmDependsJavaParser.cxx"
+#line 4359 "cmDependsJavaParser.cxx"
     break;
 
   case 182: /* EmptyStatement: jp_SEMICOL  */
-#line 1647 "cmDependsJavaParser.y"
+#line 1646 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4368,11 +4367,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4372 "cmDependsJavaParser.cxx"
+#line 4371 "cmDependsJavaParser.cxx"
     break;
 
   case 183: /* LabeledStatement: Identifier jp_COLON Statement  */
-#line 1657 "cmDependsJavaParser.y"
+#line 1656 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-2].str)));
@@ -4381,11 +4380,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4385 "cmDependsJavaParser.cxx"
+#line 4384 "cmDependsJavaParser.cxx"
     break;
 
   case 184: /* LabeledStatementNoShortIf: Identifier jp_COLON StatementNoShortIf  */
-#line 1668 "cmDependsJavaParser.y"
+#line 1667 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4393,11 +4392,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4397 "cmDependsJavaParser.cxx"
+#line 4396 "cmDependsJavaParser.cxx"
     break;
 
   case 185: /* ExpressionStatement: StatementExpression jp_SEMICOL  */
-#line 1678 "cmDependsJavaParser.y"
+#line 1677 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4405,11 +4404,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4409 "cmDependsJavaParser.cxx"
+#line 4408 "cmDependsJavaParser.cxx"
     break;
 
   case 186: /* StatementExpression: Assignment  */
-#line 1688 "cmDependsJavaParser.y"
+#line 1687 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4417,11 +4416,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4421 "cmDependsJavaParser.cxx"
+#line 4420 "cmDependsJavaParser.cxx"
     break;
 
   case 187: /* StatementExpression: PreIncrementExpression  */
-#line 1697 "cmDependsJavaParser.y"
+#line 1696 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4429,11 +4428,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4433 "cmDependsJavaParser.cxx"
+#line 4432 "cmDependsJavaParser.cxx"
     break;
 
   case 188: /* StatementExpression: PreDecrementExpression  */
-#line 1706 "cmDependsJavaParser.y"
+#line 1705 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4441,11 +4440,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4445 "cmDependsJavaParser.cxx"
+#line 4444 "cmDependsJavaParser.cxx"
     break;
 
   case 189: /* StatementExpression: PostIncrementExpression  */
-#line 1715 "cmDependsJavaParser.y"
+#line 1714 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4453,11 +4452,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4457 "cmDependsJavaParser.cxx"
+#line 4456 "cmDependsJavaParser.cxx"
     break;
 
   case 190: /* StatementExpression: PostDecrementExpression  */
-#line 1724 "cmDependsJavaParser.y"
+#line 1723 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4465,11 +4464,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4469 "cmDependsJavaParser.cxx"
+#line 4468 "cmDependsJavaParser.cxx"
     break;
 
   case 191: /* StatementExpression: MethodInvocation  */
-#line 1733 "cmDependsJavaParser.y"
+#line 1732 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4477,11 +4476,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4481 "cmDependsJavaParser.cxx"
+#line 4480 "cmDependsJavaParser.cxx"
     break;
 
   case 192: /* StatementExpression: ClassInstanceCreationExpression  */
-#line 1742 "cmDependsJavaParser.y"
+#line 1741 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4489,11 +4488,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4493 "cmDependsJavaParser.cxx"
+#line 4492 "cmDependsJavaParser.cxx"
     break;
 
   case 193: /* IfThenStatement: jp_IF jp_PARESTART Expression jp_PAREEND Statement  */
-#line 1752 "cmDependsJavaParser.y"
+#line 1751 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4501,11 +4500,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4505 "cmDependsJavaParser.cxx"
+#line 4504 "cmDependsJavaParser.cxx"
     break;
 
   case 194: /* IfThenElseStatement: jp_IF jp_PARESTART Expression jp_PAREEND StatementNoShortIf jp_ELSE Statement  */
-#line 1762 "cmDependsJavaParser.y"
+#line 1761 "cmDependsJavaParser.y"
 {
   jpElementStart(7);
   jpCheckEmpty(7);
@@ -4513,11 +4512,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4517 "cmDependsJavaParser.cxx"
+#line 4516 "cmDependsJavaParser.cxx"
     break;
 
   case 195: /* IfThenElseStatementNoShortIf: jp_IF jp_PARESTART Expression jp_PAREEND StatementNoShortIf jp_ELSE StatementNoShortIf  */
-#line 1772 "cmDependsJavaParser.y"
+#line 1771 "cmDependsJavaParser.y"
 {
   jpElementStart(7);
   jpCheckEmpty(7);
@@ -4525,40 +4524,40 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4529 "cmDependsJavaParser.cxx"
+#line 4528 "cmDependsJavaParser.cxx"
     break;
 
   case 196: /* SwitchStatement: jp_SWITCH jp_PARESTART Expression jp_PAREEND SwitchBlock  */
-#line 1782 "cmDependsJavaParser.y"
+#line 1781 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 4538 "cmDependsJavaParser.cxx"
+#line 4537 "cmDependsJavaParser.cxx"
     break;
 
   case 197: /* SwitchBlock: jp_CURLYSTART SwitchBlockStatementGroups SwitchLabelsopt jp_CURLYEND  */
-#line 1789 "cmDependsJavaParser.y"
+#line 1788 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
 
 }
-#line 4547 "cmDependsJavaParser.cxx"
+#line 4546 "cmDependsJavaParser.cxx"
     break;
 
   case 198: /* SwitchLabelsopt: %empty  */
-#line 1795 "cmDependsJavaParser.y"
+#line 1794 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4558 "cmDependsJavaParser.cxx"
+#line 4557 "cmDependsJavaParser.cxx"
     break;
 
   case 199: /* SwitchLabelsopt: SwitchLabels  */
-#line 1803 "cmDependsJavaParser.y"
+#line 1802 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4566,22 +4565,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4570 "cmDependsJavaParser.cxx"
+#line 4569 "cmDependsJavaParser.cxx"
     break;
 
   case 200: /* SwitchBlockStatementGroups: %empty  */
-#line 1812 "cmDependsJavaParser.y"
+#line 1811 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4581 "cmDependsJavaParser.cxx"
+#line 4580 "cmDependsJavaParser.cxx"
     break;
 
   case 201: /* SwitchBlockStatementGroups: SwitchBlockStatementGroups SwitchBlockStatementGroup  */
-#line 1820 "cmDependsJavaParser.y"
+#line 1819 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4589,11 +4588,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4593 "cmDependsJavaParser.cxx"
+#line 4592 "cmDependsJavaParser.cxx"
     break;
 
   case 202: /* SwitchBlockStatementGroup: SwitchLabels BlockStatements  */
-#line 1830 "cmDependsJavaParser.y"
+#line 1829 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4601,11 +4600,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4605 "cmDependsJavaParser.cxx"
+#line 4604 "cmDependsJavaParser.cxx"
     break;
 
   case 203: /* SwitchLabels: SwitchLabel  */
-#line 1840 "cmDependsJavaParser.y"
+#line 1839 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4613,11 +4612,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4617 "cmDependsJavaParser.cxx"
+#line 4616 "cmDependsJavaParser.cxx"
     break;
 
   case 204: /* SwitchLabels: SwitchLabels SwitchLabel  */
-#line 1849 "cmDependsJavaParser.y"
+#line 1848 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4625,11 +4624,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4629 "cmDependsJavaParser.cxx"
+#line 4628 "cmDependsJavaParser.cxx"
     break;
 
   case 205: /* SwitchLabel: jp_CASE ConstantExpression jp_COLON  */
-#line 1859 "cmDependsJavaParser.y"
+#line 1858 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4637,11 +4636,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4641 "cmDependsJavaParser.cxx"
+#line 4640 "cmDependsJavaParser.cxx"
     break;
 
   case 206: /* SwitchLabel: jp_DEFAULT jp_COLON  */
-#line 1868 "cmDependsJavaParser.y"
+#line 1867 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -4649,58 +4648,58 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4653 "cmDependsJavaParser.cxx"
+#line 4652 "cmDependsJavaParser.cxx"
     break;
 
   case 207: /* WhileStatement: jp_WHILE jp_PARESTART Expression jp_PAREEND Statement  */
-#line 1878 "cmDependsJavaParser.y"
+#line 1877 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 4662 "cmDependsJavaParser.cxx"
+#line 4661 "cmDependsJavaParser.cxx"
     break;
 
   case 208: /* WhileStatementNoShortIf: jp_WHILE jp_PARESTART Expression jp_PAREEND StatementNoShortIf  */
-#line 1885 "cmDependsJavaParser.y"
+#line 1884 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 4671 "cmDependsJavaParser.cxx"
+#line 4670 "cmDependsJavaParser.cxx"
     break;
 
   case 209: /* DoStatement: jp_DO Statement jp_WHILE jp_PARESTART Expression jp_PAREEND jp_SEMICOL  */
-#line 1892 "cmDependsJavaParser.y"
+#line 1891 "cmDependsJavaParser.y"
 {
   jpElementStart(7);
 
 }
-#line 4680 "cmDependsJavaParser.cxx"
+#line 4679 "cmDependsJavaParser.cxx"
     break;
 
   case 210: /* ForStatement: jp_FOR jp_PARESTART ForInitopt jp_SEMICOL Expressionopt jp_SEMICOL ForUpdateopt jp_PAREEND Statement  */
-#line 1900 "cmDependsJavaParser.y"
+#line 1899 "cmDependsJavaParser.y"
 {
   jpElementStart(9);
 
 }
-#line 4689 "cmDependsJavaParser.cxx"
+#line 4688 "cmDependsJavaParser.cxx"
     break;
 
   case 211: /* ForUpdateopt: %empty  */
-#line 1906 "cmDependsJavaParser.y"
+#line 1905 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4700 "cmDependsJavaParser.cxx"
+#line 4699 "cmDependsJavaParser.cxx"
     break;
 
   case 212: /* ForUpdateopt: ForUpdate  */
-#line 1914 "cmDependsJavaParser.y"
+#line 1913 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4708,22 +4707,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4712 "cmDependsJavaParser.cxx"
+#line 4711 "cmDependsJavaParser.cxx"
     break;
 
   case 213: /* ForInitopt: %empty  */
-#line 1923 "cmDependsJavaParser.y"
+#line 1922 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4723 "cmDependsJavaParser.cxx"
+#line 4722 "cmDependsJavaParser.cxx"
     break;
 
   case 214: /* ForInitopt: ForInit  */
-#line 1931 "cmDependsJavaParser.y"
+#line 1930 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4731,33 +4730,33 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4735 "cmDependsJavaParser.cxx"
+#line 4734 "cmDependsJavaParser.cxx"
     break;
 
   case 215: /* ForStatementNoShortIf: jp_FOR jp_PARESTART ForInitopt jp_SEMICOL Expressionopt jp_SEMICOL ForUpdateopt jp_PAREEND StatementNoShortIf  */
-#line 1942 "cmDependsJavaParser.y"
+#line 1941 "cmDependsJavaParser.y"
 {
   jpElementStart(9);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4746 "cmDependsJavaParser.cxx"
+#line 4745 "cmDependsJavaParser.cxx"
     break;
 
   case 216: /* Expressionopt: %empty  */
-#line 1950 "cmDependsJavaParser.y"
+#line 1949 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4757 "cmDependsJavaParser.cxx"
+#line 4756 "cmDependsJavaParser.cxx"
     break;
 
   case 217: /* Expressionopt: Expression  */
-#line 1958 "cmDependsJavaParser.y"
+#line 1957 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4765,11 +4764,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4769 "cmDependsJavaParser.cxx"
+#line 4768 "cmDependsJavaParser.cxx"
     break;
 
   case 218: /* ForInit: StatementExpressionList  */
-#line 1968 "cmDependsJavaParser.y"
+#line 1967 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4777,11 +4776,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4781 "cmDependsJavaParser.cxx"
+#line 4780 "cmDependsJavaParser.cxx"
     break;
 
   case 219: /* ForInit: LocalVariableDeclaration  */
-#line 1977 "cmDependsJavaParser.y"
+#line 1976 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4789,11 +4788,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4793 "cmDependsJavaParser.cxx"
+#line 4792 "cmDependsJavaParser.cxx"
     break;
 
   case 220: /* ForUpdate: StatementExpressionList  */
-#line 1987 "cmDependsJavaParser.y"
+#line 1986 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4801,11 +4800,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4805 "cmDependsJavaParser.cxx"
+#line 4804 "cmDependsJavaParser.cxx"
     break;
 
   case 221: /* StatementExpressionList: StatementExpression  */
-#line 1997 "cmDependsJavaParser.y"
+#line 1996 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4813,11 +4812,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4817 "cmDependsJavaParser.cxx"
+#line 4816 "cmDependsJavaParser.cxx"
     break;
 
   case 222: /* StatementExpressionList: StatementExpressionList jp_COMMA StatementExpression  */
-#line 2006 "cmDependsJavaParser.y"
+#line 2005 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4825,11 +4824,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4829 "cmDependsJavaParser.cxx"
+#line 4828 "cmDependsJavaParser.cxx"
     break;
 
   case 223: /* AssertStatement: jp_ASSERT Expression jp_SEMICOL  */
-#line 2016 "cmDependsJavaParser.y"
+#line 2015 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4837,11 +4836,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4841 "cmDependsJavaParser.cxx"
+#line 4840 "cmDependsJavaParser.cxx"
     break;
 
   case 224: /* AssertStatement: jp_ASSERT Expression jp_COLON Expression jp_SEMICOL  */
-#line 2025 "cmDependsJavaParser.y"
+#line 2024 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4849,11 +4848,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4853 "cmDependsJavaParser.cxx"
+#line 4852 "cmDependsJavaParser.cxx"
     break;
 
   case 225: /* BreakStatement: jp_BREAK Identifieropt jp_SEMICOL  */
-#line 2035 "cmDependsJavaParser.y"
+#line 2034 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-1].str)));
@@ -4862,31 +4861,31 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4866 "cmDependsJavaParser.cxx"
+#line 4865 "cmDependsJavaParser.cxx"
     break;
 
   case 226: /* Identifieropt: %empty  */
-#line 2045 "cmDependsJavaParser.y"
+#line 2044 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4877 "cmDependsJavaParser.cxx"
+#line 4876 "cmDependsJavaParser.cxx"
     break;
 
   case 227: /* Identifieropt: Identifier  */
-#line 2053 "cmDependsJavaParser.y"
+#line 2052 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 4886 "cmDependsJavaParser.cxx"
+#line 4885 "cmDependsJavaParser.cxx"
     break;
 
   case 228: /* ContinueStatement: jp_CONTINUE Identifieropt jp_SEMICOL  */
-#line 2060 "cmDependsJavaParser.y"
+#line 2059 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[-1].str)));
@@ -4895,11 +4894,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4899 "cmDependsJavaParser.cxx"
+#line 4898 "cmDependsJavaParser.cxx"
     break;
 
   case 229: /* ReturnStatement: jp_RETURN Expressionopt jp_SEMICOL  */
-#line 2071 "cmDependsJavaParser.y"
+#line 2070 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4907,11 +4906,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4911 "cmDependsJavaParser.cxx"
+#line 4910 "cmDependsJavaParser.cxx"
     break;
 
   case 230: /* ThrowStatement: jp_THROW Expression jp_SEMICOL  */
-#line 2081 "cmDependsJavaParser.y"
+#line 2080 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4919,11 +4918,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4923 "cmDependsJavaParser.cxx"
+#line 4922 "cmDependsJavaParser.cxx"
     break;
 
   case 231: /* SynchronizedStatement: jp_SYNCHRONIZED jp_PARESTART Expression jp_PAREEND Block  */
-#line 2091 "cmDependsJavaParser.y"
+#line 2090 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -4931,11 +4930,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4935 "cmDependsJavaParser.cxx"
+#line 4934 "cmDependsJavaParser.cxx"
     break;
 
   case 232: /* TryStatement: jp_TRY Block Catches  */
-#line 2101 "cmDependsJavaParser.y"
+#line 2100 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -4943,11 +4942,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4947 "cmDependsJavaParser.cxx"
+#line 4946 "cmDependsJavaParser.cxx"
     break;
 
   case 233: /* TryStatement: jp_TRY Block Catchesopt Finally  */
-#line 2110 "cmDependsJavaParser.y"
+#line 2109 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -4955,22 +4954,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4959 "cmDependsJavaParser.cxx"
+#line 4958 "cmDependsJavaParser.cxx"
     break;
 
   case 234: /* Catchesopt: %empty  */
-#line 2119 "cmDependsJavaParser.y"
+#line 2118 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4970 "cmDependsJavaParser.cxx"
+#line 4969 "cmDependsJavaParser.cxx"
     break;
 
   case 235: /* Catchesopt: Catches  */
-#line 2127 "cmDependsJavaParser.y"
+#line 2126 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4978,11 +4977,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4982 "cmDependsJavaParser.cxx"
+#line 4981 "cmDependsJavaParser.cxx"
     break;
 
   case 236: /* Catches: CatchClause  */
-#line 2137 "cmDependsJavaParser.y"
+#line 2136 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -4990,11 +4989,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 4994 "cmDependsJavaParser.cxx"
+#line 4993 "cmDependsJavaParser.cxx"
     break;
 
   case 237: /* Catches: Catches CatchClause  */
-#line 2146 "cmDependsJavaParser.y"
+#line 2145 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5002,20 +5001,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5006 "cmDependsJavaParser.cxx"
+#line 5005 "cmDependsJavaParser.cxx"
     break;
 
   case 238: /* CatchClause: jp_CATCH jp_PARESTART FormalParameter jp_PAREEND Block  */
-#line 2156 "cmDependsJavaParser.y"
+#line 2155 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 5015 "cmDependsJavaParser.cxx"
+#line 5014 "cmDependsJavaParser.cxx"
     break;
 
   case 239: /* Finally: jp_FINALLY Block  */
-#line 2163 "cmDependsJavaParser.y"
+#line 2162 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5023,11 +5022,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5027 "cmDependsJavaParser.cxx"
+#line 5026 "cmDependsJavaParser.cxx"
     break;
 
   case 240: /* Primary: PrimaryNoNewArray  */
-#line 2173 "cmDependsJavaParser.y"
+#line 2172 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5035,11 +5034,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5039 "cmDependsJavaParser.cxx"
+#line 5038 "cmDependsJavaParser.cxx"
     break;
 
   case 241: /* Primary: ArrayCreationExpression  */
-#line 2182 "cmDependsJavaParser.y"
+#line 2181 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5047,11 +5046,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5051 "cmDependsJavaParser.cxx"
+#line 5050 "cmDependsJavaParser.cxx"
     break;
 
   case 242: /* PrimaryNoNewArray: Literal  */
-#line 2192 "cmDependsJavaParser.y"
+#line 2191 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5059,20 +5058,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5063 "cmDependsJavaParser.cxx"
+#line 5062 "cmDependsJavaParser.cxx"
     break;
 
   case 243: /* PrimaryNoNewArray: jp_THIS  */
-#line 2201 "cmDependsJavaParser.y"
+#line 2200 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
 
 }
-#line 5072 "cmDependsJavaParser.cxx"
+#line 5071 "cmDependsJavaParser.cxx"
     break;
 
   case 244: /* PrimaryNoNewArray: jp_PARESTART Expression jp_PAREEND  */
-#line 2207 "cmDependsJavaParser.y"
+#line 2206 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5080,11 +5079,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5084 "cmDependsJavaParser.cxx"
+#line 5083 "cmDependsJavaParser.cxx"
     break;
 
   case 245: /* PrimaryNoNewArray: ClassInstanceCreationExpression  */
-#line 2216 "cmDependsJavaParser.y"
+#line 2215 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5092,11 +5091,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5096 "cmDependsJavaParser.cxx"
+#line 5095 "cmDependsJavaParser.cxx"
     break;
 
   case 246: /* PrimaryNoNewArray: FieldAccess  */
-#line 2225 "cmDependsJavaParser.y"
+#line 2224 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5104,11 +5103,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5108 "cmDependsJavaParser.cxx"
+#line 5107 "cmDependsJavaParser.cxx"
     break;
 
   case 247: /* PrimaryNoNewArray: MethodInvocation  */
-#line 2234 "cmDependsJavaParser.y"
+#line 2233 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5116,11 +5115,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5120 "cmDependsJavaParser.cxx"
+#line 5119 "cmDependsJavaParser.cxx"
     break;
 
   case 248: /* PrimaryNoNewArray: ArrayAccess  */
-#line 2243 "cmDependsJavaParser.y"
+#line 2242 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5128,11 +5127,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5132 "cmDependsJavaParser.cxx"
+#line 5131 "cmDependsJavaParser.cxx"
     break;
 
   case 249: /* ClassInstanceCreationExpression: New ClassType jp_PARESTART ArgumentListopt jp_PAREEND ClassBodyOpt  */
-#line 2253 "cmDependsJavaParser.y"
+#line 2252 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   jpCheckEmpty(6);
@@ -5140,22 +5139,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5144 "cmDependsJavaParser.cxx"
+#line 5143 "cmDependsJavaParser.cxx"
     break;
 
   case 250: /* ClassBodyOpt: %empty  */
-#line 2262 "cmDependsJavaParser.y"
+#line 2261 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5155 "cmDependsJavaParser.cxx"
+#line 5154 "cmDependsJavaParser.cxx"
     break;
 
   case 251: /* ClassBodyOpt: ClassBody  */
-#line 2270 "cmDependsJavaParser.y"
+#line 2269 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5163,22 +5162,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5167 "cmDependsJavaParser.cxx"
+#line 5166 "cmDependsJavaParser.cxx"
     break;
 
   case 252: /* ArgumentListopt: %empty  */
-#line 2279 "cmDependsJavaParser.y"
+#line 2278 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5178 "cmDependsJavaParser.cxx"
+#line 5177 "cmDependsJavaParser.cxx"
     break;
 
   case 253: /* ArgumentListopt: ArgumentList  */
-#line 2287 "cmDependsJavaParser.y"
+#line 2286 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5186,11 +5185,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5190 "cmDependsJavaParser.cxx"
+#line 5189 "cmDependsJavaParser.cxx"
     break;
 
   case 254: /* ArgumentList: Expression  */
-#line 2297 "cmDependsJavaParser.y"
+#line 2296 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5198,11 +5197,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5202 "cmDependsJavaParser.cxx"
+#line 5201 "cmDependsJavaParser.cxx"
     break;
 
   case 255: /* ArgumentList: ArgumentList jp_COMMA Expression  */
-#line 2306 "cmDependsJavaParser.y"
+#line 2305 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5210,11 +5209,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5214 "cmDependsJavaParser.cxx"
+#line 5213 "cmDependsJavaParser.cxx"
     break;
 
   case 256: /* ArrayCreationExpression: New PrimitiveType DimExprs Dimsopt  */
-#line 2316 "cmDependsJavaParser.y"
+#line 2315 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5222,11 +5221,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5226 "cmDependsJavaParser.cxx"
+#line 5225 "cmDependsJavaParser.cxx"
     break;
 
   case 257: /* ArrayCreationExpression: New ClassOrInterfaceType DimExprs Dimsopt  */
-#line 2325 "cmDependsJavaParser.y"
+#line 2324 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5234,11 +5233,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5238 "cmDependsJavaParser.cxx"
+#line 5237 "cmDependsJavaParser.cxx"
     break;
 
   case 258: /* ArrayCreationExpression: New PrimitiveType Dims ArrayInitializer  */
-#line 2334 "cmDependsJavaParser.y"
+#line 2333 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5246,11 +5245,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5250 "cmDependsJavaParser.cxx"
+#line 5249 "cmDependsJavaParser.cxx"
     break;
 
   case 259: /* ArrayCreationExpression: New ClassOrInterfaceType Dims ArrayInitializer  */
-#line 2343 "cmDependsJavaParser.y"
+#line 2342 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5258,22 +5257,22 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5262 "cmDependsJavaParser.cxx"
+#line 5261 "cmDependsJavaParser.cxx"
     break;
 
   case 260: /* Dimsopt: %empty  */
-#line 2352 "cmDependsJavaParser.y"
+#line 2351 "cmDependsJavaParser.y"
 {
   jpElementStart(0);
   (yyval.str) = 0;
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5273 "cmDependsJavaParser.cxx"
+#line 5272 "cmDependsJavaParser.cxx"
     break;
 
   case 261: /* Dimsopt: Dims  */
-#line 2360 "cmDependsJavaParser.y"
+#line 2359 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5281,11 +5280,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5285 "cmDependsJavaParser.cxx"
+#line 5284 "cmDependsJavaParser.cxx"
     break;
 
   case 262: /* DimExprs: DimExpr  */
-#line 2370 "cmDependsJavaParser.y"
+#line 2369 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5293,11 +5292,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5297 "cmDependsJavaParser.cxx"
+#line 5296 "cmDependsJavaParser.cxx"
     break;
 
   case 263: /* DimExprs: DimExprs DimExpr  */
-#line 2379 "cmDependsJavaParser.y"
+#line 2378 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5305,11 +5304,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5309 "cmDependsJavaParser.cxx"
+#line 5308 "cmDependsJavaParser.cxx"
     break;
 
   case 264: /* DimExpr: jp_BRACKETSTART Expression jp_BRACKETEND  */
-#line 2389 "cmDependsJavaParser.y"
+#line 2388 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5317,29 +5316,29 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5321 "cmDependsJavaParser.cxx"
+#line 5320 "cmDependsJavaParser.cxx"
     break;
 
   case 265: /* Dims: jp_BRACKETSTART jp_BRACKETEND  */
-#line 2399 "cmDependsJavaParser.y"
+#line 2398 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
 
 }
-#line 5330 "cmDependsJavaParser.cxx"
+#line 5329 "cmDependsJavaParser.cxx"
     break;
 
   case 266: /* Dims: Dims jp_BRACKETSTART jp_BRACKETEND  */
-#line 2405 "cmDependsJavaParser.y"
+#line 2404 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
 
 }
-#line 5339 "cmDependsJavaParser.cxx"
+#line 5338 "cmDependsJavaParser.cxx"
     break;
 
   case 267: /* FieldAccess: Primary jp_DOT Identifier  */
-#line 2412 "cmDependsJavaParser.y"
+#line 2411 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5348,11 +5347,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5352 "cmDependsJavaParser.cxx"
+#line 5351 "cmDependsJavaParser.cxx"
     break;
 
   case 268: /* FieldAccess: jp_SUPER jp_DOT Identifier  */
-#line 2422 "cmDependsJavaParser.y"
+#line 2421 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5361,11 +5360,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5365 "cmDependsJavaParser.cxx"
+#line 5364 "cmDependsJavaParser.cxx"
     break;
 
   case 269: /* FieldAccess: jp_THIS jp_DOT Identifier  */
-#line 2432 "cmDependsJavaParser.y"
+#line 2431 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5374,11 +5373,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5378 "cmDependsJavaParser.cxx"
+#line 5377 "cmDependsJavaParser.cxx"
     break;
 
   case 270: /* FieldAccess: Primary jp_DOT jp_THIS  */
-#line 2442 "cmDependsJavaParser.y"
+#line 2441 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5387,11 +5386,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5391 "cmDependsJavaParser.cxx"
+#line 5390 "cmDependsJavaParser.cxx"
     break;
 
   case 271: /* MethodInvocation: Name jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2453 "cmDependsJavaParser.y"
+#line 2452 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5400,11 +5399,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5404 "cmDependsJavaParser.cxx"
+#line 5403 "cmDependsJavaParser.cxx"
     break;
 
   case 272: /* MethodInvocation: Primary jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2463 "cmDependsJavaParser.y"
+#line 2462 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-5].str)));
@@ -5414,11 +5413,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5418 "cmDependsJavaParser.cxx"
+#line 5417 "cmDependsJavaParser.cxx"
     break;
 
   case 273: /* MethodInvocation: jp_SUPER jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2474 "cmDependsJavaParser.y"
+#line 2473 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5427,11 +5426,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5431 "cmDependsJavaParser.cxx"
+#line 5430 "cmDependsJavaParser.cxx"
     break;
 
   case 274: /* MethodInvocation: jp_THIS jp_DOT Identifier jp_PARESTART ArgumentListopt jp_PAREEND  */
-#line 2484 "cmDependsJavaParser.y"
+#line 2483 "cmDependsJavaParser.y"
 {
   jpElementStart(6);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5440,11 +5439,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5444 "cmDependsJavaParser.cxx"
+#line 5443 "cmDependsJavaParser.cxx"
     break;
 
   case 275: /* ArrayAccess: Name jp_BRACKETSTART Expression jp_BRACKETEND  */
-#line 2495 "cmDependsJavaParser.y"
+#line 2494 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   yyGetParser->DeallocateParserType(&((yyvsp[-3].str)));
@@ -5453,11 +5452,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5457 "cmDependsJavaParser.cxx"
+#line 5456 "cmDependsJavaParser.cxx"
     break;
 
   case 276: /* ArrayAccess: PrimaryNoNewArray jp_BRACKETSTART Expression jp_BRACKETEND  */
-#line 2505 "cmDependsJavaParser.y"
+#line 2504 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5465,11 +5464,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5469 "cmDependsJavaParser.cxx"
+#line 5468 "cmDependsJavaParser.cxx"
     break;
 
   case 277: /* PostfixExpression: Primary  */
-#line 2515 "cmDependsJavaParser.y"
+#line 2514 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5477,11 +5476,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5481 "cmDependsJavaParser.cxx"
+#line 5480 "cmDependsJavaParser.cxx"
     break;
 
   case 278: /* PostfixExpression: Name  */
-#line 2524 "cmDependsJavaParser.y"
+#line 2523 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -5489,11 +5488,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5493 "cmDependsJavaParser.cxx"
+#line 5492 "cmDependsJavaParser.cxx"
     break;
 
   case 279: /* PostfixExpression: ArrayType jp_DOT jp_CLASS  */
-#line 2533 "cmDependsJavaParser.y"
+#line 2532 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5501,11 +5500,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5505 "cmDependsJavaParser.cxx"
+#line 5504 "cmDependsJavaParser.cxx"
     break;
 
   case 280: /* PostfixExpression: PostIncrementExpression  */
-#line 2542 "cmDependsJavaParser.y"
+#line 2541 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5513,11 +5512,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5517 "cmDependsJavaParser.cxx"
+#line 5516 "cmDependsJavaParser.cxx"
     break;
 
   case 281: /* PostfixExpression: PostDecrementExpression  */
-#line 2551 "cmDependsJavaParser.y"
+#line 2550 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5525,11 +5524,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5529 "cmDependsJavaParser.cxx"
+#line 5528 "cmDependsJavaParser.cxx"
     break;
 
   case 282: /* PostIncrementExpression: PostfixExpression jp_PLUSPLUS  */
-#line 2561 "cmDependsJavaParser.y"
+#line 2560 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5537,11 +5536,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5541 "cmDependsJavaParser.cxx"
+#line 5540 "cmDependsJavaParser.cxx"
     break;
 
   case 283: /* PostDecrementExpression: PostfixExpression jp_MINUSMINUS  */
-#line 2571 "cmDependsJavaParser.y"
+#line 2570 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5549,11 +5548,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5553 "cmDependsJavaParser.cxx"
+#line 5552 "cmDependsJavaParser.cxx"
     break;
 
   case 284: /* UnaryExpression: PreIncrementExpression  */
-#line 2581 "cmDependsJavaParser.y"
+#line 2580 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5561,11 +5560,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5565 "cmDependsJavaParser.cxx"
+#line 5564 "cmDependsJavaParser.cxx"
     break;
 
   case 285: /* UnaryExpression: PreDecrementExpression  */
-#line 2590 "cmDependsJavaParser.y"
+#line 2589 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5573,11 +5572,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5577 "cmDependsJavaParser.cxx"
+#line 5576 "cmDependsJavaParser.cxx"
     break;
 
   case 286: /* UnaryExpression: jp_PLUS UnaryExpression  */
-#line 2599 "cmDependsJavaParser.y"
+#line 2598 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5585,11 +5584,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5589 "cmDependsJavaParser.cxx"
+#line 5588 "cmDependsJavaParser.cxx"
     break;
 
   case 287: /* UnaryExpression: jp_MINUS UnaryExpression  */
-#line 2608 "cmDependsJavaParser.y"
+#line 2607 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5597,11 +5596,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5601 "cmDependsJavaParser.cxx"
+#line 5600 "cmDependsJavaParser.cxx"
     break;
 
   case 288: /* UnaryExpression: UnaryExpressionNotPlusMinus  */
-#line 2617 "cmDependsJavaParser.y"
+#line 2616 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5609,11 +5608,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5613 "cmDependsJavaParser.cxx"
+#line 5612 "cmDependsJavaParser.cxx"
     break;
 
   case 289: /* PreIncrementExpression: jp_PLUSPLUS UnaryExpression  */
-#line 2627 "cmDependsJavaParser.y"
+#line 2626 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5621,11 +5620,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5625 "cmDependsJavaParser.cxx"
+#line 5624 "cmDependsJavaParser.cxx"
     break;
 
   case 290: /* PreDecrementExpression: jp_MINUSMINUS UnaryExpression  */
-#line 2637 "cmDependsJavaParser.y"
+#line 2636 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5633,11 +5632,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5637 "cmDependsJavaParser.cxx"
+#line 5636 "cmDependsJavaParser.cxx"
     break;
 
   case 291: /* UnaryExpressionNotPlusMinus: PostfixExpression  */
-#line 2647 "cmDependsJavaParser.y"
+#line 2646 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5645,11 +5644,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5649 "cmDependsJavaParser.cxx"
+#line 5648 "cmDependsJavaParser.cxx"
     break;
 
   case 292: /* UnaryExpressionNotPlusMinus: jp_TILDE UnaryExpression  */
-#line 2656 "cmDependsJavaParser.y"
+#line 2655 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5657,11 +5656,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5661 "cmDependsJavaParser.cxx"
+#line 5660 "cmDependsJavaParser.cxx"
     break;
 
   case 293: /* UnaryExpressionNotPlusMinus: jp_EXCLAMATION UnaryExpression  */
-#line 2665 "cmDependsJavaParser.y"
+#line 2664 "cmDependsJavaParser.y"
 {
   jpElementStart(2);
   jpCheckEmpty(2);
@@ -5669,11 +5668,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5673 "cmDependsJavaParser.cxx"
+#line 5672 "cmDependsJavaParser.cxx"
     break;
 
   case 294: /* UnaryExpressionNotPlusMinus: CastExpression  */
-#line 2674 "cmDependsJavaParser.y"
+#line 2673 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5681,11 +5680,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5685 "cmDependsJavaParser.cxx"
+#line 5684 "cmDependsJavaParser.cxx"
     break;
 
   case 295: /* CastExpression: jp_PARESTART PrimitiveType Dimsopt jp_PAREEND UnaryExpression  */
-#line 2684 "cmDependsJavaParser.y"
+#line 2683 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -5693,11 +5692,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5697 "cmDependsJavaParser.cxx"
+#line 5696 "cmDependsJavaParser.cxx"
     break;
 
   case 296: /* CastExpression: jp_PARESTART Expression jp_PAREEND UnaryExpressionNotPlusMinus  */
-#line 2693 "cmDependsJavaParser.y"
+#line 2692 "cmDependsJavaParser.y"
 {
   jpElementStart(4);
   jpCheckEmpty(4);
@@ -5705,20 +5704,20 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5709 "cmDependsJavaParser.cxx"
+#line 5708 "cmDependsJavaParser.cxx"
     break;
 
   case 297: /* CastExpression: jp_PARESTART Name Dims jp_PAREEND UnaryExpressionNotPlusMinus  */
-#line 2702 "cmDependsJavaParser.y"
+#line 2701 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
 
 }
-#line 5718 "cmDependsJavaParser.cxx"
+#line 5717 "cmDependsJavaParser.cxx"
     break;
 
   case 298: /* MultiplicativeExpression: UnaryExpression  */
-#line 2709 "cmDependsJavaParser.y"
+#line 2708 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5726,11 +5725,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5730 "cmDependsJavaParser.cxx"
+#line 5729 "cmDependsJavaParser.cxx"
     break;
 
   case 299: /* MultiplicativeExpression: MultiplicativeExpression jp_TIMES UnaryExpression  */
-#line 2718 "cmDependsJavaParser.y"
+#line 2717 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5738,11 +5737,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5742 "cmDependsJavaParser.cxx"
+#line 5741 "cmDependsJavaParser.cxx"
     break;
 
   case 300: /* MultiplicativeExpression: MultiplicativeExpression jp_DIVIDE UnaryExpression  */
-#line 2727 "cmDependsJavaParser.y"
+#line 2726 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5750,11 +5749,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5754 "cmDependsJavaParser.cxx"
+#line 5753 "cmDependsJavaParser.cxx"
     break;
 
   case 301: /* MultiplicativeExpression: MultiplicativeExpression jp_PERCENT UnaryExpression  */
-#line 2736 "cmDependsJavaParser.y"
+#line 2735 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5762,11 +5761,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5766 "cmDependsJavaParser.cxx"
+#line 5765 "cmDependsJavaParser.cxx"
     break;
 
   case 302: /* AdditiveExpression: MultiplicativeExpression  */
-#line 2746 "cmDependsJavaParser.y"
+#line 2745 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5774,11 +5773,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5778 "cmDependsJavaParser.cxx"
+#line 5777 "cmDependsJavaParser.cxx"
     break;
 
   case 303: /* AdditiveExpression: AdditiveExpression jp_PLUS MultiplicativeExpression  */
-#line 2755 "cmDependsJavaParser.y"
+#line 2754 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5786,11 +5785,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5790 "cmDependsJavaParser.cxx"
+#line 5789 "cmDependsJavaParser.cxx"
     break;
 
   case 304: /* AdditiveExpression: AdditiveExpression jp_MINUS MultiplicativeExpression  */
-#line 2764 "cmDependsJavaParser.y"
+#line 2763 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5798,11 +5797,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5802 "cmDependsJavaParser.cxx"
+#line 5801 "cmDependsJavaParser.cxx"
     break;
 
   case 305: /* ShiftExpression: AdditiveExpression  */
-#line 2774 "cmDependsJavaParser.y"
+#line 2773 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5810,11 +5809,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5814 "cmDependsJavaParser.cxx"
+#line 5813 "cmDependsJavaParser.cxx"
     break;
 
   case 306: /* ShiftExpression: ShiftExpression jp_LTLT AdditiveExpression  */
-#line 2783 "cmDependsJavaParser.y"
+#line 2782 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5822,11 +5821,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5826 "cmDependsJavaParser.cxx"
+#line 5825 "cmDependsJavaParser.cxx"
     break;
 
   case 307: /* ShiftExpression: ShiftExpression jp_GTGT AdditiveExpression  */
-#line 2792 "cmDependsJavaParser.y"
+#line 2791 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5834,11 +5833,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5838 "cmDependsJavaParser.cxx"
+#line 5837 "cmDependsJavaParser.cxx"
     break;
 
   case 308: /* ShiftExpression: ShiftExpression jp_GTGTGT AdditiveExpression  */
-#line 2801 "cmDependsJavaParser.y"
+#line 2800 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5846,11 +5845,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5850 "cmDependsJavaParser.cxx"
+#line 5849 "cmDependsJavaParser.cxx"
     break;
 
   case 309: /* RelationalExpression: ShiftExpression  */
-#line 2811 "cmDependsJavaParser.y"
+#line 2810 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5858,11 +5857,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5862 "cmDependsJavaParser.cxx"
+#line 5861 "cmDependsJavaParser.cxx"
     break;
 
   case 310: /* RelationalExpression: RelationalExpression jp_LESSTHAN ShiftExpression  */
-#line 2820 "cmDependsJavaParser.y"
+#line 2819 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5870,11 +5869,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5874 "cmDependsJavaParser.cxx"
+#line 5873 "cmDependsJavaParser.cxx"
     break;
 
   case 311: /* RelationalExpression: RelationalExpression jp_GREATER ShiftExpression  */
-#line 2829 "cmDependsJavaParser.y"
+#line 2828 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5882,11 +5881,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5886 "cmDependsJavaParser.cxx"
+#line 5885 "cmDependsJavaParser.cxx"
     break;
 
   case 312: /* RelationalExpression: RelationalExpression jp_LTEQUALS ShiftExpression  */
-#line 2838 "cmDependsJavaParser.y"
+#line 2837 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5894,11 +5893,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5898 "cmDependsJavaParser.cxx"
+#line 5897 "cmDependsJavaParser.cxx"
     break;
 
   case 313: /* RelationalExpression: RelationalExpression jp_GTEQUALS ShiftExpression  */
-#line 2847 "cmDependsJavaParser.y"
+#line 2846 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5906,11 +5905,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5910 "cmDependsJavaParser.cxx"
+#line 5909 "cmDependsJavaParser.cxx"
     break;
 
   case 314: /* RelationalExpression: RelationalExpression jp_INSTANCEOF ReferenceType  */
-#line 2856 "cmDependsJavaParser.y"
+#line 2855 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5918,11 +5917,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5922 "cmDependsJavaParser.cxx"
+#line 5921 "cmDependsJavaParser.cxx"
     break;
 
   case 315: /* EqualityExpression: RelationalExpression  */
-#line 2866 "cmDependsJavaParser.y"
+#line 2865 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5930,11 +5929,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5934 "cmDependsJavaParser.cxx"
+#line 5933 "cmDependsJavaParser.cxx"
     break;
 
   case 316: /* EqualityExpression: EqualityExpression jp_EQUALSEQUALS RelationalExpression  */
-#line 2875 "cmDependsJavaParser.y"
+#line 2874 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5942,11 +5941,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5946 "cmDependsJavaParser.cxx"
+#line 5945 "cmDependsJavaParser.cxx"
     break;
 
   case 317: /* EqualityExpression: EqualityExpression jp_EXCLAMATIONEQUALS RelationalExpression  */
-#line 2884 "cmDependsJavaParser.y"
+#line 2883 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5954,11 +5953,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5958 "cmDependsJavaParser.cxx"
+#line 5957 "cmDependsJavaParser.cxx"
     break;
 
   case 318: /* AndExpression: EqualityExpression  */
-#line 2894 "cmDependsJavaParser.y"
+#line 2893 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5966,11 +5965,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5970 "cmDependsJavaParser.cxx"
+#line 5969 "cmDependsJavaParser.cxx"
     break;
 
   case 319: /* AndExpression: AndExpression jp_AND EqualityExpression  */
-#line 2903 "cmDependsJavaParser.y"
+#line 2902 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -5978,11 +5977,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5982 "cmDependsJavaParser.cxx"
+#line 5981 "cmDependsJavaParser.cxx"
     break;
 
   case 320: /* ExclusiveOrExpression: AndExpression  */
-#line 2913 "cmDependsJavaParser.y"
+#line 2912 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -5990,11 +5989,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 5994 "cmDependsJavaParser.cxx"
+#line 5993 "cmDependsJavaParser.cxx"
     break;
 
   case 321: /* ExclusiveOrExpression: ExclusiveOrExpression jp_CARROT AndExpression  */
-#line 2922 "cmDependsJavaParser.y"
+#line 2921 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6002,11 +6001,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6006 "cmDependsJavaParser.cxx"
+#line 6005 "cmDependsJavaParser.cxx"
     break;
 
   case 322: /* InclusiveOrExpression: ExclusiveOrExpression  */
-#line 2932 "cmDependsJavaParser.y"
+#line 2931 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6014,11 +6013,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6018 "cmDependsJavaParser.cxx"
+#line 6017 "cmDependsJavaParser.cxx"
     break;
 
   case 323: /* InclusiveOrExpression: InclusiveOrExpression jp_PIPE ExclusiveOrExpression  */
-#line 2941 "cmDependsJavaParser.y"
+#line 2940 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6026,11 +6025,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6030 "cmDependsJavaParser.cxx"
+#line 6029 "cmDependsJavaParser.cxx"
     break;
 
   case 324: /* ConditionalAndExpression: InclusiveOrExpression  */
-#line 2951 "cmDependsJavaParser.y"
+#line 2950 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6038,11 +6037,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6042 "cmDependsJavaParser.cxx"
+#line 6041 "cmDependsJavaParser.cxx"
     break;
 
   case 325: /* ConditionalAndExpression: ConditionalAndExpression jp_ANDAND InclusiveOrExpression  */
-#line 2960 "cmDependsJavaParser.y"
+#line 2959 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6050,11 +6049,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6054 "cmDependsJavaParser.cxx"
+#line 6053 "cmDependsJavaParser.cxx"
     break;
 
   case 326: /* ConditionalOrExpression: ConditionalAndExpression  */
-#line 2970 "cmDependsJavaParser.y"
+#line 2969 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6062,11 +6061,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6066 "cmDependsJavaParser.cxx"
+#line 6065 "cmDependsJavaParser.cxx"
     break;
 
   case 327: /* ConditionalOrExpression: ConditionalOrExpression jp_PIPEPIPE ConditionalAndExpression  */
-#line 2979 "cmDependsJavaParser.y"
+#line 2978 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6074,11 +6073,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6078 "cmDependsJavaParser.cxx"
+#line 6077 "cmDependsJavaParser.cxx"
     break;
 
   case 328: /* ConditionalExpression: ConditionalOrExpression  */
-#line 2989 "cmDependsJavaParser.y"
+#line 2988 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6086,11 +6085,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6090 "cmDependsJavaParser.cxx"
+#line 6089 "cmDependsJavaParser.cxx"
     break;
 
   case 329: /* ConditionalExpression: ConditionalOrExpression jp_QUESTION Expression jp_COLON ConditionalExpression  */
-#line 2998 "cmDependsJavaParser.y"
+#line 2997 "cmDependsJavaParser.y"
 {
   jpElementStart(5);
   jpCheckEmpty(5);
@@ -6098,11 +6097,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6102 "cmDependsJavaParser.cxx"
+#line 6101 "cmDependsJavaParser.cxx"
     break;
 
   case 330: /* AssignmentExpression: ConditionalExpression  */
-#line 3008 "cmDependsJavaParser.y"
+#line 3007 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6110,11 +6109,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6114 "cmDependsJavaParser.cxx"
+#line 6113 "cmDependsJavaParser.cxx"
     break;
 
   case 331: /* AssignmentExpression: Assignment  */
-#line 3017 "cmDependsJavaParser.y"
+#line 3016 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6122,11 +6121,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6126 "cmDependsJavaParser.cxx"
+#line 6125 "cmDependsJavaParser.cxx"
     break;
 
   case 332: /* Assignment: LeftHandSide AssignmentOperator AssignmentExpression  */
-#line 3027 "cmDependsJavaParser.y"
+#line 3026 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpCheckEmpty(3);
@@ -6134,11 +6133,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6138 "cmDependsJavaParser.cxx"
+#line 6137 "cmDependsJavaParser.cxx"
     break;
 
   case 333: /* LeftHandSide: Name  */
-#line 3037 "cmDependsJavaParser.y"
+#line 3036 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   yyGetParser->DeallocateParserType(&((yyvsp[0].str)));
@@ -6147,11 +6146,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6151 "cmDependsJavaParser.cxx"
+#line 6150 "cmDependsJavaParser.cxx"
     break;
 
   case 334: /* LeftHandSide: FieldAccess  */
-#line 3047 "cmDependsJavaParser.y"
+#line 3046 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6159,11 +6158,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6163 "cmDependsJavaParser.cxx"
+#line 6162 "cmDependsJavaParser.cxx"
     break;
 
   case 335: /* LeftHandSide: ArrayAccess  */
-#line 3056 "cmDependsJavaParser.y"
+#line 3055 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6171,11 +6170,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6175 "cmDependsJavaParser.cxx"
+#line 6174 "cmDependsJavaParser.cxx"
     break;
 
   case 336: /* AssignmentOperator: jp_EQUALS  */
-#line 3066 "cmDependsJavaParser.y"
+#line 3065 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6183,11 +6182,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6187 "cmDependsJavaParser.cxx"
+#line 6186 "cmDependsJavaParser.cxx"
     break;
 
   case 337: /* AssignmentOperator: jp_TIMESEQUALS  */
-#line 3075 "cmDependsJavaParser.y"
+#line 3074 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6195,11 +6194,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6199 "cmDependsJavaParser.cxx"
+#line 6198 "cmDependsJavaParser.cxx"
     break;
 
   case 338: /* AssignmentOperator: jp_DIVIDEEQUALS  */
-#line 3084 "cmDependsJavaParser.y"
+#line 3083 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6207,11 +6206,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6211 "cmDependsJavaParser.cxx"
+#line 6210 "cmDependsJavaParser.cxx"
     break;
 
   case 339: /* AssignmentOperator: jp_PERCENTEQUALS  */
-#line 3093 "cmDependsJavaParser.y"
+#line 3092 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6219,11 +6218,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6223 "cmDependsJavaParser.cxx"
+#line 6222 "cmDependsJavaParser.cxx"
     break;
 
   case 340: /* AssignmentOperator: jp_PLUSEQUALS  */
-#line 3102 "cmDependsJavaParser.y"
+#line 3101 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6231,11 +6230,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6235 "cmDependsJavaParser.cxx"
+#line 6234 "cmDependsJavaParser.cxx"
     break;
 
   case 341: /* AssignmentOperator: jp_MINUSEQUALS  */
-#line 3111 "cmDependsJavaParser.y"
+#line 3110 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6243,11 +6242,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6247 "cmDependsJavaParser.cxx"
+#line 6246 "cmDependsJavaParser.cxx"
     break;
 
   case 342: /* AssignmentOperator: jp_LESLESEQUALS  */
-#line 3120 "cmDependsJavaParser.y"
+#line 3119 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6255,11 +6254,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6259 "cmDependsJavaParser.cxx"
+#line 6258 "cmDependsJavaParser.cxx"
     break;
 
   case 343: /* AssignmentOperator: jp_GTGTEQUALS  */
-#line 3129 "cmDependsJavaParser.y"
+#line 3128 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6267,11 +6266,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6271 "cmDependsJavaParser.cxx"
+#line 6270 "cmDependsJavaParser.cxx"
     break;
 
   case 344: /* AssignmentOperator: jp_GTGTGTEQUALS  */
-#line 3138 "cmDependsJavaParser.y"
+#line 3137 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6279,11 +6278,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6283 "cmDependsJavaParser.cxx"
+#line 6282 "cmDependsJavaParser.cxx"
     break;
 
   case 345: /* AssignmentOperator: jp_ANDEQUALS  */
-#line 3147 "cmDependsJavaParser.y"
+#line 3146 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6291,11 +6290,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6295 "cmDependsJavaParser.cxx"
+#line 6294 "cmDependsJavaParser.cxx"
     break;
 
   case 346: /* AssignmentOperator: jp_CARROTEQUALS  */
-#line 3156 "cmDependsJavaParser.y"
+#line 3155 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6303,11 +6302,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6307 "cmDependsJavaParser.cxx"
+#line 6306 "cmDependsJavaParser.cxx"
     break;
 
   case 347: /* AssignmentOperator: jp_PIPEEQUALS  */
-#line 3165 "cmDependsJavaParser.y"
+#line 3164 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6315,11 +6314,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6319 "cmDependsJavaParser.cxx"
+#line 6318 "cmDependsJavaParser.cxx"
     break;
 
   case 348: /* Expression: AssignmentExpression  */
-#line 3175 "cmDependsJavaParser.y"
+#line 3174 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6327,11 +6326,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6331 "cmDependsJavaParser.cxx"
+#line 6330 "cmDependsJavaParser.cxx"
     break;
 
   case 349: /* ConstantExpression: Expression  */
-#line 3185 "cmDependsJavaParser.y"
+#line 3184 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6339,11 +6338,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6343 "cmDependsJavaParser.cxx"
+#line 6342 "cmDependsJavaParser.cxx"
     break;
 
   case 350: /* New: jp_NEW  */
-#line 3195 "cmDependsJavaParser.y"
+#line 3194 "cmDependsJavaParser.y"
 {
   jpElementStart(1);
   jpCheckEmpty(1);
@@ -6351,11 +6350,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6355 "cmDependsJavaParser.cxx"
+#line 6354 "cmDependsJavaParser.cxx"
     break;
 
   case 351: /* New: Name jp_DOT jp_NEW  */
-#line 3204 "cmDependsJavaParser.y"
+#line 3203 "cmDependsJavaParser.y"
 {
   jpElementStart(3);
   jpStoreClass((yyvsp[-2].str));
@@ -6364,11 +6363,11 @@
   yyGetParser->SetCurrentCombine("");
 
 }
-#line 6368 "cmDependsJavaParser.cxx"
+#line 6367 "cmDependsJavaParser.cxx"
     break;
 
 
-#line 6372 "cmDependsJavaParser.cxx"
+#line 6371 "cmDependsJavaParser.cxx"
 
       default: break;
     }
@@ -6592,7 +6591,7 @@
   return yyresult;
 }
 
-#line 3213 "cmDependsJavaParser.y"
+#line 3212 "cmDependsJavaParser.y"
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmDependsJavaParser.y b/Source/LexerParser/cmDependsJavaParser.y
index ff37af2..d64043a 100644
--- a/Source/LexerParser/cmDependsJavaParser.y
+++ b/Source/LexerParser/cmDependsJavaParser.y
@@ -13,7 +13,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string>
diff --git a/Source/LexerParser/cmExprParser.cxx b/Source/LexerParser/cmExprParser.cxx
index cb5e498..80c0abd 100644
--- a/Source/LexerParser/cmExprParser.cxx
+++ b/Source/LexerParser/cmExprParser.cxx
@@ -88,7 +88,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdexcept>
@@ -127,7 +126,7 @@
 #  pragma diag_suppress 550 /* variable set but never used */
 #endif
 
-#line 131 "cmExprParser.cxx"
+#line 130 "cmExprParser.cxx"
 
 # ifndef YY_CAST
 #  ifdef __cplusplus
@@ -569,9 +568,9 @@
 /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint8 yyrline[] =
 {
-       0,    88,    88,    93,    96,   101,   104,   109,   112,   117,
-     120,   123,   128,   131,   134,   139,   142,   145,   151,   156,
-     159,   162,   165,   170,   173
+       0,    87,    87,    92,    95,   100,   103,   108,   111,   116,
+     119,   122,   127,   130,   133,   138,   141,   144,   150,   155,
+     158,   161,   164,   169,   172
 };
 #endif
 
@@ -1432,194 +1431,194 @@
   switch (yyn)
     {
   case 2: /* start: exp  */
-#line 88 "cmExprParser.y"
+#line 87 "cmExprParser.y"
       {
     cmExpr_yyget_extra(yyscanner)->SetResult((yyvsp[0].Number));
   }
-#line 1440 "cmExprParser.cxx"
+#line 1439 "cmExprParser.cxx"
     break;
 
   case 3: /* exp: bitwiseor  */
-#line 93 "cmExprParser.y"
+#line 92 "cmExprParser.y"
             {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1448 "cmExprParser.cxx"
+#line 1447 "cmExprParser.cxx"
     break;
 
   case 4: /* exp: exp exp_OR bitwiseor  */
-#line 96 "cmExprParser.y"
+#line 95 "cmExprParser.y"
                        {
     (yyval.Number) = (yyvsp[-2].Number) | (yyvsp[0].Number);
   }
-#line 1456 "cmExprParser.cxx"
+#line 1455 "cmExprParser.cxx"
     break;
 
   case 5: /* bitwiseor: bitwisexor  */
-#line 101 "cmExprParser.y"
+#line 100 "cmExprParser.y"
              {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1464 "cmExprParser.cxx"
+#line 1463 "cmExprParser.cxx"
     break;
 
   case 6: /* bitwiseor: bitwiseor exp_XOR bitwisexor  */
-#line 104 "cmExprParser.y"
+#line 103 "cmExprParser.y"
                                {
     (yyval.Number) = (yyvsp[-2].Number) ^ (yyvsp[0].Number);
   }
-#line 1472 "cmExprParser.cxx"
+#line 1471 "cmExprParser.cxx"
     break;
 
   case 7: /* bitwisexor: bitwiseand  */
-#line 109 "cmExprParser.y"
+#line 108 "cmExprParser.y"
              {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1480 "cmExprParser.cxx"
+#line 1479 "cmExprParser.cxx"
     break;
 
   case 8: /* bitwisexor: bitwisexor exp_AND bitwiseand  */
-#line 112 "cmExprParser.y"
+#line 111 "cmExprParser.y"
                                 {
     (yyval.Number) = (yyvsp[-2].Number) & (yyvsp[0].Number);
   }
-#line 1488 "cmExprParser.cxx"
+#line 1487 "cmExprParser.cxx"
     break;
 
   case 9: /* bitwiseand: shift  */
-#line 117 "cmExprParser.y"
+#line 116 "cmExprParser.y"
         {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1496 "cmExprParser.cxx"
+#line 1495 "cmExprParser.cxx"
     break;
 
   case 10: /* bitwiseand: bitwiseand exp_SHIFTLEFT shift  */
-#line 120 "cmExprParser.y"
+#line 119 "cmExprParser.y"
                                  {
     (yyval.Number) = (yyvsp[-2].Number) << (yyvsp[0].Number);
   }
-#line 1504 "cmExprParser.cxx"
+#line 1503 "cmExprParser.cxx"
     break;
 
   case 11: /* bitwiseand: bitwiseand exp_SHIFTRIGHT shift  */
-#line 123 "cmExprParser.y"
+#line 122 "cmExprParser.y"
                                   {
     (yyval.Number) = (yyvsp[-2].Number) >> (yyvsp[0].Number);
   }
-#line 1512 "cmExprParser.cxx"
+#line 1511 "cmExprParser.cxx"
     break;
 
   case 12: /* shift: term  */
-#line 128 "cmExprParser.y"
+#line 127 "cmExprParser.y"
        {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1520 "cmExprParser.cxx"
+#line 1519 "cmExprParser.cxx"
     break;
 
   case 13: /* shift: shift exp_PLUS term  */
-#line 131 "cmExprParser.y"
+#line 130 "cmExprParser.y"
                       {
     (yyval.Number) = (yyvsp[-2].Number) + (yyvsp[0].Number);
   }
-#line 1528 "cmExprParser.cxx"
+#line 1527 "cmExprParser.cxx"
     break;
 
   case 14: /* shift: shift exp_MINUS term  */
-#line 134 "cmExprParser.y"
+#line 133 "cmExprParser.y"
                        {
     (yyval.Number) = (yyvsp[-2].Number) - (yyvsp[0].Number);
   }
-#line 1536 "cmExprParser.cxx"
+#line 1535 "cmExprParser.cxx"
     break;
 
   case 15: /* term: unary  */
-#line 139 "cmExprParser.y"
+#line 138 "cmExprParser.y"
         {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1544 "cmExprParser.cxx"
+#line 1543 "cmExprParser.cxx"
     break;
 
   case 16: /* term: term exp_TIMES unary  */
-#line 142 "cmExprParser.y"
+#line 141 "cmExprParser.y"
                        {
     (yyval.Number) = (yyvsp[-2].Number) * (yyvsp[0].Number);
   }
-#line 1552 "cmExprParser.cxx"
+#line 1551 "cmExprParser.cxx"
     break;
 
   case 17: /* term: term exp_DIVIDE unary  */
-#line 145 "cmExprParser.y"
+#line 144 "cmExprParser.y"
                         {
     if (yyvsp[0].Number == 0) {
       throw std::overflow_error("divide by zero");
     }
     (yyval.Number) = (yyvsp[-2].Number) / (yyvsp[0].Number);
   }
-#line 1563 "cmExprParser.cxx"
+#line 1562 "cmExprParser.cxx"
     break;
 
   case 18: /* term: term exp_MOD unary  */
-#line 151 "cmExprParser.y"
+#line 150 "cmExprParser.y"
                      {
     (yyval.Number) = (yyvsp[-2].Number) % (yyvsp[0].Number);
   }
-#line 1571 "cmExprParser.cxx"
+#line 1570 "cmExprParser.cxx"
     break;
 
   case 19: /* unary: factor  */
-#line 156 "cmExprParser.y"
+#line 155 "cmExprParser.y"
          {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1579 "cmExprParser.cxx"
+#line 1578 "cmExprParser.cxx"
     break;
 
   case 20: /* unary: exp_PLUS unary  */
-#line 159 "cmExprParser.y"
+#line 158 "cmExprParser.y"
                  {
     (yyval.Number) = + (yyvsp[0].Number);
   }
-#line 1587 "cmExprParser.cxx"
+#line 1586 "cmExprParser.cxx"
     break;
 
   case 21: /* unary: exp_MINUS unary  */
-#line 162 "cmExprParser.y"
+#line 161 "cmExprParser.y"
                   {
     (yyval.Number) = - (yyvsp[0].Number);
   }
-#line 1595 "cmExprParser.cxx"
+#line 1594 "cmExprParser.cxx"
     break;
 
   case 22: /* unary: exp_NOT unary  */
-#line 165 "cmExprParser.y"
+#line 164 "cmExprParser.y"
                 {
     (yyval.Number) = ~ (yyvsp[0].Number);
   }
-#line 1603 "cmExprParser.cxx"
+#line 1602 "cmExprParser.cxx"
     break;
 
   case 23: /* factor: exp_NUMBER  */
-#line 170 "cmExprParser.y"
+#line 169 "cmExprParser.y"
              {
     (yyval.Number) = (yyvsp[0].Number);
   }
-#line 1611 "cmExprParser.cxx"
+#line 1610 "cmExprParser.cxx"
     break;
 
   case 24: /* factor: exp_OPENPARENT exp exp_CLOSEPARENT  */
-#line 173 "cmExprParser.y"
+#line 172 "cmExprParser.y"
                                      {
     (yyval.Number) = (yyvsp[-1].Number);
   }
-#line 1619 "cmExprParser.cxx"
+#line 1618 "cmExprParser.cxx"
     break;
 
 
-#line 1623 "cmExprParser.cxx"
+#line 1622 "cmExprParser.cxx"
 
       default: break;
     }
@@ -1843,7 +1842,7 @@
   return yyresult;
 }
 
-#line 178 "cmExprParser.y"
+#line 177 "cmExprParser.y"
 
 /* End of grammar */
 
diff --git a/Source/LexerParser/cmExprParser.y b/Source/LexerParser/cmExprParser.y
index 1c959f6..51df56e 100644
--- a/Source/LexerParser/cmExprParser.y
+++ b/Source/LexerParser/cmExprParser.y
@@ -13,7 +13,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdexcept>
diff --git a/Source/LexerParser/cmGccDepfileLexer.cxx b/Source/LexerParser/cmGccDepfileLexer.cxx
index e588853..ca5577e 100644
--- a/Source/LexerParser/cmGccDepfileLexer.cxx
+++ b/Source/LexerParser/cmGccDepfileLexer.cxx
@@ -256,7 +256,6 @@
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
-typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -421,7 +420,7 @@
 	/* Number of characters read into yy_ch_buf, not including EOB
 	 * characters.
 	 */
-	yy_size_t yy_n_chars;
+	int yy_n_chars;
 
 	/* Whether we "own" the buffer - i.e., we know we created it,
 	 * and can realloc() it to grow it, and should free() it to
@@ -498,7 +497,7 @@
 
 YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
 YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
-YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, yy_size_t len , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
 
 void *yyalloc ( yy_size_t , yyscan_t yyscanner );
 void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
@@ -545,7 +544,7 @@
  */
 #define YY_DO_BEFORE_ACTION \
 	yyg->yytext_ptr = yy_bp; \
-	yyleng = (yy_size_t) (yy_cp - yy_bp); \
+	yyleng = (int) (yy_cp - yy_bp); \
 	yyg->yy_hold_char = *yy_cp; \
 	*yy_cp = '\0'; \
 	yyg->yy_c_buf_p = yy_cp;
@@ -674,8 +673,8 @@
     size_t yy_buffer_stack_max; /**< capacity of stack. */
     YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
     char yy_hold_char;
-    yy_size_t yy_n_chars;
-    yy_size_t yyleng_r;
+    int yy_n_chars;
+    int yyleng_r;
     char *yy_c_buf_p;
     int yy_init;
     int yy_start;
@@ -722,7 +721,7 @@
 
 void yyset_out  ( FILE * _out_str , yyscan_t yyscanner );
 
-			yy_size_t yyget_leng ( yyscan_t yyscanner );
+			int yyget_leng ( yyscan_t yyscanner );
 
 char *yyget_text ( yyscan_t yyscanner );
 
@@ -795,7 +794,7 @@
 	if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
 		{ \
 		int c = '*'; \
-		yy_size_t n; \
+		int n; \
 		for ( n = 0; n < max_size && \
 			     (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
 			buf[n] = (char) c; \
@@ -1241,7 +1240,7 @@
 
 	else
 		{
-			yy_size_t num_to_read =
+			int num_to_read =
 			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
 
 		while ( num_to_read <= 0 )
@@ -1255,7 +1254,7 @@
 
 			if ( b->yy_is_our_buffer )
 				{
-				yy_size_t new_size = b->yy_buf_size * 2;
+				int new_size = b->yy_buf_size * 2;
 
 				if ( new_size <= 0 )
 					b->yy_buf_size += b->yy_buf_size / 8;
@@ -1313,7 +1312,7 @@
 
 	if ((yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
 		/* Extend the array by 50%, plus the number we really need. */
-		yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+		int new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
 		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
 			(void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size , yyscanner );
 		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
@@ -1406,7 +1405,7 @@
 	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
 		{ /* need to shift things up to make room */
 		/* +2 for EOB chars. */
-		yy_size_t number_to_move = yyg->yy_n_chars + 2;
+		int number_to_move = yyg->yy_n_chars + 2;
 		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
 					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
 		char *source =
@@ -1458,7 +1457,7 @@
 
 		else
 			{ /* need more input */
-			yy_size_t offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+			int offset = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr);
 			++yyg->yy_c_buf_p;
 
 			switch ( yy_get_next_buffer( yyscanner ) )
@@ -1836,12 +1835,12 @@
  * @param yyscanner The scanner object.
  * @return the newly allocated buffer state object.
  */
-YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, yy_size_t  _yybytes_len , yyscan_t yyscanner)
+YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len , yyscan_t yyscanner)
 {
 	YY_BUFFER_STATE b;
 	char *buf;
 	yy_size_t n;
-	yy_size_t i;
+	int i;
 
 	/* Get memory for full buffer, including space for trailing EOB's. */
 	n = (yy_size_t) (_yybytes_len + 2);
@@ -1885,7 +1884,7 @@
 	do \
 		{ \
 		/* Undo effects of setting up yytext. */ \
-        yy_size_t yyless_macro_arg = (n); \
+        int yyless_macro_arg = (n); \
         YY_LESS_LINENO(yyless_macro_arg);\
 		yytext[yyleng] = yyg->yy_hold_char; \
 		yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
@@ -1953,7 +1952,7 @@
 /** Get the length of the current token.
  * @param yyscanner The scanner object.
  */
-yy_size_t yyget_leng  (yyscan_t yyscanner)
+int yyget_leng  (yyscan_t yyscanner)
 {
     struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
     return yyleng;
diff --git a/Source/LexerParser/cmGccDepfileLexer.h b/Source/LexerParser/cmGccDepfileLexer.h
index ab73ebc..7d34060 100644
--- a/Source/LexerParser/cmGccDepfileLexer.h
+++ b/Source/LexerParser/cmGccDepfileLexer.h
@@ -258,7 +258,6 @@
 typedef uint16_t flex_uint16_t;
 typedef int32_t flex_int32_t;
 typedef uint32_t flex_uint32_t;
-typedef uint64_t flex_uint64_t;
 #else
 typedef signed char flex_int8_t;
 typedef short int flex_int16_t;
@@ -372,7 +371,7 @@
 	/* Number of characters read into yy_ch_buf, not including EOB
 	 * characters.
 	 */
-	yy_size_t yy_n_chars;
+	int yy_n_chars;
 
 	/* Whether we "own" the buffer - i.e., we know we created it,
 	 * and can realloc() it to grow it, and should free() it to
@@ -416,7 +415,7 @@
 
 YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size , yyscan_t yyscanner );
 YY_BUFFER_STATE yy_scan_string ( const char *yy_str , yyscan_t yyscanner );
-YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, yy_size_t len , yyscan_t yyscanner );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len , yyscan_t yyscanner );
 
 void *yyalloc ( yy_size_t , yyscan_t yyscanner );
 void *yyrealloc ( void *, yy_size_t , yyscan_t yyscanner );
@@ -463,7 +462,7 @@
 
 void yyset_out  ( FILE * _out_str , yyscan_t yyscanner );
 
-			yy_size_t yyget_leng ( yyscan_t yyscanner );
+			int yyget_leng ( yyscan_t yyscanner );
 
 char *yyget_text ( yyscan_t yyscanner );
 
diff --git a/Source/cmAddCustomCommandCommand.cxx b/Source/cmAddCustomCommandCommand.cxx
index b1398db..b1589ff 100644
--- a/Source/cmAddCustomCommandCommand.cxx
+++ b/Source/cmAddCustomCommandCommand.cxx
@@ -19,6 +19,7 @@
 #include "cmPolicies.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmValue.h"
 
 bool cmAddCustomCommandCommand(std::vector<std::string> const& args,
                                cmExecutionStatus& status)
@@ -39,6 +40,7 @@
   std::string working;
   std::string depfile;
   std::string job_pool;
+  std::string job_server_aware;
   std::string comment_buffer;
   const char* comment = nullptr;
   std::vector<std::string> depends;
@@ -78,6 +80,7 @@
     doing_working_directory,
     doing_depfile,
     doing_job_pool,
+    doing_job_server_aware,
     doing_nothing
   };
 
@@ -95,6 +98,7 @@
   MAKE_STATIC_KEYWORD(DEPFILE);
   MAKE_STATIC_KEYWORD(IMPLICIT_DEPENDS);
   MAKE_STATIC_KEYWORD(JOB_POOL);
+  MAKE_STATIC_KEYWORD(JOB_SERVER_AWARE);
   MAKE_STATIC_KEYWORD(MAIN_DEPENDENCY);
   MAKE_STATIC_KEYWORD(OUTPUT);
   MAKE_STATIC_KEYWORD(OUTPUTS);
@@ -126,6 +130,7 @@
     keyPRE_BUILD,
     keyPRE_LINK,
     keySOURCE,
+    keyJOB_SERVER_AWARE,
     keyTARGET,
     keyUSES_TERMINAL,
     keyVERBATIM,
@@ -190,6 +195,8 @@
         }
       } else if (copy == keyJOB_POOL) {
         doing = doing_job_pool;
+      } else if (copy == keyJOB_SERVER_AWARE) {
+        doing = doing_job_server_aware;
       }
     } else {
       std::string filename;
@@ -226,6 +233,9 @@
         case doing_job_pool:
           job_pool = copy;
           break;
+        case doing_job_server_aware:
+          job_server_aware = copy;
+          break;
         case doing_working_directory:
           working = copy;
           break;
@@ -334,6 +344,7 @@
   cc->SetUsesTerminal(uses_terminal);
   cc->SetDepfile(depfile);
   cc->SetJobPool(job_pool);
+  cc->SetJobserverAware(cmIsOn(job_server_aware));
   cc->SetCommandExpandLists(command_expand_lists);
   cc->SetDependsExplicitOnly(depends_explicit_only);
   if (source.empty() && output.empty()) {
diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx
index a246d06..711eaa5 100644
--- a/Source/cmAddCustomTargetCommand.cxx
+++ b/Source/cmAddCustomTargetCommand.cxx
@@ -17,6 +17,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
+#include "cmValue.h"
 
 bool cmAddCustomTargetCommand(std::vector<std::string> const& args,
                               cmExecutionStatus& status)
@@ -54,6 +55,7 @@
   const char* comment = nullptr;
   std::vector<std::string> sources;
   std::string job_pool;
+  std::string job_server_aware;
 
   // Keep track of parser state.
   enum tdoing
@@ -65,6 +67,7 @@
     doing_comment,
     doing_source,
     doing_job_pool,
+    doing_job_server_aware,
     doing_nothing
   };
   tdoing doing = doing_command;
@@ -102,6 +105,8 @@
       doing = doing_comment;
     } else if (copy == "JOB_POOL") {
       doing = doing_job_pool;
+    } else if (copy == "JOB_SERVER_AWARE") {
+      doing = doing_job_server_aware;
     } else if (copy == "COMMAND") {
       doing = doing_command;
 
@@ -148,6 +153,9 @@
         case doing_job_pool:
           job_pool = copy;
           break;
+        case doing_job_server_aware:
+          job_server_aware = copy;
+          break;
         default:
           status.SetError("Wrong syntax. Unknown type of argument.");
           return false;
@@ -223,6 +231,7 @@
   cc->SetUsesTerminal(uses_terminal);
   cc->SetCommandExpandLists(command_expand_lists);
   cc->SetJobPool(job_pool);
+  cc->SetJobserverAware(cmIsOn(job_server_aware));
   cmTarget* target =
     mf.AddUtilityCommand(targetName, excludeFromAll, std::move(cc));
 
diff --git a/Source/cmArgumentParser.h b/Source/cmArgumentParser.h
index fdf54fb..b35d7f3 100644
--- a/Source/cmArgumentParser.h
+++ b/Source/cmArgumentParser.h
@@ -7,6 +7,7 @@
 #include <cassert>
 #include <cstddef>
 #include <functional>
+#include <iterator>
 #include <map>
 #include <string>
 #include <utility>
@@ -176,6 +177,17 @@
   void Bind(MaybeEmpty<std::vector<std::string>>& val);
   void Bind(NonEmpty<std::vector<std::string>>& val);
   void Bind(std::vector<std::vector<std::string>>& val);
+  template <typename U>
+  void Bind(NonEmpty<std::vector<std::pair<std::string, U>>>& val,
+            U const& context)
+  {
+    this->Bind(
+      [&val, &context](cm::string_view arg) -> Continue {
+        val.emplace_back(std::string(arg), context);
+        return Continue::Yes;
+      },
+      ExpectAtLeast{ 1 });
+  }
 
   // cm::optional<> records the presence the keyword to which it binds.
   template <typename T>
@@ -187,6 +199,15 @@
     this->Bind(*optVal);
   }
 
+  template <typename T, typename U>
+  void Bind(cm::optional<T>& optVal, U const& context)
+  {
+    if (!optVal) {
+      optVal.emplace();
+    }
+    this->Bind(*optVal, context);
+  }
+
   template <typename Range>
   void Parse(Range const& args, std::size_t pos = 0)
   {
@@ -232,6 +253,17 @@
     return *this;
   }
 
+  template <typename T, typename U>
+  cmArgumentParser& BindWithContext(cm::static_string_view name,
+                                    T Result::*member, U Result::*context)
+  {
+    this->Base::Bind(name, [member, context](Instance& instance) {
+      auto* result = static_cast<Result*>(instance.Result);
+      instance.Bind(result->*member, result->*context);
+    });
+    return *this;
+  }
+
   cmArgumentParser& Bind(cm::static_string_view name,
                          Continue (Result::*member)(cm::string_view),
                          ExpectAtLeast expect = { 1 })
diff --git a/Source/cmAuxSourceDirectoryCommand.cxx b/Source/cmAuxSourceDirectoryCommand.cxx
index 35f4c88..b2e3cad 100644
--- a/Source/cmAuxSourceDirectoryCommand.cxx
+++ b/Source/cmAuxSourceDirectoryCommand.cxx
@@ -11,6 +11,7 @@
 #include "cmsys/Directory.hxx"
 
 #include "cmExecutionStatus.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmSourceFile.h"
 #include "cmStringAlgorithms.h"
@@ -67,7 +68,7 @@
   if (!sourceListValue.empty()) {
     sourceListValue += ";";
   }
-  sourceListValue += cmJoin(files, ";");
+  sourceListValue += cmList::to_string(files);
   mf.AddDefinition(args[1], sourceListValue);
   return true;
 }
diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx
index 5972202..e2a8f92 100644
--- a/Source/cmBinUtilsLinuxELFLinker.cxx
+++ b/Source/cmBinUtilsLinuxELFLinker.cxx
@@ -73,6 +73,9 @@
   if (ldConfigTool == "ldconfig") {
     this->LDConfigTool =
       cm::make_unique<cmLDConfigLDConfigTool>(this->Archive);
+    if (!this->LDConfigTool->GetLDConfigPaths(this->LDConfigPaths)) {
+      return false;
+    }
   } else {
     std::ostringstream e;
     e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool;
@@ -132,12 +135,8 @@
                        parentRpaths.end());
   }
 
-  std::vector<std::string> ldConfigPaths;
-  if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) {
-    return false;
-  }
-  searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(),
-                     ldConfigPaths.end());
+  searchPaths.insert(searchPaths.end(), this->LDConfigPaths.begin(),
+                     this->LDConfigPaths.end());
 
   for (auto const& dep : needed) {
     if (!this->Archive->IsPreExcluded(dep)) {
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
index 566e4a4..8043f82 100644
--- a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
@@ -10,6 +10,7 @@
 #include "cmRuntimeDependencyArchive.h"
 #include "cmSystemTools.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::
   cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
@@ -35,7 +36,7 @@
   builder.AddCommand(command);
 
   auto process = builder.Start();
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     std::ostringstream e;
     e << "Failed to start objdump process for:\n  " << file;
     this->SetError(e.str());
@@ -46,7 +47,8 @@
   static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$");
   static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$");
   static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (neededRegex.find(line.c_str(), match)) {
       needed.push_back(match.match(1));
@@ -73,8 +75,7 @@
     this->SetError(e.str());
     return false;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
+  if (process.GetStatus(0).ExitStatus != 0) {
     std::ostringstream e;
     e << "Failed to run objdump on:\n  " << file;
     this->SetError(e.str());
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx
index c064377..90e0891 100644
--- a/Source/cmBinUtilsMacOSMachOLinker.cxx
+++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -5,7 +5,6 @@
 
 #include <sstream>
 #include <string>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
index 6d97720..4c35841 100644
--- a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
@@ -9,6 +9,7 @@
 
 #include "cmRuntimeDependencyArchive.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::
   cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
@@ -34,7 +35,7 @@
     .AddCommand(command);
 
   auto process = builder.Start();
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     std::ostringstream e;
     e << "Failed to start otool process for:\n  " << file;
     this->SetError(e.str());
@@ -49,11 +50,12 @@
     "^ *path (.*) \\(offset [0-9]+\\)$");
   static const cmsys::RegularExpression nameRegex(
     "^ *name (.*) \\(offset [0-9]+\\)$");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch cmdMatch;
     if (rpathRegex.find(line.c_str(), cmdMatch)) {
-      if (!std::getline(*process.OutputStream(), line) ||
-          !std::getline(*process.OutputStream(), line)) {
+      // NOLINTNEXTLINE(misc-redundant-expression)
+      if (!std::getline(output, line) || !std::getline(output, line)) {
         this->SetError("Invalid output from otool");
         return false;
       }
@@ -66,8 +68,8 @@
         return false;
       }
     } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) {
-      if (!std::getline(*process.OutputStream(), line) ||
-          !std::getline(*process.OutputStream(), line)) {
+      // NOLINTNEXTLINE(misc-redundant-expression)
+      if (!std::getline(output, line) || !std::getline(output, line)) {
         this->SetError("Invalid output from otool");
         return false;
       }
@@ -88,8 +90,7 @@
     this->SetError(e.str());
     return false;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
+  if (process.GetStatus(0).ExitStatus != 0) {
     std::ostringstream e;
     e << "Failed to run otool on:\n  " << file;
     this->SetError(e.str());
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
index f342884..cd21140 100644
--- a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
@@ -9,6 +9,7 @@
 
 #include "cmRuntimeDependencyArchive.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::
   cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
@@ -33,7 +34,7 @@
   builder.AddCommand(command);
 
   auto process = builder.Start();
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     std::ostringstream e;
     e << "Failed to start dumpbin process for:\n  " << file;
     this->SetError(e.str());
@@ -43,7 +44,8 @@
   std::string line;
   static const cmsys::RegularExpression regex(
     "^    ([^\n]*\\.[Dd][Ll][Ll])\r$");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (regex.find(line.c_str(), match)) {
       needed.push_back(match.match(1));
@@ -56,8 +58,7 @@
     this->SetError(e.str());
     return false;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
+  if (process.GetStatus(0).ExitStatus != 0) {
     std::ostringstream e;
     e << "Failed to run dumpbin on:\n  " << file;
     this->SetError(e.str());
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
index f14de55..d95da95 100644
--- a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
@@ -10,6 +10,7 @@
 #include "cmRuntimeDependencyArchive.h"
 #include "cmSystemTools.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::
   cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
@@ -34,7 +35,7 @@
   builder.AddCommand(command);
 
   auto process = builder.Start();
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     std::ostringstream e;
     e << "Failed to start objdump process for:\n  " << file;
     this->SetError(e.str());
@@ -44,7 +45,8 @@
   std::string line;
   static const cmsys::RegularExpression regex(
     "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])$");
-  while (cmSystemTools::GetLineFromStream(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (cmSystemTools::GetLineFromStream(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (regex.find(line.c_str(), match)) {
       needed.push_back(match.match(1));
@@ -57,8 +59,7 @@
     this->SetError(e.str());
     return false;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
+  if (process.GetStatus(0).ExitStatus != 0) {
     std::ostringstream e;
     e << "Failed to run objdump on:\n  " << file;
     this->SetError(e.str());
diff --git a/Source/cmBlockCommand.cxx b/Source/cmBlockCommand.cxx
index ec79149..42f1ad3 100644
--- a/Source/cmBlockCommand.cxx
+++ b/Source/cmBlockCommand.cxx
@@ -3,7 +3,8 @@
 
 #include "cmBlockCommand.h"
 
-#include <cstdint> // IWYU pragma: keep
+#include <cstdint>
+#include <initializer_list>
 #include <utility>
 
 #include <cm/memory>
diff --git a/Source/cmCMakeHostSystemInformationCommand.cxx b/Source/cmCMakeHostSystemInformationCommand.cxx
index 1c00f15..0efb9a4 100644
--- a/Source/cmCMakeHostSystemInformationCommand.cxx
+++ b/Source/cmCMakeHostSystemInformationCommand.cxx
@@ -5,6 +5,7 @@
 #include <algorithm>
 #include <cassert>
 #include <cctype>
+#include <cstddef>
 #include <initializer_list>
 #include <map>
 #include <string>
@@ -34,7 +35,6 @@
 #  include "cmGlobalVisualStudio10Generator.h"
 #  include "cmGlobalVisualStudioVersionedGenerator.h"
 #  include "cmVSSetupHelper.h"
-#  define HAVE_VS_SETUP_HELPER
 #endif
 
 namespace {
@@ -377,9 +377,9 @@
   return data;
 }
 
-cm::optional<std::string> GetValue(cmExecutionStatus& status,
-                                   std::string const& key,
-                                   std::string const& variable)
+cm::optional<std::string> GetDistribValue(cmExecutionStatus& status,
+                                          std::string const& key,
+                                          std::string const& variable)
 {
   const auto prefix = "DISTRIB_"_s;
   if (!cmHasPrefix(key, prefix)) {
@@ -413,9 +413,89 @@
   return std::string{};
 }
 
-#ifdef HAVE_VS_SETUP_HELPER
-cm::optional<std::string> GetValue(cmExecutionStatus& status,
-                                   std::string const& key)
+#ifdef _WIN32
+std::string FindMSYSTEM_PREFIX(std::vector<std::string> prefixes)
+{
+  for (std::string const& prefix : prefixes) {
+    std::string out;
+    std::string err;
+    int ret;
+    // In a modern MSYSTEM environment we expect cygpath to be in PATH.
+    std::vector<std::string> cygpath_cmd{ "cygpath", "-w", prefix };
+    if (cmSystemTools::RunSingleCommand(cygpath_cmd, &out, &err, &ret, nullptr,
+                                        cmSystemTools::OUTPUT_NONE)) {
+      if (ret == 0) {
+        out = cmTrimWhitespace(out);
+        cmSystemTools::ConvertToUnixSlashes(out);
+        if (cmSystemTools::FileIsDirectory(out)) {
+          return out;
+        }
+      }
+    } else {
+      // In a legacy MSYSTEM environment (MinGW/MSYS 1.0) there is no
+      // cygpath but we expect 'sh' to be in PATH.
+      std::vector<std::string> sh_cmd{
+        "sh", "-c", cmStrCat("cd \"", prefix, "\" && cmd //c cd")
+      };
+      if (cmSystemTools::RunSingleCommand(sh_cmd, &out, &err, &ret, nullptr,
+                                          cmSystemTools::OUTPUT_NONE)) {
+        if (ret == 0) {
+          out = cmTrimWhitespace(out);
+          cmSystemTools::ConvertToUnixSlashes(out);
+          if (cmSystemTools::FileIsDirectory(out)) {
+            return out;
+          }
+        }
+      }
+    }
+  }
+  return {};
+}
+
+std::string FallbackMSYSTEM_PREFIX(cm::string_view msystem)
+{
+  // These layouts are used by distributions such as
+  // * MSYS2: https://www.msys2.org/docs/environments/
+  // * MinGW/MSYS 1.0: http://mingw.osdn.io/
+  if (msystem == "MSYS"_s) {
+    static std::string const msystem_msys = FindMSYSTEM_PREFIX({ "/usr" });
+    return msystem_msys;
+  }
+  if (msystem == "MINGW32"_s) {
+    static std::string const msystem_mingw32 =
+      FindMSYSTEM_PREFIX({ "/mingw32", "/mingw" });
+    return msystem_mingw32;
+  }
+  if (msystem == "MINGW64"_s) {
+    static std::string const msystem_mingw64 =
+      FindMSYSTEM_PREFIX({ "/mingw64" });
+    return msystem_mingw64;
+  }
+  if (msystem == "UCRT64"_s) {
+    static std::string const msystem_ucrt64 =
+      FindMSYSTEM_PREFIX({ "/ucrt64" });
+    return msystem_ucrt64;
+  }
+  if (msystem == "CLANG32"_s) {
+    static std::string const msystem_clang32 =
+      FindMSYSTEM_PREFIX({ "/clang32" });
+    return msystem_clang32;
+  }
+  if (msystem == "CLANG64"_s) {
+    static std::string const msystem_clang64 =
+      FindMSYSTEM_PREFIX({ "/clang64" });
+    return msystem_clang64;
+  }
+  if (msystem == "CLANGARM64"_s) {
+    static std::string const msystem_clangarm64 =
+      FindMSYSTEM_PREFIX({ "/clangarm64" });
+    return msystem_clangarm64;
+  }
+  return {};
+}
+
+cm::optional<std::string> GetWindowsValue(cmExecutionStatus& status,
+                                          std::string const& key)
 {
   auto* const gg = status.GetMakefile().GetGlobalGenerator();
   for (auto vs : { 15, 16, 17 }) {
@@ -446,6 +526,23 @@
     return vs10gen->FindMSBuildCommandEarly(&status.GetMakefile());
   }
 
+  if (key == "MSYSTEM_PREFIX") {
+    // MSYSTEM_PREFIX is meaningful only under a MSYSTEM environment.
+    cm::optional<std::string> ms = cmSystemTools::GetEnvVar("MSYSTEM");
+    if (!ms || ms->empty()) {
+      return std::string();
+    }
+    // Prefer the MSYSTEM_PREFIX environment variable.
+    if (cm::optional<std::string> msp =
+          cmSystemTools::GetEnvVar("MSYSTEM_PREFIX")) {
+      cmSystemTools::ConvertToUnixSlashes(*msp);
+      if (cmSystemTools::FileIsDirectory(*msp)) {
+        return msp;
+      }
+    }
+    // Fall back to known distribution layouts.
+    return FallbackMSYSTEM_PREFIX(*ms);
+  }
   return {};
 }
 #endif
@@ -529,12 +626,12 @@
   if (arguments.ValueNames) {
     auto result = registry.GetValueNames(key, view);
     if (result) {
-      makefile.AddDefinition(variable, cmJoin(*result, ";"_s));
+      makefile.AddDefinition(variable, cmList::to_string(*result));
     }
   } else if (arguments.SubKeys) {
     auto result = registry.GetSubKeys(key, view);
     if (result) {
-      makefile.AddDefinition(variable, cmJoin(*result, ";"_s));
+      makefile.AddDefinition(variable, cmList::to_string(*result));
     }
   } else {
     auto result =
@@ -597,9 +694,9 @@
     auto value =
       GetValueChained(
           [&]() { return GetValue(info, key); }
-        , [&]() { return GetValue(status, key, variable); }
-#ifdef HAVE_VS_SETUP_HELPER
-        , [&]() { return GetValue(status, key); }
+        , [&]() { return GetDistribValue(status, key, variable); }
+#ifdef _WIN32
+        , [&]() { return GetWindowsValue(status, key); }
 #endif
         );
     // clang-format on
diff --git a/Source/cmCMakeLanguageCommand.cxx b/Source/cmCMakeLanguageCommand.cxx
index c7e9209..329427c 100644
--- a/Source/cmCMakeLanguageCommand.cxx
+++ b/Source/cmCMakeLanguageCommand.cxx
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <array>
 #include <cstddef>
-#include <memory>
 #include <string>
 #include <utility>
 
@@ -17,10 +16,11 @@
 #include "cmArgumentParserTypes.h"
 #include "cmDependencyProvider.h"
 #include "cmExecutionStatus.h"
+#include "cmExperimental.h"
 #include "cmGlobalGenerator.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 #include "cmRange.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
@@ -328,6 +328,46 @@
   makefile.AddDefinition(outputVariable, outputValue);
   return true;
 }
+
+bool cmCMakeLanguageCommandGET_EXPERIMENTAL_FEATURE_ENABLED(
+  std::vector<cmListFileArgument> const& args, cmExecutionStatus& status)
+{
+  cmMakefile& makefile = status.GetMakefile();
+  std::vector<std::string> expandedArgs;
+  makefile.ExpandArguments(args, expandedArgs);
+
+  if (expandedArgs.size() != 3) {
+    return FatalError(status,
+                      "sub-command GET_EXPERIMENTAL_FEATURE_ENABLED expects "
+                      "exactly two arguments");
+  }
+
+  auto const& featureName = expandedArgs[1];
+  auto const& variableName = expandedArgs[2];
+
+  auto feature = cmExperimental::Feature::Sentinel;
+  for (std::size_t i = 0;
+       i < static_cast<std::size_t>(cmExperimental::Feature::Sentinel); i++) {
+    if (cmExperimental::DataForFeature(static_cast<cmExperimental::Feature>(i))
+          .Name == featureName) {
+      feature = static_cast<cmExperimental::Feature>(i);
+      break;
+    }
+  }
+  if (feature == cmExperimental::Feature::Sentinel) {
+    return FatalError(status,
+                      cmStrCat("Experimental feature name \"", featureName,
+                               "\" does not exist."));
+  }
+
+  if (cmExperimental::HasSupportEnabled(makefile, feature)) {
+    makefile.AddDefinition(variableName, "TRUE");
+  } else {
+    makefile.AddDefinition(variableName, "FALSE");
+  }
+
+  return true;
+}
 }
 
 bool cmCMakeLanguageCommand(std::vector<cmListFileArgument> const& args,
@@ -480,5 +520,10 @@
     return cmCMakeLanguageCommandGET_MESSAGE_LOG_LEVEL(args, status);
   }
 
+  if (expArgs[expArg] == "GET_EXPERIMENTAL_FEATURE_ENABLED") {
+    return cmCMakeLanguageCommandGET_EXPERIMENTAL_FEATURE_ENABLED(args,
+                                                                  status);
+  }
+
   return FatalError(status, "called with unknown meta-operation");
 }
diff --git a/Source/cmCMakePath.cxx b/Source/cmCMakePath.cxx
index 73321c6..5080f58 100644
--- a/Source/cmCMakePath.cxx
+++ b/Source/cmCMakePath.cxx
@@ -15,6 +15,8 @@
 #include <cm/string_view>
 
 #if defined(_WIN32)
+#  include <cmext/string_view>
+
 #  include "cmStringAlgorithms.h"
 #endif
 
diff --git a/Source/cmCMakePath.h b/Source/cmCMakePath.h
index 15aa30c..a42ac98 100644
--- a/Source/cmCMakePath.h
+++ b/Source/cmCMakePath.h
@@ -12,7 +12,10 @@
 #include <cm/filesystem>
 #include <cm/string_view>
 #include <cm/type_traits>
-#include <cmext/string_view>
+
+namespace cm {
+class static_string_view;
+}
 
 namespace detail {
 #if defined(__SUNPRO_CC) && defined(__sparc)
@@ -123,11 +126,29 @@
     : Path(FormatPath(source, fmt))
   {
   }
+  cmCMakePath(const char* source, format fmt = generic_format) noexcept
+    : Path(FormatPath(cm::string_view{ source }, fmt))
+  {
+  }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath(const std::string& source, format fmt = generic_format)
+    : Path(FormatPath(source, fmt))
+  {
+  }
+  cmCMakePath(std::string&& source, format fmt = generic_format)
+    : Path(FormatPath(std::move(source), fmt))
+  {
+  }
+#else
   template <typename Source, typename = enable_if_move_pathable<Source>>
   cmCMakePath(Source source, format fmt = generic_format)
     : Path(FormatPath(std::move(source), fmt))
   {
   }
+#endif
 
   template <typename Source, typename = enable_if_move_pathable<Source>>
   cmCMakePath& Assign(Source&& source)
@@ -156,6 +177,41 @@
     }
     return *this;
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath& operator=(cm::filesystem::path&& source)
+  {
+    this->Assign(std::forward<cm::filesystem::path>(source));
+    return *this;
+  }
+  cmCMakePath& operator=(std::string&& source)
+  {
+    this->Assign(std::forward<std::string>(source));
+    return *this;
+  }
+  cmCMakePath& operator=(const cm::filesystem::path& source)
+  {
+    this->Assign(source);
+    return *this;
+  }
+  cmCMakePath& operator=(const std::string& source)
+  {
+    this->Assign(source);
+    return *this;
+  }
+  cmCMakePath& operator=(const cm::string_view source)
+  {
+    this->Assign(source);
+    return *this;
+  }
+  cmCMakePath& operator=(const char* source)
+  {
+    this->Assign(cm::string_view{ source });
+    return *this;
+  }
+#else
   template <typename Source, typename = enable_if_move_pathable<Source>>
   cmCMakePath& operator=(Source&& source)
   {
@@ -168,6 +224,7 @@
     this->Assign(source);
     return *this;
   }
+#endif
 
   // Concatenation
   cmCMakePath& Append(const cmCMakePath& path)
@@ -182,12 +239,29 @@
     this->Path = this->Path.generic_string();
     return *this;
   }
-
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath& Append(const std::string& source)
+  {
+    return this->Append(cm::filesystem::path(source));
+  }
+  cmCMakePath& Append(cm::string_view source)
+  {
+    return this->Append(cm::filesystem::path(source));
+  }
+  cmCMakePath& Append(const char* source)
+  {
+    return this->Append(cm::filesystem::path(cm::string_view{ source }));
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath& Append(const Source& source)
   {
     return this->Append(cm::filesystem::path(source));
   }
+#endif
 
   cmCMakePath& operator/=(const cmCMakePath& path)
   {
@@ -204,17 +278,38 @@
     this->Path += path.Path;
     return *this;
   }
-  cmCMakePath& Concat(cm::static_string_view source)
+  cmCMakePath& Concat(cm::string_view source)
   {
-    this->Path.concat(std::string(source));
+    this->Path.operator+=(std::string(source));
     return *this;
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath& Concat(const cm::filesystem::path& source)
+  {
+    this->Path.operator+=(source);
+    return *this;
+  }
+  cmCMakePath& Concat(const std::string& source)
+  {
+    this->Path.operator+=(source);
+    return *this;
+  }
+  cmCMakePath& Concat(const char* source)
+  {
+    this->Path.operator+=(source);
+    return *this;
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath& Concat(const Source& source)
   {
-    this->Path.concat(source);
+    this->Path.operator+=(source);
     return *this;
   }
+#endif
 
   cmCMakePath& operator+=(const cmCMakePath& path)
   {
@@ -242,6 +337,32 @@
     }
     return *this;
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath& ReplaceFileName(const cm::filesystem::path& filename)
+  {
+    if (this->Path.has_filename()) {
+      this->Path.replace_filename(filename);
+    }
+    return *this;
+  }
+  cmCMakePath& ReplaceFileName(const std::string& filename)
+  {
+    if (this->Path.has_filename()) {
+      this->Path.replace_filename(filename);
+    }
+    return *this;
+  }
+  cmCMakePath& ReplaceFileName(cm::string_view filename)
+  {
+    if (this->Path.has_filename()) {
+      this->Path.replace_filename(filename);
+    }
+    return *this;
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath& ReplaceFileName(const Source& filename)
   {
@@ -250,18 +371,40 @@
     }
     return *this;
   }
+#endif
 
   cmCMakePath& ReplaceExtension(const cmCMakePath& extension = cmCMakePath())
   {
     this->Path.replace_extension(extension.Path);
     return *this;
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath& ReplaceExtension(const cm::filesystem::path& extension)
+  {
+    this->Path.replace_extension(extension);
+    return *this;
+  }
+  cmCMakePath& ReplaceExtension(const std::string& extension)
+  {
+    this->Path.replace_extension(extension);
+    return *this;
+  }
+  cmCMakePath& ReplaceExtension(const cm::string_view extension)
+  {
+    this->Path.replace_extension(extension);
+    return *this;
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath& ReplaceExtension(const Source& extension)
   {
     this->Path.replace_extension(extension);
     return *this;
   }
+#endif
 
   cmCMakePath& ReplaceWideExtension(
     const cmCMakePath& extension = cmCMakePath())
@@ -269,11 +412,26 @@
     return this->ReplaceWideExtension(
       static_cast<cm::string_view>(extension.Path.string()));
   }
+  cmCMakePath& ReplaceWideExtension(const cm::filesystem::path& extension)
+  {
+    return this->ReplaceWideExtension(
+      static_cast<cm::string_view>(extension.string()));
+  }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath& ReplaceWideExtension(const std::string& extension)
+  {
+    return this->ReplaceWideExtension(cm::string_view{ extension });
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath& ReplaceWideExtension(const Source& extension)
   {
-    return this->ReplaceWideExtension(cm::string_view(extension));
+    return this->ReplaceWideExtension(extension);
   }
+#endif
   cmCMakePath& ReplaceWideExtension(cm::string_view extension);
 
   cmCMakePath& RemoveExtension()
@@ -355,12 +513,25 @@
     // Windows) so convert back to '/'
     return path.generic_string();
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath Relative(const std::string& base) const
+  {
+    return this->Relative(cm::filesystem::path(base));
+  }
+  cmCMakePath Relative(cm::string_view base) const
+  {
+    return this->Relative(cm::filesystem::path(base));
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath Relative(const Source& base) const
   {
     return this->Relative(cm::filesystem::path(base));
   }
-
+#endif
   cmCMakePath Proximate(const cmCMakePath& base) const
   {
     return this->Proximate(base.Path);
@@ -372,21 +543,49 @@
     // Windows) so convert back to '/'
     return path.generic_string();
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath Proximate(const std::string& base) const
+  {
+    return this->Proximate(cm::filesystem::path(base));
+  }
+  cmCMakePath Proximate(cm::string_view base) const
+  {
+    return this->Proximate(cm::filesystem::path(base));
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath Proximate(const Source& base) const
   {
     return this->Proximate(cm::filesystem::path(base));
   }
+#endif
 
   cmCMakePath Absolute(const cmCMakePath& base) const
   {
     return this->Absolute(base.Path);
   }
+#if defined(__SUNPRO_CC) && defined(__sparc)
+  // Oracle DeveloperStudio C++ compiler on Solaris/Sparc is confused when
+  // standard methods and templates use the same name. The template is selected
+  // rather than the standard one regardless the arguments of the method.
+  cmCMakePath Absolute(const std::string& base) const
+  {
+    return this->Absolute(cm::filesystem::path(base));
+  }
+  cmCMakePath Absolute(cm::string_view base) const
+  {
+    return this->Absolute(cm::filesystem::path(base));
+  }
+#else
   template <typename Source, typename = enable_if_pathable<Source>>
   cmCMakePath Absolute(const Source& base) const
   {
     return this->Absolute(cm::filesystem::path(base));
   }
+#endif
   cmCMakePath Absolute(const cm::filesystem::path& base) const;
 
   // Comparison
diff --git a/Source/cmCMakePresetErrors.h b/Source/cmCMakePresetErrors.h
deleted file mode 100644
index 0db391e..0000000
--- a/Source/cmCMakePresetErrors.h
+++ /dev/null
@@ -1,245 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#pragma once
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <cm3p/json/value.h>
-
-#include "cmJSONHelpers.h"
-#include "cmJSONState.h"
-#include "cmStringAlgorithms.h"
-
-namespace cmCMakePresetErrors {
-const auto getPreset = [](cmJSONState* state) -> const Json::Value* {
-  if (state->parseStack.size() < 2) {
-    return nullptr;
-  }
-  std::string firstKey = state->parseStack[0].first;
-  if (firstKey == "configurePresets" || firstKey == "packagePresets" ||
-      firstKey == "buildPresets" || firstKey == "testPresets") {
-    return state->parseStack[1].second;
-  }
-  return nullptr;
-};
-const auto getPresetName = [](cmJSONState* state) -> std::string {
-#if !defined(CMAKE_BOOTSTRAP)
-  const Json::Value* preset = getPreset(state);
-  if (preset != nullptr && preset->isMember("name")) {
-    return preset->operator[]("name").asString();
-  }
-#endif
-  return "";
-};
-const auto getVariableName = [](cmJSONState* state) -> std::string {
-  std::string var = state->key_after("cacheVariables");
-  std::string errMsg = cmStrCat("variable \"", var, "\"");
-  errMsg = cmStrCat(errMsg, " for preset \"", getPresetName(state), "\"");
-  return errMsg;
-};
-const auto FILE_NOT_FOUND = [](const std::string& filename,
-                               cmJSONState* state) -> void {
-  state->AddError(cmStrCat("File not found: ", filename));
-};
-const auto INVALID_ROOT = [](const Json::Value* value,
-                             cmJSONState* state) -> void {
-  state->AddErrorAtValue("Invalid root object", value);
-};
-const auto NO_VERSION = [](const Json::Value* value,
-                           cmJSONState* state) -> void {
-  state->AddErrorAtValue("No \"version\" field", value);
-};
-const auto INVALID_VERSION = [](const Json::Value* value,
-                                cmJSONState* state) -> void {
-  state->AddErrorAtValue("Invalid \"version\" field", value);
-};
-const auto UNRECOGNIZED_VERSION = [](const Json::Value* value,
-                                     cmJSONState* state) -> void {
-  state->AddErrorAtValue("Unrecognized \"version\" field", value);
-};
-const auto INVALID_PRESETS = [](const Json::Value* value,
-                                cmJSONState* state) -> void {
-  state->AddErrorAtValue("Invalid \"configurePresets\" field", value);
-};
-const auto INVALID_PRESET = [](const Json::Value* value,
-                               cmJSONState* state) -> void {
-  state->AddErrorAtValue("Invalid preset", value);
-};
-const auto INVALID_PRESET_NAMED = [](const std::string& presetName,
-                                     cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Invalid preset: \"", presetName, "\""));
-};
-const auto INVALID_VARIABLE = [](const Json::Value* value,
-                                 cmJSONState* state) -> void {
-  std::string var = cmCMakePresetErrors::getVariableName(state);
-  state->AddErrorAtValue(cmStrCat("Invalid CMake ", var), value);
-};
-const auto DUPLICATE_PRESETS = [](const std::string& presetName,
-                                  cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Duplicate preset: \"", presetName, "\""));
-};
-const auto CYCLIC_PRESET_INHERITANCE = [](const std::string& presetName,
-                                          cmJSONState* state) -> void {
-  state->AddError(
-    cmStrCat("Cyclic preset inheritance for preset \"", presetName, "\""));
-};
-const auto INHERITED_PRESET_UNREACHABLE_FROM_FILE =
-  [](const std::string& presetName, cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Inherited preset \"", presetName,
-                           "\" is unreachable from preset's file"));
-};
-const auto CONFIGURE_PRESET_UNREACHABLE_FROM_FILE =
-  [](const std::string& presetName, cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Configure preset \"", presetName,
-                           "\" is unreachable from preset's file"));
-};
-const auto INVALID_MACRO_EXPANSION = [](const std::string& presetName,
-                                        cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Invalid macro expansion in \"", presetName, "\""));
-};
-const auto BUILD_TEST_PRESETS_UNSUPPORTED = [](const Json::Value*,
-                                               cmJSONState* state) -> void {
-  state->AddError("File version must be 2 or higher for build and test preset "
-                  "support");
-};
-const auto PACKAGE_PRESETS_UNSUPPORTED = [](const Json::Value*,
-                                            cmJSONState* state) -> void {
-  state->AddError(
-    "File version must be 6 or higher for package preset support");
-};
-const auto WORKFLOW_PRESETS_UNSUPPORTED = [](const Json::Value*,
-                                             cmJSONState* state) -> void {
-  state->AddError(
-    "File version must be 6 or higher for workflow preset support");
-};
-const auto INCLUDE_UNSUPPORTED = [](const Json::Value*,
-                                    cmJSONState* state) -> void {
-  state->AddError("File version must be 4 or higher for include support");
-};
-const auto INVALID_INCLUDE = [](const Json::Value* value,
-                                cmJSONState* state) -> void {
-  state->AddErrorAtValue("Invalid \"include\" field", value);
-};
-const auto INVALID_CONFIGURE_PRESET = [](const std::string& presetName,
-                                         cmJSONState* state) -> void {
-  state->AddError(
-    cmStrCat(R"(Invalid "configurePreset": ")", presetName, "\""));
-};
-const auto INSTALL_PREFIX_UNSUPPORTED = [](const Json::Value* value,
-                                           cmJSONState* state) -> void {
-  state->AddErrorAtValue(
-    "File version must be 3 or higher for installDir preset "
-    "support",
-    value);
-};
-const auto CONDITION_UNSUPPORTED = [](cmJSONState* state) -> void {
-  state->AddError("File version must be 3 or higher for condition support");
-};
-const auto TOOLCHAIN_FILE_UNSUPPORTED = [](cmJSONState* state) -> void {
-  state->AddError("File version must be 3 or higher for toolchainFile preset "
-                  "support");
-};
-const auto CYCLIC_INCLUDE = [](const std::string& file,
-                               cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Cyclic include among preset files: ", file));
-};
-const auto TEST_OUTPUT_TRUNCATION_UNSUPPORTED =
-  [](cmJSONState* state) -> void {
-  state->AddError("File version must be 5 or higher for testOutputTruncation "
-                  "preset support");
-};
-const auto INVALID_WORKFLOW_STEPS = [](const std::string& workflowStep,
-                                       cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Invalid workflow step \"", workflowStep, "\""));
-};
-const auto NO_WORKFLOW_STEPS = [](const std::string& presetName,
-                                  cmJSONState* state) -> void {
-  state->AddError(
-    cmStrCat("No workflow steps specified for \"", presetName, "\""));
-};
-const auto FIRST_WORKFLOW_STEP_NOT_CONFIGURE = [](const std::string& stepName,
-                                                  cmJSONState* state) -> void {
-  state->AddError(cmStrCat("First workflow step \"", stepName,
-                           "\" must be a configure step"));
-};
-const auto CONFIGURE_WORKFLOW_STEP_NOT_FIRST = [](const std::string& stepName,
-                                                  cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Configure workflow step \"", stepName,
-                           "\" must be the first step"));
-};
-const auto WORKFLOW_STEP_UNREACHABLE_FROM_FILE =
-  [](const std::string& workflowStep, cmJSONState* state) -> void {
-  state->AddError(cmStrCat("Workflow step \"", workflowStep,
-                           "\" is unreachable from preset's file"));
-};
-const auto CTEST_JUNIT_UNSUPPORTED = [](cmJSONState* state) -> void {
-  state->AddError(
-    "File version must be 6 or higher for CTest JUnit output support");
-};
-const auto TRACE_UNSUPPORTED = [](cmJSONState* state) -> void {
-  state->AddError("File version must be 7 or higher for trace preset support");
-};
-const auto UNRECOGNIZED_CMAKE_VERSION = [](const std::string& version,
-                                           int current, int required) {
-  return [version, current, required](const Json::Value* value,
-                                      cmJSONState* state) -> void {
-    state->AddErrorAtValue(cmStrCat("\"cmakeMinimumRequired\" ", version,
-                                    " version ", required,
-                                    " must be less than ", current),
-                           value);
-  };
-};
-const auto INVALID_PRESET_NAME = [](const Json::Value* value,
-                                    cmJSONState* state) -> void {
-  std::string errMsg = "Invalid Preset Name";
-  if (value && value->isConvertibleTo(Json::ValueType::stringValue) &&
-      !value->asString().empty()) {
-    errMsg = cmStrCat(errMsg, ": ", value->asString());
-  }
-  state->AddErrorAtValue(errMsg, value);
-};
-const auto INVALID_CONDITION = [](const Json::Value* value,
-                                  cmJSONState* state) -> void {
-  state->AddErrorAtValue(
-    cmStrCat("Invalid condition for preset \"", getPresetName(state), "\""),
-    value);
-};
-const auto INVALID_CONDITION_OBJECT =
-  [](JsonErrors::ObjectError errorType,
-     const Json::Value::Members& extraFields) {
-    return JsonErrors::INVALID_NAMED_OBJECT(
-      [](const Json::Value*, cmJSONState* state) -> std::string {
-        return cmStrCat(" condition for preset \"", getPresetName(state),
-                        "\"");
-      })(errorType, extraFields);
-  };
-const auto INVALID_VARIABLE_OBJECT =
-  [](JsonErrors::ObjectError errorType,
-     const Json::Value::Members& extraFields) {
-    return JsonErrors::INVALID_NAMED_OBJECT(
-      [](const Json::Value*, cmJSONState* state) -> std::string {
-        return getVariableName(state);
-      })(errorType, extraFields);
-  };
-const auto INVALID_PRESET_OBJECT =
-  [](JsonErrors::ObjectError errorType,
-     const Json::Value::Members& extraFields) {
-    return JsonErrors::INVALID_NAMED_OBJECT(
-      [](const Json::Value*, cmJSONState*) -> std::string {
-        return "Preset";
-      })(errorType, extraFields);
-  };
-const auto INVALID_ROOT_OBJECT = [](JsonErrors::ObjectError errorType,
-                                    const Json::Value::Members& extraFields) {
-  return JsonErrors::INVALID_NAMED_OBJECT(
-    [](const Json::Value*, cmJSONState*) -> std::string {
-      return "root object";
-    })(errorType, extraFields);
-};
-const auto PRESET_MISSING_FIELD = [](const std::string& presetName,
-                                     const std::string& missingField,
-                                     cmJSONState* state) {
-  state->AddError(cmStrCat("Preset \"", presetName, "\" missing field \"",
-                           missingField, "\""));
-};
-}
diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx
new file mode 100644
index 0000000..1b88a0c
--- /dev/null
+++ b/Source/cmCMakePresetsErrors.cxx
@@ -0,0 +1,310 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmCMakePresetsErrors.h"
+
+#include <functional>
+#include <utility>
+#include <vector>
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
+#include "cmStringAlgorithms.h"
+
+namespace cmCMakePresetsErrors {
+const Json::Value* getPreset(cmJSONState* state)
+{
+  if (state->parseStack.size() < 2) {
+    return nullptr;
+  }
+  std::string firstKey = state->parseStack[0].first;
+  if (firstKey == "configurePresets" || firstKey == "packagePresets" ||
+      firstKey == "buildPresets" || firstKey == "testPresets") {
+    return state->parseStack[1].second;
+  }
+  return nullptr;
+}
+
+std::string getPresetName(cmJSONState* state)
+{
+  const Json::Value* preset = getPreset(state);
+  if (preset != nullptr && preset->isMember("name")) {
+    return preset->operator[]("name").asString();
+  }
+  return "";
+}
+
+std::string getVariableName(cmJSONState* state)
+{
+  std::string var = state->key_after("cacheVariables");
+  std::string errMsg = cmStrCat("variable \"", var, "\"");
+  errMsg = cmStrCat(errMsg, " for preset \"", getPresetName(state), "\"");
+  return errMsg;
+}
+
+void FILE_NOT_FOUND(const std::string& filename, cmJSONState* state)
+{
+  state->AddError(cmStrCat("File not found: ", filename));
+}
+
+void INVALID_ROOT(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("Invalid root object", value);
+}
+
+void NO_VERSION(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("No \"version\" field", value);
+}
+
+void INVALID_VERSION(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("Invalid \"version\" field", value);
+}
+
+void UNRECOGNIZED_VERSION(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("Unrecognized \"version\" field", value);
+}
+
+void INVALID_PRESETS(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("Invalid \"configurePresets\" field", value);
+}
+
+void INVALID_PRESET(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("Invalid preset", value);
+}
+
+void INVALID_PRESET_NAMED(const std::string& presetName, cmJSONState* state)
+{
+  state->AddError(cmStrCat("Invalid preset: \"", presetName, "\""));
+}
+
+void INVALID_VARIABLE(const Json::Value* value, cmJSONState* state)
+{
+  std::string var = cmCMakePresetsErrors::getVariableName(state);
+  state->AddErrorAtValue(cmStrCat("Invalid CMake ", var), value);
+}
+
+void DUPLICATE_PRESETS(const std::string& presetName, cmJSONState* state)
+{
+  state->AddError(cmStrCat("Duplicate preset: \"", presetName, "\""));
+}
+
+void CYCLIC_PRESET_INHERITANCE(const std::string& presetName,
+                               cmJSONState* state)
+
+{
+  state->AddError(
+    cmStrCat("Cyclic preset inheritance for preset \"", presetName, "\""));
+}
+
+void INHERITED_PRESET_UNREACHABLE_FROM_FILE(const std::string& presetName,
+                                            cmJSONState* state)
+{
+  state->AddError(cmStrCat("Inherited preset \"", presetName,
+                           "\" is unreachable from preset's file"));
+}
+
+void CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(const std::string& presetName,
+                                            cmJSONState* state)
+{
+  state->AddError(cmStrCat("Configure preset \"", presetName,
+                           "\" is unreachable from preset's file"));
+}
+
+void INVALID_MACRO_EXPANSION(const std::string& presetName, cmJSONState* state)
+{
+  state->AddError(cmStrCat("Invalid macro expansion in \"", presetName, "\""));
+}
+
+void BUILD_TEST_PRESETS_UNSUPPORTED(const Json::Value*, cmJSONState* state)
+{
+  state->AddError("File version must be 2 or higher for build and test preset "
+                  "support");
+}
+
+void PACKAGE_PRESETS_UNSUPPORTED(const Json::Value*, cmJSONState* state)
+{
+  state->AddError(
+    "File version must be 6 or higher for package preset support");
+}
+
+void WORKFLOW_PRESETS_UNSUPPORTED(const Json::Value*, cmJSONState* state)
+{
+  state->AddError(
+    "File version must be 6 or higher for workflow preset support");
+}
+
+void INCLUDE_UNSUPPORTED(const Json::Value*, cmJSONState* state)
+{
+  state->AddError("File version must be 4 or higher for include support");
+}
+
+void INVALID_INCLUDE(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue("Invalid \"include\" field", value);
+}
+
+void INVALID_CONFIGURE_PRESET(const std::string& presetName,
+                              cmJSONState* state)
+{
+  state->AddError(
+    cmStrCat(R"(Invalid "configurePreset": ")", presetName, "\""));
+}
+
+void INSTALL_PREFIX_UNSUPPORTED(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue(
+    "File version must be 3 or higher for installDir preset "
+    "support",
+    value);
+}
+
+void CONDITION_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError("File version must be 3 or higher for condition support");
+}
+
+void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError("File version must be 3 or higher for toolchainFile preset "
+                  "support");
+}
+
+void CYCLIC_INCLUDE(const std::string& file, cmJSONState* state)
+{
+  state->AddError(cmStrCat("Cyclic include among preset files: ", file));
+}
+
+void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError("File version must be 5 or higher for testOutputTruncation "
+                  "preset support");
+}
+
+void INVALID_WORKFLOW_STEPS(const std::string& workflowStep,
+                            cmJSONState* state)
+{
+  state->AddError(cmStrCat("Invalid workflow step \"", workflowStep, "\""));
+}
+
+void NO_WORKFLOW_STEPS(const std::string& presetName, cmJSONState* state)
+{
+  state->AddError(
+    cmStrCat("No workflow steps specified for \"", presetName, "\""));
+}
+
+void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(const std::string& stepName,
+                                       cmJSONState* state)
+{
+  state->AddError(cmStrCat("First workflow step \"", stepName,
+                           "\" must be a configure step"));
+}
+
+void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(const std::string& stepName,
+                                       cmJSONState* state)
+{
+  state->AddError(cmStrCat("Configure workflow step \"", stepName,
+                           "\" must be the first step"));
+}
+
+void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(const std::string& workflowStep,
+                                         cmJSONState* state)
+{
+  state->AddError(cmStrCat("Workflow step \"", workflowStep,
+                           "\" is unreachable from preset's file"));
+}
+
+void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError(
+    "File version must be 6 or higher for CTest JUnit output support");
+}
+
+void TRACE_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError("File version must be 7 or higher for trace preset support");
+}
+
+JsonErrors::ErrorGenerator UNRECOGNIZED_CMAKE_VERSION(
+  const std::string& version, int current, int required)
+{
+  return [version, current, required](const Json::Value* value,
+                                      cmJSONState* state) -> void {
+    state->AddErrorAtValue(cmStrCat("\"cmakeMinimumRequired\" ", version,
+                                    " version ", required,
+                                    " must be less than ", current),
+                           value);
+  };
+}
+
+void INVALID_PRESET_NAME(const Json::Value* value, cmJSONState* state)
+{
+  std::string errMsg = "Invalid Preset Name";
+  if (value && value->isConvertibleTo(Json::ValueType::stringValue) &&
+      !value->asString().empty()) {
+    errMsg = cmStrCat(errMsg, ": ", value->asString());
+  }
+  state->AddErrorAtValue(errMsg, value);
+}
+
+void INVALID_CONDITION(const Json::Value* value, cmJSONState* state)
+{
+  state->AddErrorAtValue(
+    cmStrCat("Invalid condition for preset \"", getPresetName(state), "\""),
+    value);
+}
+
+JsonErrors::ErrorGenerator INVALID_CONDITION_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields)
+{
+  return JsonErrors::INVALID_NAMED_OBJECT(
+    [](const Json::Value*, cmJSONState* state) -> std::string {
+      return cmStrCat(" condition for preset \"", getPresetName(state), "\"");
+    })(errorType, extraFields);
+}
+
+JsonErrors::ErrorGenerator INVALID_VARIABLE_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields)
+{
+  return JsonErrors::INVALID_NAMED_OBJECT(
+    [](const Json::Value*, cmJSONState* state) -> std::string {
+      return getVariableName(state);
+    })(errorType, extraFields);
+}
+
+JsonErrors::ErrorGenerator INVALID_PRESET_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields)
+{
+  return JsonErrors::INVALID_NAMED_OBJECT(
+    [](const Json::Value*, cmJSONState*) -> std::string { return "Preset"; })(
+    errorType, extraFields);
+}
+
+JsonErrors::ErrorGenerator INVALID_ROOT_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields)
+{
+  return JsonErrors::INVALID_NAMED_OBJECT(
+    [](const Json::Value*, cmJSONState*) -> std::string {
+      return "root object";
+    })(errorType, extraFields);
+}
+
+void PRESET_MISSING_FIELD(const std::string& presetName,
+                          const std::string& missingField, cmJSONState* state)
+{
+  state->AddError(cmStrCat("Preset \"", presetName, "\" missing field \"",
+                           missingField, "\""));
+}
+
+void SCHEMA_UNSUPPORTED(cmJSONState* state)
+{
+  state->AddError("File version must be 8 or higher for $schema support");
+}
+}
diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h
new file mode 100644
index 0000000..b755c25
--- /dev/null
+++ b/Source/cmCMakePresetsErrors.h
@@ -0,0 +1,118 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONHelpers.h"
+
+class cmJSONState;
+
+namespace cmCMakePresetsErrors {
+const Json::Value* getPreset(cmJSONState* state);
+
+std::string getPresetName(cmJSONState* state);
+
+std::string getVariableName(cmJSONState* state);
+
+void FILE_NOT_FOUND(const std::string& filename, cmJSONState* state);
+
+void INVALID_ROOT(const Json::Value* value, cmJSONState* state);
+
+void NO_VERSION(const Json::Value* value, cmJSONState* state);
+
+void INVALID_VERSION(const Json::Value* value, cmJSONState* state);
+
+void UNRECOGNIZED_VERSION(const Json::Value* value, cmJSONState* state);
+
+void INVALID_PRESETS(const Json::Value* value, cmJSONState* state);
+
+void INVALID_PRESET(const Json::Value* value, cmJSONState* state);
+
+void INVALID_PRESET_NAMED(const std::string& presetName, cmJSONState* state);
+
+void INVALID_VARIABLE(const Json::Value* value, cmJSONState* state);
+
+void DUPLICATE_PRESETS(const std::string& presetName, cmJSONState* state);
+
+void CYCLIC_PRESET_INHERITANCE(const std::string& presetName,
+                               cmJSONState* state);
+
+void INHERITED_PRESET_UNREACHABLE_FROM_FILE(const std::string& presetName,
+                                            cmJSONState* state);
+
+void CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(const std::string& presetName,
+                                            cmJSONState* state);
+
+void INVALID_MACRO_EXPANSION(const std::string& presetName,
+                             cmJSONState* state);
+
+void BUILD_TEST_PRESETS_UNSUPPORTED(const Json::Value*, cmJSONState* state);
+
+void PACKAGE_PRESETS_UNSUPPORTED(const Json::Value*, cmJSONState* state);
+
+void WORKFLOW_PRESETS_UNSUPPORTED(const Json::Value*, cmJSONState* state);
+
+void INCLUDE_UNSUPPORTED(const Json::Value*, cmJSONState* state);
+
+void INVALID_INCLUDE(const Json::Value* value, cmJSONState* state);
+
+void INVALID_CONFIGURE_PRESET(const std::string& presetName,
+                              cmJSONState* state);
+
+void INSTALL_PREFIX_UNSUPPORTED(const Json::Value* value, cmJSONState* state);
+
+void CONDITION_UNSUPPORTED(cmJSONState* state);
+
+void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state);
+
+void CYCLIC_INCLUDE(const std::string& file, cmJSONState* state);
+
+void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state);
+
+void INVALID_WORKFLOW_STEPS(const std::string& workflowStep,
+                            cmJSONState* state);
+
+void NO_WORKFLOW_STEPS(const std::string& presetName, cmJSONState* state);
+
+void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(const std::string& stepName,
+                                       cmJSONState* state);
+
+void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(const std::string& stepName,
+                                       cmJSONState* state);
+
+void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(const std::string& workflowStep,
+                                         cmJSONState* state);
+
+void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state);
+
+void TRACE_UNSUPPORTED(cmJSONState* state);
+
+JsonErrors::ErrorGenerator UNRECOGNIZED_CMAKE_VERSION(
+  const std::string& version, int current, int required);
+
+void INVALID_PRESET_NAME(const Json::Value* value, cmJSONState* state);
+
+void INVALID_CONDITION(const Json::Value* value, cmJSONState* state);
+
+JsonErrors::ErrorGenerator INVALID_CONDITION_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields);
+
+JsonErrors::ErrorGenerator INVALID_VARIABLE_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields);
+
+JsonErrors::ErrorGenerator INVALID_PRESET_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields);
+
+JsonErrors::ErrorGenerator INVALID_ROOT_OBJECT(
+  JsonErrors::ObjectError errorType, const Json::Value::Members& extraFields);
+
+void PRESET_MISSING_FIELD(const std::string& presetName,
+                          const std::string& missingField, cmJSONState* state);
+
+void SCHEMA_UNSUPPORTED(cmJSONState* state);
+}
diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx
index 13eddbe..d37c8a4 100644
--- a/Source/cmCMakePresetsGraph.cxx
+++ b/Source/cmCMakePresetsGraph.cxx
@@ -4,6 +4,7 @@
 
 #include <algorithm>
 #include <cassert>
+#include <cstddef>
 #include <functional>
 #include <iostream>
 #include <iterator>
@@ -13,7 +14,7 @@
 
 #include "cmsys/RegularExpression.hxx"
 
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -88,8 +89,8 @@
 {
   switch (cycleStatus[preset.Name]) {
     case CycleStatus::InProgress:
-      cmCMakePresetErrors::CYCLIC_PRESET_INHERITANCE(preset.Name,
-                                                     &graph.parseState);
+      cmCMakePresetsErrors::CYCLIC_PRESET_INHERITANCE(preset.Name,
+                                                      &graph.parseState);
       return false;
     case CycleStatus::Verified:
       return true;
@@ -100,27 +101,27 @@
   cycleStatus[preset.Name] = CycleStatus::InProgress;
 
   if (preset.Environment.count("") != 0) {
-    cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+    cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
     return false;
   }
 
   bool result = preset.VisitPresetBeforeInherit();
   if (!result) {
-    cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+    cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
     return false;
   }
 
   for (auto const& i : preset.Inherits) {
     auto parent = presets.find(i);
     if (parent == presets.end()) {
-      cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                &graph.parseState);
+      cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
+                                                 &graph.parseState);
       return false;
     }
 
     auto& parentPreset = parent->second.Unexpanded;
     if (!preset.OriginFile->ReachableFiles.count(parentPreset.OriginFile)) {
-      cmCMakePresetErrors::INHERITED_PRESET_UNREACHABLE_FROM_FILE(
+      cmCMakePresetsErrors::INHERITED_PRESET_UNREACHABLE_FROM_FILE(
         preset.Name, &graph.parseState);
       return false;
     }
@@ -131,8 +132,8 @@
 
     result = preset.VisitPresetInherit(parentPreset);
     if (!result) {
-      cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                &graph.parseState);
+      cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
+                                                 &graph.parseState);
       return false;
     }
 
@@ -152,7 +153,7 @@
   result = preset.VisitPresetAfterInherit(graph.GetVersion(preset),
                                           &graph.parseState);
   if (!result) {
-    cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
+    cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name, &graph.parseState);
     return false;
   }
 
@@ -458,8 +459,8 @@
       switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders,
                        graph.GetVersion(preset))) {
         case ExpandMacroResult::Error:
-          cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                    &graph.parseState);
+          cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
+                                                     &graph.parseState);
           return false;
         case ExpandMacroResult::Ignore:
           out.reset();
@@ -474,8 +475,8 @@
     cm::optional<bool> result;
     if (!preset.ConditionEvaluator->Evaluate(
           macroExpanders, graph.GetVersion(preset), result)) {
-      cmCMakePresetErrors::INVALID_PRESET_NAMED(preset.Name,
-                                                &graph.parseState);
+      cmCMakePresetsErrors::INVALID_PRESET_NAMED(preset.Name,
+                                                 &graph.parseState);
       return false;
     }
     if (!result) {
@@ -614,7 +615,7 @@
                                   cmJSONState* state)
 {
   if (preset.ConfigurePreset != configurePreset->Name) {
-    cmCMakePresetErrors::INVALID_WORKFLOW_STEPS(configurePreset->Name, state);
+    cmCMakePresetsErrors::INVALID_WORKFLOW_STEPS(configurePreset->Name, state);
     return false;
   }
   return true;
@@ -637,12 +638,12 @@
 {
   auto it = presets.find(name);
   if (it == presets.end()) {
-    cmCMakePresetErrors::INVALID_WORKFLOW_STEPS(name, state);
+    cmCMakePresetsErrors::INVALID_WORKFLOW_STEPS(name, state);
     return false;
   }
   if (!origin.OriginFile->ReachableFiles.count(
         it->second.Unexpanded.OriginFile)) {
-    cmCMakePresetErrors::WORKFLOW_STEP_UNREACHABLE_FROM_FILE(name, state);
+    cmCMakePresetsErrors::WORKFLOW_STEP_UNREACHABLE_FROM_FILE(name, state);
     return false;
   }
   return SetupWorkflowConfigurePreset<T>(it->second.Unexpanded,
@@ -792,13 +793,13 @@
   if (!preset.Hidden) {
     if (version < 3) {
       if (preset.Generator.empty()) {
-        cmCMakePresetErrors::PRESET_MISSING_FIELD(preset.Name, "generator",
-                                                  state);
+        cmCMakePresetsErrors::PRESET_MISSING_FIELD(preset.Name, "generator",
+                                                   state);
         return false;
       }
       if (preset.BinaryDir.empty()) {
-        cmCMakePresetErrors::PRESET_MISSING_FIELD(preset.Name, "binaryDir",
-                                                  state);
+        cmCMakePresetsErrors::PRESET_MISSING_FIELD(preset.Name, "binaryDir",
+                                                   state);
         return false;
       }
     }
@@ -1063,7 +1064,7 @@
     if (allowNoFiles) {
       return true;
     }
-    cmCMakePresetErrors::FILE_NOT_FOUND(filename, &this->parseState);
+    cmCMakePresetsErrors::FILE_NOT_FOUND(filename, &this->parseState);
     return false;
   }
 
@@ -1079,8 +1080,8 @@
 
   for (auto& it : this->ConfigurePresets) {
     if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
-      cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
-                                                   &this->parseState);
+      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first,
+                                                    &this->parseState);
       return false;
     }
   }
@@ -1090,13 +1091,13 @@
       const auto configurePreset =
         this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
       if (configurePreset == this->ConfigurePresets.end()) {
-        cmCMakePresetErrors::INVALID_CONFIGURE_PRESET(it.first,
-                                                      &this->parseState);
+        cmCMakePresetsErrors::INVALID_CONFIGURE_PRESET(it.first,
+                                                       &this->parseState);
         return false;
       }
       if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
             configurePreset->second.Unexpanded.OriginFile)) {
-        cmCMakePresetErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
+        cmCMakePresetsErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
           it.first, &this->parseState);
         return false;
       }
@@ -1109,8 +1110,8 @@
     }
 
     if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
-      cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
-                                                   &this->parseState);
+      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first,
+                                                    &this->parseState);
       return false;
     }
   }
@@ -1120,13 +1121,13 @@
       const auto configurePreset =
         this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
       if (configurePreset == this->ConfigurePresets.end()) {
-        cmCMakePresetErrors::INVALID_CONFIGURE_PRESET(it.first,
-                                                      &this->parseState);
+        cmCMakePresetsErrors::INVALID_CONFIGURE_PRESET(it.first,
+                                                       &this->parseState);
         return false;
       }
       if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
             configurePreset->second.Unexpanded.OriginFile)) {
-        cmCMakePresetErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
+        cmCMakePresetsErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
           it.first, &this->parseState);
         return false;
       }
@@ -1139,8 +1140,8 @@
     }
 
     if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
-      cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
-                                                   &this->parseState);
+      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first,
+                                                    &this->parseState);
       return false;
     }
   }
@@ -1150,13 +1151,13 @@
       const auto configurePreset =
         this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
       if (configurePreset == this->ConfigurePresets.end()) {
-        cmCMakePresetErrors::INVALID_CONFIGURE_PRESET(it.first,
-                                                      &this->parseState);
+        cmCMakePresetsErrors::INVALID_CONFIGURE_PRESET(it.first,
+                                                       &this->parseState);
         return false;
       }
       if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
             configurePreset->second.Unexpanded.OriginFile)) {
-        cmCMakePresetErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
+        cmCMakePresetsErrors::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE(
           it.first, &this->parseState);
         return false;
       }
@@ -1169,8 +1170,8 @@
     }
 
     if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
-      cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
-                                                   &this->parseState);
+      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first,
+                                                    &this->parseState);
       return false;
     }
   }
@@ -1181,12 +1182,12 @@
     const ConfigurePreset* configurePreset = nullptr;
     for (auto const& step : it.second.Unexpanded.Steps) {
       if (configurePreset == nullptr && step.PresetType != Type::Configure) {
-        cmCMakePresetErrors::FIRST_WORKFLOW_STEP_NOT_CONFIGURE(
+        cmCMakePresetsErrors::FIRST_WORKFLOW_STEP_NOT_CONFIGURE(
           step.PresetName, &this->parseState);
         return false;
       }
       if (configurePreset != nullptr && step.PresetType == Type::Configure) {
-        cmCMakePresetErrors::CONFIGURE_WORKFLOW_STEP_NOT_FIRST(
+        cmCMakePresetsErrors::CONFIGURE_WORKFLOW_STEP_NOT_FIRST(
           step.PresetName, &this->parseState);
         return false;
       }
@@ -1219,13 +1220,13 @@
     }
 
     if (configurePreset == nullptr) {
-      cmCMakePresetErrors::NO_WORKFLOW_STEPS(it.first, &this->parseState);
+      cmCMakePresetsErrors::NO_WORKFLOW_STEPS(it.first, &this->parseState);
       return false;
     }
 
     if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
-      cmCMakePresetErrors::INVALID_MACRO_EXPANSION(it.first,
-                                                   &this->parseState);
+      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(it.first,
+                                                    &this->parseState);
       return false;
     }
   }
diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h
index 7844624..a000e7d 100644
--- a/Source/cmCMakePresetsGraph.h
+++ b/Source/cmCMakePresetsGraph.h
@@ -15,9 +15,9 @@
 #include <cm/optional>
 
 #include "cmJSONState.h"
-#include "cmStateTypes.h"
+#include "cmStateTypes.h" // IWYU pragma: keep
 
-#include "CTest/cmCTestTypes.h"
+#include "CTest/cmCTestTypes.h" // IWYU pragma: keep
 
 enum class PackageResolveMode;
 
diff --git a/Source/cmCMakePresetsGraphInternal.h b/Source/cmCMakePresetsGraphInternal.h
index f133efb..5c76e0e 100644
--- a/Source/cmCMakePresetsGraphInternal.h
+++ b/Source/cmCMakePresetsGraphInternal.h
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include <cstddef>
 #include <memory>
 #include <string>
 #include <vector>
@@ -179,4 +180,6 @@
 bool EnvironmentMapHelper(
   std::map<std::string, cm::optional<std::string>>& out,
   const Json::Value* value, cmJSONState* state);
+
+cmJSONHelper<std::nullptr_t> SchemaHelper();
 }
diff --git a/Source/cmCMakePresetsGraphReadJSON.cxx b/Source/cmCMakePresetsGraphReadJSON.cxx
index 54fea40..0d8ec63 100644
--- a/Source/cmCMakePresetsGraphReadJSON.cxx
+++ b/Source/cmCMakePresetsGraphReadJSON.cxx
@@ -15,7 +15,7 @@
 
 #include <cm3p/json/value.h>
 
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmJSONHelpers.h"
@@ -38,7 +38,7 @@
 using cmCMakePresetsGraphInternal::ExpandMacros;
 
 constexpr int MIN_VERSION = 1;
-constexpr int MAX_VERSION = 7;
+constexpr int MAX_VERSION = 8;
 
 struct CMakeVersion
 {
@@ -71,18 +71,18 @@
 auto const ConditionBoolHelper = JSONHelperBuilder::Bool();
 
 auto const ConditionStringListHelper = JSONHelperBuilder::Vector<std::string>(
-  cmCMakePresetErrors::INVALID_CONDITION, ConditionStringHelper);
+  cmCMakePresetsErrors::INVALID_CONDITION, ConditionStringHelper);
 
 auto const ConstConditionHelper =
   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::ConstCondition>(
-    cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
     .Bind("value"_s, &cmCMakePresetsGraphInternal::ConstCondition::Value,
           ConditionBoolHelper, true);
 
 auto const EqualsConditionHelper =
   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::EqualsCondition>(
-    cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
     .Bind("lhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Lhs,
           ConditionStringHelper, true)
@@ -91,7 +91,7 @@
 
 auto const InListConditionHelper =
   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::InListCondition>(
-    cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
     .Bind("string"_s, &cmCMakePresetsGraphInternal::InListCondition::String,
           ConditionStringHelper, true)
@@ -100,7 +100,7 @@
 
 auto const MatchesConditionHelper =
   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::MatchesCondition>(
-    cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
     .Bind("string"_s, &cmCMakePresetsGraphInternal::MatchesCondition::String,
           ConditionStringHelper, true)
@@ -112,10 +112,10 @@
 
 auto const ListConditionVectorHelper =
   JSONHelperBuilder::Vector<std::unique_ptr<cmCMakePresetsGraph::Condition>>(
-    cmCMakePresetErrors::INVALID_CONDITION, SubConditionHelper);
+    cmCMakePresetsErrors::INVALID_CONDITION, SubConditionHelper);
 auto const AnyAllOfConditionHelper =
   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::AnyAllOfCondition>(
-    cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
     .Bind("conditions"_s,
           &cmCMakePresetsGraphInternal::AnyAllOfCondition::Conditions,
@@ -123,7 +123,7 @@
 
 auto const NotConditionHelper =
   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::NotCondition>(
-    cmCMakePresetErrors::INVALID_CONDITION_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
     .Bind("condition"_s,
           &cmCMakePresetsGraphInternal::NotCondition::SubCondition,
@@ -151,12 +151,12 @@
 
   if (value->isObject()) {
     if (!value->isMember("type")) {
-      cmCMakePresetErrors::INVALID_CONDITION(value, state);
+      cmCMakePresetsErrors::INVALID_CONDITION(value, state);
       return false;
     }
 
     if (!(*value)["type"].isString()) {
-      cmCMakePresetErrors::INVALID_CONDITION(value, state);
+      cmCMakePresetsErrors::INVALID_CONDITION(value, state);
       return false;
     }
     auto type = (*value)["type"].asString();
@@ -216,7 +216,7 @@
     }
   }
 
-  cmCMakePresetErrors::INVALID_CONDITION(value, state);
+  cmCMakePresetsErrors::INVALID_CONDITION(value, state);
   return false;
 }
 
@@ -226,7 +226,7 @@
   std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
   auto result = ConditionHelper(ptr, value, state);
   if (ptr && ptr->IsNull()) {
-    cmCMakePresetErrors::INVALID_CONDITION(value, state);
+    cmCMakePresetsErrors::INVALID_CONDITION(value, state);
     return false;
   }
   out = std::move(ptr);
@@ -244,22 +244,22 @@
     out = value->asString();
     return true;
   }
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
 auto const VersionIntHelper =
-  JSONHelperBuilder::Int(cmCMakePresetErrors::INVALID_VERSION);
+  JSONHelperBuilder::Int(cmCMakePresetsErrors::INVALID_VERSION);
 
 auto const VersionHelper = JSONHelperBuilder::Required<int>(
-  cmCMakePresetErrors::NO_VERSION, VersionIntHelper);
+  cmCMakePresetsErrors::NO_VERSION, VersionIntHelper);
 
 auto const RootVersionHelper =
-  JSONHelperBuilder::Object<int>(cmCMakePresetErrors::INVALID_ROOT_OBJECT)
+  JSONHelperBuilder::Object<int>(cmCMakePresetsErrors::INVALID_ROOT_OBJECT)
     .Bind("version"_s, VersionHelper, false);
 
 auto const CMakeVersionUIntHelper =
-  JSONHelperBuilder::UInt(cmCMakePresetErrors::INVALID_VERSION);
+  JSONHelperBuilder::UInt(cmCMakePresetsErrors::INVALID_VERSION);
 
 auto const CMakeVersionHelper =
   JSONHelperBuilder::Object<CMakeVersion>(JsonErrors::INVALID_NAMED_OBJECT_KEY,
@@ -269,14 +269,14 @@
     .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
 
 auto const IncludeHelper =
-  JSONHelperBuilder::String(cmCMakePresetErrors::INVALID_INCLUDE);
+  JSONHelperBuilder::String(cmCMakePresetsErrors::INVALID_INCLUDE);
 
 auto const IncludeVectorHelper = JSONHelperBuilder::Vector<std::string>(
-  cmCMakePresetErrors::INVALID_INCLUDE, IncludeHelper);
+  cmCMakePresetsErrors::INVALID_INCLUDE, IncludeHelper);
 
 auto const RootPresetsHelper =
   JSONHelperBuilder::Object<RootPresets>(
-    cmCMakePresetErrors::INVALID_ROOT_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_ROOT_OBJECT, false)
     .Bind<int>("version"_s, nullptr, VersionHelper)
     .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
           cmCMakePresetsGraphInternal::ConfigurePresetsHelper, false)
@@ -293,8 +293,10 @@
     .Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false)
     .Bind<std::nullptr_t>("vendor"_s, nullptr,
                           cmCMakePresetsGraphInternal::VendorHelper(
-                            cmCMakePresetErrors::INVALID_ROOT),
-                          false);
+                            cmCMakePresetsErrors::INVALID_ROOT),
+                          false)
+    .Bind<std::nullptr_t>("$schema"_s, nullptr,
+                          cmCMakePresetsGraphInternal::SchemaHelper(), false);
 }
 
 namespace cmCMakePresetsGraphInternal {
@@ -309,7 +311,7 @@
                       cmJSONState* state)
 {
   if (!value || !value->isString() || value->asString().empty()) {
-    cmCMakePresetErrors::INVALID_PRESET_NAME(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET_NAME(value, state);
     return false;
   }
   out = value->asString();
@@ -320,7 +322,7 @@
                               const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = JSONHelperBuilder::Vector<std::string>(
-    cmCMakePresetErrors::INVALID_PRESET,
+    cmCMakePresetsErrors::INVALID_PRESET,
     cmCMakePresetsGraphInternal::PresetStringHelper);
   return helper(out, value, state);
 }
@@ -356,7 +358,7 @@
                            cmJSONState* state)
 {
   static auto const helper = JSONHelperBuilder::Vector<int>(
-    cmCMakePresetErrors::INVALID_PRESET, PresetIntHelper);
+    cmCMakePresetsErrors::INVALID_PRESET, PresetIntHelper);
   return helper(out, value, state);
 }
 
@@ -409,10 +411,17 @@
   const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = JSONHelperBuilder::Map<cm::optional<std::string>>(
-    cmCMakePresetErrors::INVALID_PRESET, EnvironmentHelper);
+    cmCMakePresetsErrors::INVALID_PRESET, EnvironmentHelper);
 
   return helper(out, value, state);
 }
+
+cmJSONHelper<std::nullptr_t> SchemaHelper()
+{
+  return [](std::nullptr_t&, const Json::Value*, cmJSONState*) -> bool {
+    return true;
+  };
+}
 }
 
 bool cmCMakePresetsGraph::ReadJSONFile(const std::string& filename,
@@ -429,7 +438,7 @@
       auto fileIt =
         std::find(inProgressFiles.begin(), inProgressFiles.end(), file);
       if (fileIt != inProgressFiles.end()) {
-        cmCMakePresetErrors::CYCLIC_INCLUDE(filename, &this->parseState);
+        cmCMakePresetsErrors::CYCLIC_INCLUDE(filename, &this->parseState);
         return false;
       }
 
@@ -448,43 +457,49 @@
     return result;
   }
   if (v < MIN_VERSION || v > MAX_VERSION) {
-    cmCMakePresetErrors::UNRECOGNIZED_VERSION(&root["version"],
-                                              &this->parseState);
+    cmCMakePresetsErrors::UNRECOGNIZED_VERSION(&root["version"],
+                                               &this->parseState);
     return false;
   }
 
   // Support for build and test presets added in version 2.
   if (v < 2) {
     if (root.isMember("buildPresets")) {
-      cmCMakePresetErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
+      cmCMakePresetsErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
         &root["buildPresets"], &this->parseState);
       return false;
     }
     if (root.isMember("testPresets")) {
-      cmCMakePresetErrors::BUILD_TEST_PRESETS_UNSUPPORTED(&root["testPresets"],
-                                                          &this->parseState);
+      cmCMakePresetsErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
+        &root["testPresets"], &this->parseState);
       return false;
     }
   }
 
   // Support for package presets added in version 6.
   if (v < 6 && root.isMember("packagePresets")) {
-    cmCMakePresetErrors::PACKAGE_PRESETS_UNSUPPORTED(&root["packagePresets"],
-                                                     &this->parseState);
+    cmCMakePresetsErrors::PACKAGE_PRESETS_UNSUPPORTED(&root["packagePresets"],
+                                                      &this->parseState);
     return false;
   }
 
   // Support for workflow presets added in version 6.
   if (v < 6 && root.isMember("workflowPresets")) {
-    cmCMakePresetErrors::WORKFLOW_PRESETS_UNSUPPORTED(&root["workflowPresets"],
-                                                      &this->parseState);
+    cmCMakePresetsErrors::WORKFLOW_PRESETS_UNSUPPORTED(
+      &root["workflowPresets"], &this->parseState);
     return false;
   }
 
   // Support for include added in version 4.
   if (v < 4 && root.isMember("include")) {
-    cmCMakePresetErrors::INCLUDE_UNSUPPORTED(&root["include"],
-                                             &this->parseState);
+    cmCMakePresetsErrors::INCLUDE_UNSUPPORTED(&root["include"],
+                                              &this->parseState);
+    return false;
+  }
+
+  // Support for $schema added in version 8.
+  if (v < 8 && root.isMember("$schema")) {
+    cmCMakePresetsErrors::SCHEMA_UNSUPPORTED(&this->parseState);
     return false;
   }
 
@@ -498,20 +513,20 @@
   unsigned int currentPatch = cmVersion::GetPatchVersion();
   auto const& required = presets.CMakeMinimumRequired;
   if (required.Major > currentMajor) {
-    ErrorGenerator error = cmCMakePresetErrors::UNRECOGNIZED_CMAKE_VERSION(
+    ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
       "major", currentMajor, required.Major);
     error(&root["cmakeMinimumRequired"]["major"], &this->parseState);
     return false;
   }
   if (required.Major == currentMajor) {
     if (required.Minor > currentMinor) {
-      ErrorGenerator error = cmCMakePresetErrors::UNRECOGNIZED_CMAKE_VERSION(
+      ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
         "minor", currentMinor, required.Minor);
       error(&root["cmakeMinimumRequired"]["minor"], &this->parseState);
       return false;
     }
     if (required.Minor == currentMinor && required.Patch > currentPatch) {
-      ErrorGenerator error = cmCMakePresetErrors::UNRECOGNIZED_CMAKE_VERSION(
+      ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
         "patch", currentPatch, required.Patch);
       error(&root["cmakeMinimumRequired"]["patch"], &this->parseState);
       return false;
@@ -537,26 +552,26 @@
     presetPair.Unexpanded = preset;
     presetPair.Expanded = cm::nullopt;
     if (!this->ConfigurePresets.emplace(preset.Name, presetPair).second) {
-      cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
       return false;
     }
 
     // Support for installDir presets added in version 3.
     if (v < 3 && !preset.InstallDir.empty()) {
-      cmCMakePresetErrors::INSTALL_PREFIX_UNSUPPORTED(&root["installDir"],
-                                                      &this->parseState);
+      cmCMakePresetsErrors::INSTALL_PREFIX_UNSUPPORTED(&root["installDir"],
+                                                       &this->parseState);
       return false;
     }
 
     // Support for conditions added in version 3.
     if (v < 3 && preset.ConditionEvaluator) {
-      cmCMakePresetErrors::CONDITION_UNSUPPORTED(&this->parseState);
+      cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
       return false;
     }
 
     // Support for toolchainFile presets added in version 3.
     if (v < 3 && !preset.ToolchainFile.empty()) {
-      cmCMakePresetErrors::TOOLCHAIN_FILE_UNSUPPORTED(&this->parseState);
+      cmCMakePresetsErrors::TOOLCHAIN_FILE_UNSUPPORTED(&this->parseState);
       return false;
     }
 
@@ -564,7 +579,7 @@
     if (v < 7 &&
         (preset.TraceMode.has_value() || preset.TraceFormat.has_value() ||
          !preset.TraceRedirect.empty() || !preset.TraceSource.empty())) {
-      cmCMakePresetErrors::TRACE_UNSUPPORTED(&this->parseState);
+      cmCMakePresetsErrors::TRACE_UNSUPPORTED(&this->parseState);
       return false;
     }
 
@@ -582,13 +597,13 @@
     presetPair.Unexpanded = preset;
     presetPair.Expanded = cm::nullopt;
     if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
-      cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
       return false;
     }
 
     // Support for conditions added in version 3.
     if (v < 3 && preset.ConditionEvaluator) {
-      cmCMakePresetErrors::CONDITION_UNSUPPORTED(&this->parseState);
+      cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
       return false;
     }
 
@@ -606,26 +621,26 @@
     presetPair.Unexpanded = preset;
     presetPair.Expanded = cm::nullopt;
     if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
-      cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
       return false;
     }
 
     // Support for conditions added in version 3.
     if (v < 3 && preset.ConditionEvaluator) {
-      cmCMakePresetErrors::CONDITION_UNSUPPORTED(&this->parseState);
+      cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
       return false;
     }
 
     // Support for TestOutputTruncation added in version 5.
     if (v < 5 && preset.Output && preset.Output->TestOutputTruncation) {
-      cmCMakePresetErrors::TEST_OUTPUT_TRUNCATION_UNSUPPORTED(
+      cmCMakePresetsErrors::TEST_OUTPUT_TRUNCATION_UNSUPPORTED(
         &this->parseState);
       return false;
     }
 
     // Support for outputJUnitFile added in version 6.
     if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) {
-      cmCMakePresetErrors::CTEST_JUNIT_UNSUPPORTED(&this->parseState);
+      cmCMakePresetsErrors::CTEST_JUNIT_UNSUPPORTED(&this->parseState);
       return false;
     }
 
@@ -643,7 +658,7 @@
     presetPair.Unexpanded = preset;
     presetPair.Expanded = cm::nullopt;
     if (!this->PackagePresets.emplace(preset.Name, presetPair).second) {
-      cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
       return false;
     }
 
@@ -664,7 +679,7 @@
     presetPair.Unexpanded = preset;
     presetPair.Expanded = cm::nullopt;
     if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) {
-      cmCMakePresetErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
+      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
       return false;
     }
 
@@ -718,8 +733,8 @@
     // Support for macro expansion in includes added in version 7
     if (v >= 7) {
       if (ExpandMacros(include, macroExpanders, v) != ExpandMacroResult::Ok) {
-        cmCMakePresetErrors::INVALID_INCLUDE(&root["include"][i],
-                                             &this->parseState);
+        cmCMakePresetsErrors::INVALID_INCLUDE(&root["include"][i],
+                                              &this->parseState);
         return false;
       }
     }
diff --git a/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx b/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx
index 07f2bc3..c743ac1 100644
--- a/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONBuildPresets.cxx
@@ -13,7 +13,7 @@
 #include <cm3p/json/value.h>
 
 #include "cmBuildOptions.h"
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmJSONHelpers.h"
@@ -32,7 +32,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -43,7 +43,7 @@
   } else if (value->asString() == "only") {
     out = PackageResolveMode::OnlyResolve;
   } else {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -59,7 +59,7 @@
 
 auto const BuildPresetHelper =
   JSONHelperBuilder::Object<BuildPreset>(
-    cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_PRESET_OBJECT, false)
     .Bind("name"_s, &BuildPreset::Name,
           cmCMakePresetsGraphInternal::PresetNameHelper)
     .Bind("inherits"_s, &BuildPreset::Inherits,
@@ -69,7 +69,7 @@
           cmCMakePresetsGraphInternal::PresetBoolHelper, false)
     .Bind<std::nullptr_t>("vendor"_s, nullptr,
                           cmCMakePresetsGraphInternal::VendorHelper(
-                            cmCMakePresetErrors::INVALID_PRESET),
+                            cmCMakePresetsErrors::INVALID_PRESET),
                           false)
     .Bind("displayName"_s, &BuildPreset::DisplayName,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -105,7 +105,7 @@
                         const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = JSONHelperBuilder::Vector<BuildPreset>(
-    cmCMakePresetErrors::INVALID_PRESETS, BuildPresetHelper);
+    cmCMakePresetsErrors::INVALID_PRESETS, BuildPresetHelper);
 
   return helper(out, value, state);
 }
diff --git a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
index 66ec6a4..c52a187 100644
--- a/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONConfigurePresets.cxx
@@ -12,13 +12,14 @@
 
 #include <cm3p/json/value.h>
 
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmJSONHelpers.h"
-#include "cmJSONState.h"
 #include "cmStateTypes.h"
 
+class cmJSONState;
+
 namespace {
 using CacheVariable = cmCMakePresetsGraph::CacheVariable;
 using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
@@ -36,7 +37,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -50,7 +51,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -84,7 +85,7 @@
       return objectHelper(out, value, state);
     }
 
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   };
 }
@@ -103,7 +104,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -114,7 +115,7 @@
   } else if (value->asString() == "expand") {
     out = TraceEnableMode::Expand;
   } else {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -130,7 +131,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -139,7 +140,7 @@
   } else if (value->asString() == "json-v1") {
     out = TraceOutputFormat::JSONv1;
   } else {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -166,7 +167,7 @@
 
 auto const VariableObjectHelper =
   JSONHelperBuilder::Object<CacheVariable>(
-    cmCMakePresetErrors::INVALID_VARIABLE_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_VARIABLE_OBJECT, false)
     .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
     .Bind("value"_s, &CacheVariable::Value, VariableValueHelper);
 
@@ -195,13 +196,13 @@
     out = cm::nullopt;
     return true;
   }
-  cmCMakePresetErrors::INVALID_VARIABLE(value, state);
+  cmCMakePresetsErrors::INVALID_VARIABLE(value, state);
   return false;
 }
 
 auto const VariablesHelper =
   JSONHelperBuilder::Map<cm::optional<CacheVariable>>(
-    cmCMakePresetErrors::INVALID_PRESET, VariableHelper);
+    cmCMakePresetsErrors::INVALID_PRESET, VariableHelper);
 
 auto const PresetWarningsHelper =
   JSONHelperBuilder::Object<ConfigurePreset>(
@@ -237,7 +238,7 @@
 
 auto const PresetTraceHelper =
   JSONHelperBuilder::Object<ConfigurePreset>(
-    cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_PRESET_OBJECT, false)
     .Bind("mode"_s, &ConfigurePreset::TraceMode, TraceEnableModeHelper, false)
     .Bind("format"_s, &ConfigurePreset::TraceFormat, TraceOutputFormatHelper,
           false)
@@ -249,7 +250,7 @@
 
 auto const ConfigurePresetHelper =
   JSONHelperBuilder::Object<ConfigurePreset>(
-    cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_PRESET_OBJECT, false)
     .Bind("name"_s, &ConfigurePreset::Name,
           cmCMakePresetsGraphInternal::PresetNameHelper)
     .Bind("inherits"_s, &ConfigurePreset::Inherits,
@@ -259,7 +260,7 @@
           cmCMakePresetsGraphInternal::PresetBoolHelper, false)
     .Bind<std::nullptr_t>("vendor"_s, nullptr,
                           cmCMakePresetsGraphInternal::VendorHelper(
-                            cmCMakePresetErrors::INVALID_PRESET),
+                            cmCMakePresetsErrors::INVALID_PRESET),
                           false)
     .Bind("displayName"_s, &ConfigurePreset::DisplayName,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -294,7 +295,7 @@
                             const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = JSONHelperBuilder::Vector<ConfigurePreset>(
-    cmCMakePresetErrors::INVALID_PRESETS, ConfigurePresetHelper);
+    cmCMakePresetsErrors::INVALID_PRESETS, ConfigurePresetHelper);
 
   return helper(out, value, state);
 }
diff --git a/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
index 7290d4d..ac49c62 100644
--- a/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONPackagePresets.cxx
@@ -1,7 +1,6 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include <cstddef>
-#include <functional>
 #include <map>
 #include <memory>
 #include <string>
@@ -12,7 +11,7 @@
 
 #include <cm3p/json/value.h>
 
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmJSONHelpers.h"
@@ -30,14 +29,14 @@
           cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false);
 
 auto const VariableHelper =
-  cmJSONHelperBuilder::String(cmCMakePresetErrors::INVALID_VARIABLE);
+  cmJSONHelperBuilder::String(cmCMakePresetsErrors::INVALID_VARIABLE);
 
 auto const VariablesHelper = cmJSONHelperBuilder::Map<std::string>(
-  cmCMakePresetErrors::INVALID_VARIABLE, VariableHelper);
+  cmCMakePresetsErrors::INVALID_VARIABLE, VariableHelper);
 
 auto const PackagePresetHelper =
   cmJSONHelperBuilder::Object<PackagePreset>(
-    cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_PRESET_OBJECT, false)
     .Bind("name"_s, &PackagePreset::Name,
           cmCMakePresetsGraphInternal::PresetNameHelper)
     .Bind("inherits"_s, &PackagePreset::Inherits,
@@ -47,7 +46,7 @@
           cmCMakePresetsGraphInternal::PresetBoolHelper, false)
     .Bind<std::nullptr_t>("vendor"_s, nullptr,
                           cmCMakePresetsGraphInternal::VendorHelper(
-                            cmCMakePresetErrors::INVALID_PRESET),
+                            cmCMakePresetsErrors::INVALID_PRESET),
                           false)
     .Bind("displayName"_s, &PackagePreset::DisplayName,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -85,7 +84,7 @@
                           const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = cmJSONHelperBuilder::Vector<PackagePreset>(
-    cmCMakePresetErrors::INVALID_PRESETS, PackagePresetHelper);
+    cmCMakePresetsErrors::INVALID_PRESETS, PackagePresetHelper);
 
   return helper(out, value, state);
 }
diff --git a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
index 791be04..f9e0753 100644
--- a/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONTestPresets.cxx
@@ -1,7 +1,6 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include <cstddef>
-#include <functional>
 #include <map>
 #include <memory>
 #include <string>
@@ -12,7 +11,7 @@
 
 #include <cm3p/json/value.h>
 
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmJSONHelpers.h"
@@ -35,7 +34,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -54,7 +53,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -72,7 +71,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -91,7 +90,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -202,7 +201,7 @@
   cmJSONState* state)
 {
   if (!value || !value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -216,7 +215,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -233,7 +232,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -252,7 +251,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -274,7 +273,7 @@
   }
 
   if (!value->isString()) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -293,7 +292,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -339,7 +338,7 @@
 
 auto const TestPresetHelper =
   JSONHelperBuilder::Object<TestPreset>(
-    cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_PRESET_OBJECT, false)
     .Bind("name"_s, &TestPreset::Name,
           cmCMakePresetsGraphInternal::PresetNameHelper)
     .Bind("inherits"_s, &TestPreset::Inherits,
@@ -349,7 +348,7 @@
           cmCMakePresetsGraphInternal::PresetBoolHelper, false)
     .Bind<std::nullptr_t>("vendor"_s, nullptr,
                           cmCMakePresetsGraphInternal::VendorHelper(
-                            cmCMakePresetErrors::INVALID_PRESET),
+                            cmCMakePresetsErrors::INVALID_PRESET),
                           false)
     .Bind("displayName"_s, &TestPreset::DisplayName,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -381,7 +380,7 @@
                        const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = JSONHelperBuilder::Vector<TestPreset>(
-    cmCMakePresetErrors::INVALID_PRESETS, TestPresetHelper);
+    cmCMakePresetsErrors::INVALID_PRESETS, TestPresetHelper);
 
   return helper(out, value, state);
 }
diff --git a/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
index 7224e17..e152871 100644
--- a/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
+++ b/Source/cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
@@ -1,7 +1,6 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include <cstddef>
-#include <functional>
 #include <string>
 #include <vector>
 
@@ -9,7 +8,7 @@
 
 #include <cm3p/json/value.h>
 
-#include "cmCMakePresetErrors.h"
+#include "cmCMakePresetsErrors.h"
 #include "cmCMakePresetsGraph.h"
 #include "cmCMakePresetsGraphInternal.h"
 #include "cmJSONHelpers.h"
@@ -23,7 +22,7 @@
                             const Json::Value* value, cmJSONState* state)
 {
   if (!value) {
-    cmCMakePresetErrors::INVALID_PRESET(value, state);
+    cmCMakePresetsErrors::INVALID_PRESET(value, state);
     return false;
   }
 
@@ -51,7 +50,7 @@
     return true;
   }
 
-  cmCMakePresetErrors::INVALID_PRESET(value, state);
+  cmCMakePresetsErrors::INVALID_PRESET(value, state);
   return false;
 }
 
@@ -65,16 +64,16 @@
 
 auto const WorkflowStepsHelper =
   cmJSONHelperBuilder::Vector<WorkflowPreset::WorkflowStep>(
-    cmCMakePresetErrors::INVALID_PRESET, WorkflowStepHelper);
+    cmCMakePresetsErrors::INVALID_PRESET, WorkflowStepHelper);
 
 auto const WorkflowPresetHelper =
   cmJSONHelperBuilder::Object<WorkflowPreset>(
-    cmCMakePresetErrors::INVALID_PRESET_OBJECT, false)
+    cmCMakePresetsErrors::INVALID_PRESET_OBJECT, false)
     .Bind("name"_s, &WorkflowPreset::Name,
           cmCMakePresetsGraphInternal::PresetNameHelper)
     .Bind<std::nullptr_t>("vendor"_s, nullptr,
                           cmCMakePresetsGraphInternal::VendorHelper(
-                            cmCMakePresetErrors::INVALID_PRESET),
+                            cmCMakePresetsErrors::INVALID_PRESET),
                           false)
     .Bind("displayName"_s, &WorkflowPreset::DisplayName,
           cmCMakePresetsGraphInternal::PresetStringHelper, false)
@@ -89,7 +88,7 @@
   const Json::Value* value, cmJSONState* state)
 {
   static auto const helper = cmJSONHelperBuilder::Vector<WorkflowPreset>(
-    cmCMakePresetErrors::INVALID_PRESETS, WorkflowPresetHelper);
+    cmCMakePresetsErrors::INVALID_PRESETS, WorkflowPresetHelper);
 
   return helper(out, value, state);
 }
diff --git a/Source/cmCPackPropertiesGenerator.cxx b/Source/cmCPackPropertiesGenerator.cxx
index cc9ad01..fbf7d73 100644
--- a/Source/cmCPackPropertiesGenerator.cxx
+++ b/Source/cmCPackPropertiesGenerator.cxx
@@ -1,6 +1,7 @@
 #include "cmCPackPropertiesGenerator.h"
 
 #include <map>
+#include <memory>
 #include <ostream>
 
 #include "cmGeneratorExpression.h"
diff --git a/Source/cmCPluginAPI.cxx b/Source/cmCPluginAPI.cxx
index 13ccf4f..655f4bc 100644
--- a/Source/cmCPluginAPI.cxx
+++ b/Source/cmCPluginAPI.cxx
@@ -628,8 +628,8 @@
 {
   cmCPluginAPISourceFile* sf = static_cast<cmCPluginAPISourceFile*>(arg);
   if (cmSourceFile* rsf = sf->RealSourceFile) {
-    if (value == nullptr) {
-      rsf->SetProperty(prop, nullptr);
+    if (!value) {
+      rsf->RemoveProperty(prop);
     } else {
       rsf->SetProperty(prop, value);
     }
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index a311041..b7232f3 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -5,12 +5,14 @@
 #include <algorithm>
 #include <cctype>
 #include <chrono>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
 #include <iostream>
 #include <map>
+#include <ratio>
 #include <sstream>
 #include <string>
 #include <utility>
@@ -23,13 +25,13 @@
 #include <cmext/string_view>
 
 #include <cm3p/curl/curl.h>
+#include <cm3p/uv.h>
 #include <cm3p/zlib.h>
 
 #include "cmsys/Base64.h"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/SystemInformation.hxx"
 #if defined(_WIN32)
@@ -63,6 +65,9 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
@@ -999,8 +1004,7 @@
       for (kk = 0; kk < d.GetNumberOfFiles(); kk++) {
         const char* file = d.GetFile(kk);
         std::string fullname = notes_dir + "/" + file;
-        if (cmSystemTools::FileExists(fullname) &&
-            !cmSystemTools::FileIsDirectory(fullname)) {
+        if (cmSystemTools::FileExists(fullname, true)) {
           if (!this->Impl->NotesFiles.empty()) {
             this->Impl->NotesFiles += ";";
           }
@@ -1073,9 +1077,9 @@
 // ######################################################################
 // ######################################################################
 
-int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
-                            int* retVal, const char* dir, cmDuration timeout,
-                            std::ostream& ofs, Encoding encoding)
+bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
+                             int* retVal, const char* dir, cmDuration timeout,
+                             std::ostream& ofs, Encoding encoding)
 {
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -1084,107 +1088,107 @@
     return false;
   }
 
-  std::vector<const char*> argv;
-  argv.reserve(args.size() + 1);
-  for (std::string const& a : args) {
-    argv.push_back(a.c_str());
-  }
-  argv.push_back(nullptr);
-
   output.clear();
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
-  for (char const* arg : argv) {
-    if (!arg) {
-      break;
-    }
+  for (auto const& arg : args) {
     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
   }
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(args).SetMergedBuiltinStreams();
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
+  }
+  auto chain = builder.Start();
+  cm::uv_pipe_ptr outputStream;
+  outputStream.init(chain.GetLoop(), 0);
+  uv_pipe_open(outputStream, chain.OutputStream());
 
   // Initialize tick's
   std::string::size_type tick = 0;
   std::string::size_type tick_len = 1024;
   std::string::size_type tick_line_len = 50;
 
-  char* data;
-  int length;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              "   Each . represents " << tick_len
                                      << " bytes of output\n"
                                         "    "
                                      << std::flush);
-  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
-    processOutput.DecodeText(data, length, strdata);
-    for (char& cc : strdata) {
-      if (cc == 0) {
-        cc = '\n';
+  auto outputHandle = cmUVStreamRead(
+    outputStream,
+    [this, &processOutput, &output, &tick, &tick_len, &tick_line_len,
+     &ofs](std::vector<char> data) {
+      std::string strdata;
+      processOutput.DecodeText(data.data(), data.size(), strdata);
+      for (char& cc : strdata) {
+        if (cc == 0) {
+          cc = '\n';
+        }
       }
-    }
-    output.append(strdata);
-    while (output.size() > (tick * tick_len)) {
-      tick++;
-      cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
-      if (tick % tick_line_len == 0 && tick > 0) {
-        cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
-                   "  Size: " << int((double(output.size()) / 1024.0) + 1)
-                              << "K\n    " << std::flush);
+      output.append(strdata);
+      while (output.size() > (tick * tick_len)) {
+        tick++;
+        cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
+        if (tick % tick_line_len == 0 && tick > 0) {
+          cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
+                     "  Size: " << int((double(output.size()) / 1024.0) + 1)
+                                << "K\n    " << std::flush);
+        }
       }
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (ofs) {
-      ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
-    }
-  }
-  processOutput.DecodeText(std::string(), strdata);
-  if (!strdata.empty()) {
-    output.append(strdata);
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (ofs) {
-      ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
-    }
-  }
+      cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                 cmCTestLogWrite(strdata.c_str(), strdata.size()));
+      if (ofs) {
+        ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
+      }
+    },
+    [this, &processOutput, &output, &ofs]() {
+      std::string strdata;
+      processOutput.DecodeText(std::string(), strdata);
+      if (!strdata.empty()) {
+        output.append(strdata);
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                   cmCTestLogWrite(strdata.c_str(), strdata.size()));
+        if (ofs) {
+          ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
+        }
+      }
+    });
+
+  bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              " Size of output: " << int(double(output.size()) / 1024.0) << "K"
                                  << std::endl);
 
-  cmsysProcess_WaitForExit(cp, nullptr);
-
-  int result = cmsysProcess_GetState(cp);
-
-  if (result == cmsysProcess_State_Exited) {
-    *retVal = cmsysProcess_GetExitValue(cp);
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               "Command exited with the value: " << *retVal << std::endl);
-  } else if (result == cmsysProcess_State_Exception) {
-    *retVal = cmsysProcess_GetExitException(cp);
-    cmCTestLog(this, WARNING,
-               "There was an exception: " << *retVal << std::endl);
-  } else if (result == cmsysProcess_State_Expired) {
+  if (finished) {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        *retVal = static_cast<int>(status.ExitStatus);
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                   "Command exited with the value: " << *retVal << std::endl);
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn:
+        output += "\n*** ERROR executing: ";
+        output += exception.second;
+        output += "\n***The build process failed.";
+        cmCTestLog(this, ERROR_MESSAGE,
+                   "There was an error: " << exception.second << std::endl);
+        break;
+      default:
+        *retVal = static_cast<int>(exception.first);
+        cmCTestLog(this, WARNING,
+                   "There was an exception: " << *retVal << std::endl);
+        break;
+    }
+  } else {
     cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
-  } else if (result == cmsysProcess_State_Error) {
-    output += "\n*** ERROR executing: ";
-    output += cmsysProcess_GetErrorString(cp);
-    output += "\n***The build process failed.";
-    cmCTestLog(this, ERROR_MESSAGE,
-               "There was an error: " << cmsysProcess_GetErrorString(cp)
-                                      << std::endl);
   }
 
-  cmsysProcess_Delete(cp);
-
-  return result;
+  return true;
 }
 
 // ######################################################################
@@ -1192,9 +1196,10 @@
 // ######################################################################
 // ######################################################################
 
-int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
-                     int* retVal, std::ostream* log, cmDuration testTimeOut,
-                     std::vector<std::string>* environment, Encoding encoding)
+bool cmCTest::RunTest(const std::vector<std::string>& argv,
+                      std::string* output, int* retVal, std::ostream* log,
+                      cmDuration testTimeOut,
+                      std::vector<std::string>* environment, Encoding encoding)
 {
   bool modifyEnv = (environment && !environment->empty());
 
@@ -1233,19 +1238,16 @@
     inst.SetStreams(&oss, &oss);
 
     std::vector<std::string> args;
-    for (char const* i : argv) {
-      if (i) {
-        // make sure we pass the timeout in for any build and test
-        // invocations. Since --build-generator is required this is a
-        // good place to check for it, and to add the arguments in
-        if (strcmp(i, "--build-generator") == 0 &&
-            timeout != cmCTest::MaxDuration() &&
-            timeout > cmDuration::zero()) {
-          args.emplace_back("--test-timeout");
-          args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
-        }
-        args.emplace_back(i);
+    for (auto const& i : argv) {
+      // make sure we pass the timeout in for any build and test
+      // invocations. Since --build-generator is required this is a
+      // good place to check for it, and to add the arguments in
+      if (i == "--build-generator" && timeout != cmCTest::MaxDuration() &&
+          timeout > cmDuration::zero()) {
+        args.emplace_back("--test-timeout");
+        args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
       }
+      args.emplace_back(i);
     }
     if (log) {
       *log << "* Run internal CTest" << std::endl;
@@ -1271,7 +1273,7 @@
                                                              << std::endl);
     }
 
-    return cmsysProcess_State_Exited;
+    return true;
   }
   std::vector<char> tempOutput;
   if (output) {
@@ -1284,41 +1286,43 @@
     cmSystemTools::AppendEnv(*environment);
   }
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(argv).SetMergedBuiltinStreams();
   cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
-  if (cmSystemTools::GetRunCommandHideConsole()) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  }
+  auto chain = builder.Start();
 
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
-
-  char* data;
-  int length;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
-  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
-    processOutput.DecodeText(data, length, strdata);
-    if (output) {
-      cm::append(tempOutput, data, data + length);
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (log) {
-      log->write(strdata.c_str(), strdata.size());
-    }
-  }
-  processOutput.DecodeText(std::string(), strdata);
-  if (!strdata.empty()) {
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-               cmCTestLogWrite(strdata.c_str(), strdata.size()));
-    if (log) {
-      log->write(strdata.c_str(), strdata.size());
-    }
-  }
+  cm::uv_pipe_ptr outputStream;
+  outputStream.init(chain.GetLoop(), 0);
+  uv_pipe_open(outputStream, chain.OutputStream());
+  auto outputHandle = cmUVStreamRead(
+    outputStream,
+    [this, &processOutput, &output, &tempOutput,
+     &log](std::vector<char> data) {
+      std::string strdata;
+      processOutput.DecodeText(data.data(), data.size(), strdata);
+      if (output) {
+        cm::append(tempOutput, data.data(), data.data() + data.size());
+      }
+      cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                 cmCTestLogWrite(strdata.c_str(), strdata.size()));
+      if (log) {
+        log->write(strdata.c_str(), strdata.size());
+      }
+    },
+    [this, &processOutput, &log]() {
+      std::string strdata;
+      processOutput.DecodeText(std::string(), strdata);
+      if (!strdata.empty()) {
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+                   cmCTestLogWrite(strdata.c_str(), strdata.size()));
+        if (log) {
+          log->write(strdata.c_str(), strdata.size());
+        }
+      }
+    });
 
-  cmsysProcess_WaitForExit(cp, nullptr);
+  bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
   processOutput.DecodeText(tempOutput, tempOutput);
   if (output && tempOutput.begin() != tempOutput.end()) {
     output->append(tempOutput.data(), tempOutput.size());
@@ -1326,33 +1330,41 @@
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
              "-- Process completed" << std::endl);
 
-  int result = cmsysProcess_GetState(cp);
+  bool result = false;
 
-  if (result == cmsysProcess_State_Exited) {
-    *retVal = cmsysProcess_GetExitValue(cp);
-    if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
-      this->OutputTestErrors(tempOutput);
+  if (complete) {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        *retVal = static_cast<int>(status.ExitStatus);
+        if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
+          this->OutputTestErrors(tempOutput);
+        }
+        result = true;
+        break;
+      case cmUVProcessChain::ExceptionCode::Spawn: {
+        std::string outerr =
+          cmStrCat("\n*** ERROR executing: ", exception.second);
+        if (output) {
+          *output += outerr;
+        }
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
+      } break;
+      default: {
+        if (this->Impl->OutputTestOutputOnTestFailure) {
+          this->OutputTestErrors(tempOutput);
+        }
+        *retVal = status.TermSignal;
+        std::string outerr =
+          cmStrCat("\n*** Exception executing: ", exception.second);
+        if (output) {
+          *output += outerr;
+        }
+        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
+      } break;
     }
-  } else if (result == cmsysProcess_State_Exception) {
-    if (this->Impl->OutputTestOutputOnTestFailure) {
-      this->OutputTestErrors(tempOutput);
-    }
-    *retVal = cmsysProcess_GetExitException(cp);
-    std::string outerr = cmStrCat("\n*** Exception executing: ",
-                                  cmsysProcess_GetExceptionString(cp));
-    if (output) {
-      *output += outerr;
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
-  } else if (result == cmsysProcess_State_Error) {
-    std::string outerr =
-      cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
-    if (output) {
-      *output += outerr;
-    }
-    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
   }
-  cmsysProcess_Delete(cp);
 
   return result;
 }
@@ -3470,49 +3482,70 @@
   stdOut->clear();
   stdErr->clear();
 
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  if (cmSystemTools::GetRunCommandHideConsole()) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(args)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
   }
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
+
+  cm::uv_timer_ptr timer;
+  bool timedOut = false;
+  if (timeout.count()) {
+    timer.init(chain.GetLoop(), &timedOut);
+    timer.start(
+      [](uv_timer_t* t) {
+        auto* timedOutPtr = static_cast<bool*>(t->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+  }
 
   std::vector<char> tempOutput;
+  bool outFinished = false;
+  cm::uv_pipe_ptr outStream;
   std::vector<char> tempError;
-  char* data;
-  int length;
+  bool errFinished = false;
+  cm::uv_pipe_ptr errStream;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
-  int res;
-  bool done = false;
-  while (!done) {
-    res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
-    switch (res) {
-      case cmsysProcess_Pipe_STDOUT:
-        cm::append(tempOutput, data, data + length);
-        break;
-      case cmsysProcess_Pipe_STDERR:
-        cm::append(tempError, data, data + length);
-        break;
-      default:
-        done = true;
-    }
-    if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
-        this->Impl->ExtraVerbose) {
-      processOutput.DecodeText(data, length, strdata);
-      cmSystemTools::Stdout(strdata);
-    }
+  auto startRead = [this, &chain, &processOutput](
+                     cm::uv_pipe_ptr& pipe, int stream,
+                     std::vector<char>& temp,
+                     bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
+    pipe.init(chain.GetLoop(), 0);
+    uv_pipe_open(pipe, stream);
+    return cmUVStreamRead(
+      pipe,
+      [this, &temp, &processOutput](std::vector<char> data) {
+        cm::append(temp, data);
+        if (this->Impl->ExtraVerbose) {
+          std::string strdata;
+          processOutput.DecodeText(data.data(), data.size(), strdata);
+          cmSystemTools::Stdout(strdata);
+        }
+      },
+      [&finished]() { finished = true; });
+  };
+  auto outputHandle =
+    startRead(outStream, chain.OutputStream(), tempOutput, outFinished);
+  auto errorHandle =
+    startRead(errStream, chain.ErrorStream(), tempError, errFinished);
+  while (!timedOut && !(outFinished && errFinished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
   }
   if (this->Impl->ExtraVerbose) {
+    std::string strdata;
     processOutput.DecodeText(std::string(), strdata);
     if (!strdata.empty()) {
       cmSystemTools::Stdout(strdata);
     }
   }
 
-  cmsysProcess_WaitForExit(cp, nullptr);
+  while (!timedOut && !chain.Finished()) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
   if (!tempOutput.empty()) {
     processOutput.DecodeText(tempOutput, tempOutput);
     stdOut->append(tempOutput.data(), tempOutput.size());
@@ -3523,32 +3556,32 @@
   }
 
   bool result = true;
-  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitValue(cp);
-    } else {
-      if (cmsysProcess_GetExitValue(cp) != 0) {
-        result = false;
-      }
-    }
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
-    const char* exception_str = cmsysProcess_GetExceptionString(cp);
-    cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
-    stdErr->append(exception_str, strlen(exception_str));
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
-    const char* error_str = cmsysProcess_GetErrorString(cp);
-    cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
-    stdErr->append(error_str, strlen(error_str));
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
+  if (timedOut) {
     const char* error_str = "Process terminated due to timeout\n";
     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
     stdErr->append(error_str, strlen(error_str));
     result = false;
+  } else {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (retVal) {
+          *retVal = static_cast<int>(status.ExitStatus);
+        } else {
+          if (status.ExitStatus != 0) {
+            result = false;
+          }
+        }
+        break;
+      default: {
+        cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl);
+        stdErr->append(exception.second);
+        result = false;
+      } break;
+    }
   }
 
-  cmsysProcess_Delete(cp);
   return result;
 }
 
diff --git a/Source/cmCTest.h b/Source/cmCTest.h
index 9a8d5a6..1644d84 100644
--- a/Source/cmCTest.h
+++ b/Source/cmCTest.h
@@ -254,10 +254,10 @@
    * Run command specialized for make and configure. Returns process status
    * and retVal is return value or exception.
    */
-  int RunMakeCommand(const std::string& command, std::string& output,
-                     int* retVal, const char* dir, cmDuration timeout,
-                     std::ostream& ofs,
-                     Encoding encoding = cmProcessOutput::Auto);
+  bool RunMakeCommand(const std::string& command, std::string& output,
+                      int* retVal, const char* dir, cmDuration timeout,
+                      std::ostream& ofs,
+                      Encoding encoding = cmProcessOutput::Auto);
 
   /** Return the current tag */
   std::string GetCurrentTag();
@@ -303,10 +303,10 @@
    * environment variables prior to running the test. After running the test,
    * environment variables are restored to their previous values.
    */
-  int RunTest(std::vector<const char*> args, std::string* output, int* retVal,
-              std::ostream* logfile, cmDuration testTimeOut,
-              std::vector<std::string>* environment,
-              Encoding encoding = cmProcessOutput::Auto);
+  bool RunTest(const std::vector<std::string>& args, std::string* output,
+               int* retVal, std::ostream* logfile, cmDuration testTimeOut,
+               std::vector<std::string>* environment,
+               Encoding encoding = cmProcessOutput::Auto);
 
   /**
    * Get the handler object
diff --git a/Source/cmCacheManager.cxx b/Source/cmCacheManager.cxx
index 0c6b225..5c25cb9 100644
--- a/Source/cmCacheManager.cxx
+++ b/Source/cmCacheManager.cxx
@@ -597,15 +597,14 @@
   this->SetProperty(p, v ? std::string{ "ON" } : std::string{ "OFF" });
 }
 
-void cmCacheManager::CacheEntry::SetProperty(const std::string& prop,
-                                             std::nullptr_t)
+void cmCacheManager::CacheEntry::RemoveProperty(const std::string& prop)
 {
   if (prop == "TYPE") {
     this->Type = cmState::StringToCacheEntryType("STRING");
   } else if (prop == "VALUE") {
-    this->Value = "";
+    this->Value.clear();
   } else {
-    this->Properties.SetProperty(prop, cmValue{ nullptr });
+    this->Properties.RemoveProperty(prop);
   }
 }
 
diff --git a/Source/cmCacheManager.h b/Source/cmCacheManager.h
index 5268248..ae32759 100644
--- a/Source/cmCacheManager.h
+++ b/Source/cmCacheManager.h
@@ -41,7 +41,7 @@
     bool GetPropertyAsBool(const std::string& property) const;
     void SetProperty(const std::string& property, const std::string& value);
     void SetProperty(const std::string& property, bool value);
-    void SetProperty(const std::string& property, std::nullptr_t);
+    void RemoveProperty(const std::string& property);
     void AppendProperty(const std::string& property, const std::string& value,
                         bool asString = false);
 
@@ -144,7 +144,7 @@
                                 std::string const& propName)
   {
     if (auto* entry = this->GetCacheEntry(key)) {
-      entry->SetProperty(propName, nullptr);
+      entry->RemoveProperty(propName);
     }
   }
 
diff --git a/Source/cmCallVisualStudioMacro.cxx b/Source/cmCallVisualStudioMacro.cxx
index 94b6e18..292b9a3 100644
--- a/Source/cmCallVisualStudioMacro.cxx
+++ b/Source/cmCallVisualStudioMacro.cxx
@@ -4,6 +4,8 @@
 
 #include <sstream>
 
+#include <cmext/string_view>
+
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
@@ -305,7 +307,7 @@
 //! we perhaps looking for any and all solutions?
 bool FilesSameSolution(const std::string& slnFile, const std::string& slnName)
 {
-  if (slnFile == "ALL" || slnName == "ALL") {
+  if (slnFile == "ALL"_s || slnName == "ALL"_s) {
     return true;
   }
 
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx
index ae83b2e..2ee4f47 100644
--- a/Source/cmCommands.cxx
+++ b/Source/cmCommands.cxx
@@ -135,7 +135,6 @@
   state->AddBuiltinCommand("cmake_path", cmCMakePathCommand);
   state->AddBuiltinCommand("cmake_policy", cmCMakePolicyCommand);
   state->AddBuiltinCommand("configure_file", cmConfigureFileCommand);
-  state->AddBuiltinCommand("exec_program", cmExecProgramCommand);
   state->AddBuiltinCommand("execute_process", cmExecuteProcessCommand);
   state->AddBuiltinCommand("file", cmFileCommand);
   state->AddBuiltinCommand("find_file", cmFindFile);
@@ -220,6 +219,9 @@
   state->AddDisallowedCommand(
     "use_mangled_mesa", cmUseMangledMesaCommand, cmPolicies::CMP0030,
     "The use_mangled_mesa command should not be called; see CMP0030.");
+  state->AddDisallowedCommand(
+    "exec_program", cmExecProgramCommand, cmPolicies::CMP0153,
+    "The exec_program command should not be called; see CMP0153.");
 
 #endif
 }
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index c781137..b13576a 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -4,6 +4,7 @@
 
 #include <algorithm>
 #include <sstream>
+#include <type_traits>
 #include <utility>
 
 #include <cm/string_view>
@@ -172,13 +173,7 @@
         this->GeneratorTarget->GetLinkInformation(config)) {
     std::vector<cmGeneratorTarget const*> targets;
     for (auto const& item : cli->GetItems()) {
-      targets.push_back(item.Target);
-    }
-    for (auto const* target : cli->GetObjectLibrariesLinked()) {
-      targets.push_back(target);
-    }
-
-    for (auto const* linkee : targets) {
+      auto const* linkee = item.Target;
       if (linkee &&
           !linkee->IsImported()
           // Skip targets that build after this one in a static lib cycle.
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index f51a1c8..7394d86 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -8,6 +8,7 @@
 #include <cstdio>
 #include <iterator>
 #include <sstream>
+#include <type_traits>
 #include <unordered_map>
 #include <utility>
 
diff --git a/Source/cmComputeLinkInformation.cxx b/Source/cmComputeLinkInformation.cxx
index 7d3675e..4cf3042 100644
--- a/Source/cmComputeLinkInformation.cxx
+++ b/Source/cmComputeLinkInformation.cxx
@@ -29,6 +29,7 @@
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmValue.h"
+#include "cmXcFramework.h"
 #include "cmake.h"
 
 // #define CM_COMPUTE_LINK_INFO_DEBUG
@@ -373,6 +374,10 @@
   this->LibraryFeatureDescriptors.emplace(
     "__CMAKE_LINK_FRAMEWORK",
     LibraryFeatureDescriptor{ "__CMAKE_LINK_FRAMEWORK", "<LIBRARY>" });
+  // To link xcframework using a full path
+  this->LibraryFeatureDescriptors.emplace(
+    "__CMAKE_LINK_XCFRAMEWORK",
+    LibraryFeatureDescriptor{ "__CMAKE_LINK_XCFRAMEWORK", "<LIBRARY>" });
 
   // Check the platform policy for missing soname case.
   this->NoSONameUsesPath =
@@ -519,18 +524,18 @@
   return this->FrameworkPathsEmitted;
 }
 
+std::vector<std::string> const&
+cmComputeLinkInformation::GetXcFrameworkHeaderPaths() const
+{
+  return this->XcFrameworkHeaderPaths;
+}
+
 const std::set<const cmGeneratorTarget*>&
 cmComputeLinkInformation::GetSharedLibrariesLinked() const
 {
   return this->SharedLibrariesLinked;
 }
 
-const std::vector<const cmGeneratorTarget*>&
-cmComputeLinkInformation::GetObjectLibrariesLinked() const
-{
-  return this->ObjectLibrariesLinked;
-}
-
 bool cmComputeLinkInformation::Compute()
 {
   // Skip targets that do not link or have link-like information consumers may
@@ -1157,12 +1162,14 @@
         this->AddItem(BT<std::string>(libName, item.Backtrace));
       }
     } else if (tgt->GetType() == cmStateEnums::OBJECT_LIBRARY) {
-      if (!tgt->HaveCxx20ModuleSources() && !tgt->HaveFortranSources(config)) {
-        // Ignore object library!
-        // Its object-files should already have been extracted for linking.
-      } else {
-        this->ObjectLibrariesLinked.push_back(entry.Target);
-      }
+      this->Items.emplace_back(entry.Item, ItemIsPath::No, entry.Target);
+    } else if (this->GlobalGenerator->IsXcode() &&
+               !tgt->GetImportedXcFrameworkPath(config).empty()) {
+      this->Items.emplace_back(
+        tgt->GetImportedXcFrameworkPath(config), ItemIsPath::Yes, tgt,
+        this->FindLibraryFeature(entry.Feature == DEFAULT
+                                   ? "__CMAKE_LINK_XCFRAMEWORK"
+                                   : entry.Feature));
     } else {
       // Decide whether to use an import library.
       cmStateEnums::ArtifactType artifact = tgt->HasImportLibrary(config)
@@ -1202,6 +1209,25 @@
         this->AddRuntimeDLL(tgt);
       }
     }
+
+    auto xcFrameworkPath = tgt->GetImportedXcFrameworkPath(config);
+    if (!xcFrameworkPath.empty()) {
+      auto plist = cmParseXcFrameworkPlist(xcFrameworkPath, *this->Makefile,
+                                           item.Backtrace);
+      if (!plist) {
+        return;
+      }
+      if (auto const* library =
+            plist->SelectSuitableLibrary(*this->Makefile, item.Backtrace)) {
+        if (!library->HeadersPath.empty()) {
+          this->AddXcFrameworkHeaderPath(cmStrCat(xcFrameworkPath, '/',
+                                                  library->LibraryIdentifier,
+                                                  '/', library->HeadersPath));
+        }
+      } else {
+        return;
+      }
+    }
   } else {
     // This is not a CMake target.  Use the name given.
     if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s) ||
@@ -1210,6 +1236,12 @@
          this->Target->IsApple())) {
       // This is a framework.
       this->AddFrameworkItem(entry);
+    } else if (cmHasSuffix(entry.Feature, "XCFRAMEWORK"_s) ||
+               (entry.Feature == DEFAULT &&
+                cmSystemTools::IsPathToXcFramework(item.Value) &&
+                this->Target->IsApple())) {
+      // This is a framework.
+      this->AddXcFrameworkItem(entry);
     } else if (cmSystemTools::FileIsFullPath(item.Value)) {
       if (cmSystemTools::FileIsDirectory(item.Value)) {
         // This is a directory.
@@ -1593,7 +1625,9 @@
     this->OldLinkDirItems.push_back(item.Value);
   }
 
-  if (target->IsFrameworkOnApple()) {
+  const bool isImportedFrameworkFolderOnApple =
+    target->IsImportedFrameworkFolderOnApple(this->Config);
+  if (target->IsFrameworkOnApple() || isImportedFrameworkFolderOnApple) {
     // Add the framework directory and the framework item itself
     auto fwDescriptor = this->GlobalGenerator->SplitFrameworkPath(
       item.Value, cmGlobalGenerator::FrameworkFormat::Extended);
@@ -1611,16 +1645,33 @@
     }
 
     if (this->GlobalGenerator->IsXcode()) {
-      this->Items.emplace_back(
-        item, ItemIsPath::Yes, target,
-        this->FindLibraryFeature(entry.Feature == DEFAULT
-                                   ? "__CMAKE_LINK_FRAMEWORK"
-                                   : entry.Feature));
+      if (isImportedFrameworkFolderOnApple) {
+        if (entry.Feature == DEFAULT) {
+          this->AddLibraryFeature("FRAMEWORK");
+          this->Items.emplace_back(item, ItemIsPath::Yes, target,
+                                   this->FindLibraryFeature("FRAMEWORK"));
+        } else {
+          this->Items.emplace_back(item, ItemIsPath::Yes, target,
+                                   this->FindLibraryFeature(entry.Feature));
+        }
+      } else {
+        this->Items.emplace_back(
+          item, ItemIsPath::Yes, target,
+          this->FindLibraryFeature(entry.Feature == DEFAULT
+                                     ? "__CMAKE_LINK_FRAMEWORK"
+                                     : entry.Feature));
+      }
     } else {
       if (cmHasSuffix(entry.Feature, "FRAMEWORK"_s)) {
         this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes,
                                  target,
                                  this->FindLibraryFeature(entry.Feature));
+      } else if (entry.Feature == DEFAULT &&
+                 isImportedFrameworkFolderOnApple) {
+        this->AddLibraryFeature("FRAMEWORK");
+        this->Items.emplace_back(fwDescriptor->GetLinkName(), ItemIsPath::Yes,
+                                 target,
+                                 this->FindLibraryFeature("FRAMEWORK"));
       } else {
         this->Items.emplace_back(
           item, ItemIsPath::Yes, target,
@@ -1930,6 +1981,46 @@
   }
 }
 
+void cmComputeLinkInformation::AddXcFrameworkItem(LinkEntry const& entry)
+{
+  auto plist = cmParseXcFrameworkPlist(entry.Item.Value, *this->Makefile,
+                                       entry.Item.Backtrace);
+  if (!plist) {
+    return;
+  }
+
+  if (auto const* lib =
+        plist->SelectSuitableLibrary(*this->Makefile, entry.Item.Backtrace)) {
+    if (this->GlobalGenerator->IsXcode()) {
+      this->Items.emplace_back(
+        entry.Item.Value, ItemIsPath::Yes, nullptr,
+        this->FindLibraryFeature(entry.Feature == DEFAULT
+                                   ? "__CMAKE_LINK_XCFRAMEWORK"
+                                   : entry.Feature));
+    } else {
+      auto libraryPath = cmStrCat(
+        entry.Item.Value, '/', lib->LibraryIdentifier, '/', lib->LibraryPath);
+      LinkEntry libraryEntry(
+        BT<std::string>(libraryPath, entry.Item.Backtrace), entry.Target);
+
+      if (cmSystemTools::IsPathToFramework(libraryPath) &&
+          this->Target->IsApple()) {
+        // This is a framework.
+        this->AddFrameworkItem(libraryEntry);
+      } else {
+        this->Depends.push_back(libraryPath);
+        this->AddFullItem(libraryEntry);
+        this->AddLibraryRuntimeInfo(libraryPath);
+        if (!lib->HeadersPath.empty()) {
+          this->AddXcFrameworkHeaderPath(cmStrCat(entry.Item.Value, '/',
+                                                  lib->LibraryIdentifier, '/',
+                                                  lib->HeadersPath));
+        }
+      }
+    }
+  }
+}
+
 void cmComputeLinkInformation::DropDirectoryItem(BT<std::string> const& item)
 {
   // A full path to a directory was found as a link item.  Warn the
@@ -1967,6 +2058,11 @@
   }
 }
 
+void cmComputeLinkInformation::AddXcFrameworkHeaderPath(std::string const& p)
+{
+  this->XcFrameworkHeaderPaths.push_back(p);
+}
+
 bool cmComputeLinkInformation::CheckSharedLibNoSOName(LinkEntry const& entry)
 {
   // This platform will use the path to a library as its soname if the
@@ -2236,16 +2332,20 @@
   if (target->GetType() != cmStateEnums::SHARED_LIBRARY) {
     return;
   }
+  auto const* info = target->GetImportInfo(this->Config);
 
   // Try to get the soname of the library.  Only files with this name
   // could possibly conflict.
-  std::string soName = target->GetSOName(this->Config);
-  const char* soname = soName.empty() ? nullptr : soName.c_str();
+  const char* soname =
+    (!info || info->SOName.empty()) ? nullptr : info->SOName.c_str();
 
-  // Include this library in the runtime path ordering.
-  this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname);
-  if (this->LinkWithRuntimePath) {
-    this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname);
+  // If this shared library has a known runtime artifact (IMPORTED_LOCATION),
+  // include its location in the runtime path ordering.
+  if (!info || !info->Location.empty()) {
+    this->OrderRuntimeSearchPath->AddRuntimeLibrary(fullPath, soname);
+    if (this->LinkWithRuntimePath) {
+      this->OrderLinkerSearchPath->AddRuntimeLibrary(fullPath, soname);
+    }
   }
 }
 
diff --git a/Source/cmComputeLinkInformation.h b/Source/cmComputeLinkInformation.h
index 8393a29..a988307 100644
--- a/Source/cmComputeLinkInformation.h
+++ b/Source/cmComputeLinkInformation.h
@@ -88,6 +88,7 @@
   std::vector<std::string> const& GetDepends() const;
   std::vector<std::string> const& GetFrameworkPaths() const;
   std::set<std::string> const& GetFrameworkPathsEmitted() const;
+  std::vector<std::string> const& GetXcFrameworkHeaderPaths() const;
   std::string GetLinkLanguage() const { return this->LinkLanguage; }
   std::vector<std::string> const& GetRuntimeSearchPath() const;
   std::string const& GetRuntimeFlag() const { return this->RuntimeFlag; }
@@ -96,8 +97,6 @@
   std::string GetRPathString(bool for_install) const;
   std::string GetChrpathString() const;
   std::set<cmGeneratorTarget const*> const& GetSharedLibrariesLinked() const;
-  std::vector<cmGeneratorTarget const*> const& GetObjectLibrariesLinked()
-    const;
   std::vector<cmGeneratorTarget const*> const& GetRuntimeDLLs() const
   {
     return this->RuntimeDLLs;
@@ -132,9 +131,9 @@
   std::vector<std::string> Directories;
   std::vector<std::string> Depends;
   std::vector<std::string> FrameworkPaths;
+  std::vector<std::string> XcFrameworkHeaderPaths;
   std::vector<std::string> RuntimeSearchPath;
   std::set<cmGeneratorTarget const*> SharedLibrariesLinked;
-  std::vector<cmGeneratorTarget const*> ObjectLibrariesLinked;
   std::vector<cmGeneratorTarget const*> RuntimeDLLs;
 
   // Context information.
@@ -204,6 +203,7 @@
   bool CheckImplicitDirItem(LinkEntry const& entry);
   void AddUserItem(LinkEntry const& entry, bool pathNotKnown);
   void AddFrameworkItem(LinkEntry const& entry);
+  void AddXcFrameworkItem(LinkEntry const& entry);
   void DropDirectoryItem(BT<std::string> const& item);
   bool CheckSharedLibNoSOName(LinkEntry const& entry);
   void AddSharedLibNoSOName(LinkEntry const& entry);
@@ -214,6 +214,8 @@
   void AddFrameworkPath(std::string const& p);
   std::set<std::string> FrameworkPathsEmitted;
 
+  void AddXcFrameworkHeaderPath(std::string const& p);
+
   // Linker search path computation.
   std::unique_ptr<cmOrderDirectories> OrderLinkerSearchPath;
   bool FinishLinkerSearchDirectories();
diff --git a/Source/cmConditionEvaluator.h b/Source/cmConditionEvaluator.h
index 9486b16..4f42206 100644
--- a/Source/cmConditionEvaluator.h
+++ b/Source/cmConditionEvaluator.h
@@ -10,7 +10,7 @@
 #include <cmext/string_view>
 
 #include "cmListFileCache.h"
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 #include "cmPolicies.h"
 #include "cmValue.h"
 
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index 7d4ab50..7e19812 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -7,7 +7,6 @@
 #include <cstring>
 #include <set>
 #include <sstream>
-#include <type_traits>
 #include <utility>
 
 #include <cm/string_view>
@@ -18,6 +17,7 @@
 
 #include "cmArgumentParser.h"
 #include "cmConfigureLog.h"
+#include "cmExperimental.h"
 #include "cmExportTryCompileFileGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmList.h"
@@ -72,12 +72,14 @@
 
 std::string const kCMAKE_CUDA_ARCHITECTURES = "CMAKE_CUDA_ARCHITECTURES";
 std::string const kCMAKE_CUDA_RUNTIME_LIBRARY = "CMAKE_CUDA_RUNTIME_LIBRARY";
+std::string const kCMAKE_CXX_SCAN_FOR_MODULES = "CMAKE_CXX_SCAN_FOR_MODULES";
 std::string const kCMAKE_ENABLE_EXPORTS = "CMAKE_ENABLE_EXPORTS";
 std::string const kCMAKE_EXECUTABLE_ENABLE_EXPORTS =
   "CMAKE_EXECUTABLE_ENABLE_EXPORTS";
 std::string const kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS =
   "CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS";
 std::string const kCMAKE_HIP_ARCHITECTURES = "CMAKE_HIP_ARCHITECTURES";
+std::string const kCMAKE_HIP_PLATFORM = "CMAKE_HIP_PLATFORM";
 std::string const kCMAKE_HIP_RUNTIME_LIBRARY = "CMAKE_HIP_RUNTIME_LIBRARY";
 std::string const kCMAKE_ISPC_INSTRUCTION_SETS = "CMAKE_ISPC_INSTRUCTION_SETS";
 std::string const kCMAKE_ISPC_HEADER_SUFFIX = "CMAKE_ISPC_HEADER_SUFFIX";
@@ -167,7 +169,9 @@
 
 auto const TryCompileBaseSourcesArgParser =
   cmArgumentParser<Arguments>{ TryCompileBaseArgParser }
-    .Bind("SOURCES"_s, &Arguments::Sources)
+    .Bind("SOURCES_TYPE"_s, &Arguments::SetSourceType)
+    .BindWithContext("SOURCES"_s, &Arguments::Sources,
+                     &Arguments::SourceTypeContext)
     .Bind("COMPILE_DEFINITIONS"_s, TryCompileCompileDefs,
           ArgumentParser::ExpectAtLeast{ 0 })
     .Bind("LINK_LIBRARIES"_s, &Arguments::LinkLibraries)
@@ -184,9 +188,12 @@
 
 auto const TryCompileBaseNewSourcesArgParser =
   cmArgumentParser<Arguments>{ TryCompileBaseSourcesArgParser }
-    .Bind("SOURCE_FROM_CONTENT"_s, &Arguments::SourceFromContent)
-    .Bind("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar)
-    .Bind("SOURCE_FROM_FILE"_s, &Arguments::SourceFromFile)
+    .BindWithContext("SOURCE_FROM_CONTENT"_s, &Arguments::SourceFromContent,
+                     &Arguments::SourceTypeContext)
+    .BindWithContext("SOURCE_FROM_VAR"_s, &Arguments::SourceFromVar,
+                     &Arguments::SourceTypeContext)
+    .BindWithContext("SOURCE_FROM_FILE"_s, &Arguments::SourceFromFile,
+                     &Arguments::SourceTypeContext)
   /* keep semicolon on own line */;
 
 auto const TryCompileBaseProjectArgParser =
@@ -221,17 +228,40 @@
 std::string const TryCompileDefaultConfig = "DEBUG";
 }
 
+ArgumentParser::Continue cmCoreTryCompile::Arguments::SetSourceType(
+  cm::string_view sourceType)
+{
+  bool matched = false;
+  if (sourceType == "NORMAL"_s) {
+    this->SourceTypeContext = SourceType::Normal;
+    matched = true;
+  } else if (sourceType == "CXX_MODULE"_s) {
+    this->SourceTypeContext = SourceType::CxxModule;
+    matched = true;
+  }
+
+  if (!matched && this->SourceTypeError.empty()) {
+    // Only remember one error at a time; all other errors related to argument
+    // parsing are "indicate one error and return" anyways.
+    this->SourceTypeError =
+      cmStrCat("Invalid 'SOURCE_TYPE' '", sourceType,
+               "'; must be one of 'SOURCE' or 'CXX_MODULE'");
+  }
+  return ArgumentParser::Continue::Yes;
+}
+
 Arguments cmCoreTryCompile::ParseArgs(
   const cmRange<std::vector<std::string>::const_iterator>& args,
   const cmArgumentParser<Arguments>& parser,
   std::vector<std::string>& unparsedArguments)
 {
-  auto arguments = parser.Parse(args, &unparsedArguments, 0);
+  Arguments arguments{ this->Makefile };
+  parser.Parse(arguments, args, &unparsedArguments, 0);
   if (!arguments.MaybeReportError(*(this->Makefile)) &&
       !unparsedArguments.empty()) {
     std::string m = "Unknown arguments:";
     for (const auto& i : unparsedArguments) {
-      m = cmStrCat(m, "\n  \"", i, "\"");
+      m = cmStrCat(m, "\n  \"", i, '"');
     }
     this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING, m);
   }
@@ -345,7 +375,7 @@
       this->Makefile->IssueMessage(
         MessageType::FATAL_ERROR,
         cmStrCat("<bindir> is not an absolute path:\n '",
-                 *arguments.BinaryDirectory, "'"));
+                 *arguments.BinaryDirectory, '\''));
       return cm::nullopt;
     }
     this->BinaryDirectory = *arguments.BinaryDirectory;
@@ -377,7 +407,7 @@
               cmStrCat("Only libraries may be used as try_compile or try_run "
                        "IMPORTED LINK_LIBRARIES.  Got ",
                        tgt->GetName(), " of type ",
-                       cmState::GetTargetTypeName(tgt->GetType()), "."));
+                       cmState::GetTargetTypeName(tgt->GetType()), '.'));
             return cm::nullopt;
         }
         if (tgt->IsImported()) {
@@ -434,6 +464,11 @@
         "SOURCE_FROM_FILE requires exactly two arguments");
       return cm::nullopt;
     }
+    if (!arguments.SourceTypeError.empty()) {
+      this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+                                   arguments.SourceTypeError);
+      return cm::nullopt;
+    }
   } else {
     // only valid for srcfile signatures
     if (!arguments.LangProps.empty()) {
@@ -478,59 +513,62 @@
 
   std::map<std::string, std::string> cmakeVariables;
 
-  std::string outFileName = this->BinaryDirectory + "/CMakeLists.txt";
+  std::string outFileName = cmStrCat(this->BinaryDirectory, "/CMakeLists.txt");
   // which signature are we using? If we are using var srcfile bindir
   if (this->SrcFileSignature) {
     // remove any CMakeCache.txt files so we will have a clean test
-    std::string ccFile = this->BinaryDirectory + "/CMakeCache.txt";
+    std::string ccFile = cmStrCat(this->BinaryDirectory, "/CMakeCache.txt");
     cmSystemTools::RemoveFile(ccFile);
 
     // Choose sources.
-    std::vector<std::string> sources;
+    std::vector<std::pair<std::string, Arguments::SourceType>> sources;
     if (arguments.Sources) {
       sources = std::move(*arguments.Sources);
     } else if (arguments.SourceDirectoryOrFile) {
-      sources.emplace_back(*arguments.SourceDirectoryOrFile);
+      sources.emplace_back(*arguments.SourceDirectoryOrFile,
+                           Arguments::SourceType::Directory);
     }
     if (arguments.SourceFromContent) {
       auto const k = arguments.SourceFromContent->size();
       for (auto i = decltype(k){ 0 }; i < k; i += 2) {
-        const auto& name = (*arguments.SourceFromContent)[i + 0];
-        const auto& content = (*arguments.SourceFromContent)[i + 1];
+        const auto& name = (*arguments.SourceFromContent)[i + 0].first;
+        const auto& content = (*arguments.SourceFromContent)[i + 1].first;
         auto out = this->WriteSource(name, content, "SOURCE_FROM_CONTENT");
         if (out.empty()) {
           return cm::nullopt;
         }
-        sources.emplace_back(std::move(out));
+        sources.emplace_back(std::move(out),
+                             (*arguments.SourceFromContent)[i + 0].second);
       }
     }
     if (arguments.SourceFromVar) {
       auto const k = arguments.SourceFromVar->size();
       for (auto i = decltype(k){ 0 }; i < k; i += 2) {
-        const auto& name = (*arguments.SourceFromVar)[i + 0];
-        const auto& var = (*arguments.SourceFromVar)[i + 1];
+        const auto& name = (*arguments.SourceFromVar)[i + 0].first;
+        const auto& var = (*arguments.SourceFromVar)[i + 1].first;
         const auto& content = this->Makefile->GetDefinition(var);
         auto out = this->WriteSource(name, content, "SOURCE_FROM_VAR");
         if (out.empty()) {
           return cm::nullopt;
         }
-        sources.emplace_back(std::move(out));
+        sources.emplace_back(std::move(out),
+                             (*arguments.SourceFromVar)[i + 0].second);
       }
     }
     if (arguments.SourceFromFile) {
       auto const k = arguments.SourceFromFile->size();
       for (auto i = decltype(k){ 0 }; i < k; i += 2) {
-        const auto& dst = (*arguments.SourceFromFile)[i + 0];
-        const auto& src = (*arguments.SourceFromFile)[i + 1];
+        const auto& dst = (*arguments.SourceFromFile)[i + 0].first;
+        const auto& src = (*arguments.SourceFromFile)[i + 1].first;
 
         if (!cmSystemTools::GetFilenamePath(dst).empty()) {
           const auto& msg =
-            cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, "\"");
+            cmStrCat("SOURCE_FROM_FILE given invalid filename \"", dst, '"');
           this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
           return cm::nullopt;
         }
 
-        auto dstPath = cmStrCat(this->BinaryDirectory, "/", dst);
+        auto dstPath = cmStrCat(this->BinaryDirectory, '/', dst);
         auto const result = cmSystemTools::CopyFileAlways(src, dstPath);
         if (!result.IsSuccess()) {
           const auto& msg = cmStrCat("SOURCE_FROM_FILE failed to copy \"", src,
@@ -539,7 +577,8 @@
           return cm::nullopt;
         }
 
-        sources.emplace_back(std::move(dstPath));
+        sources.emplace_back(std::move(dstPath),
+                             (*arguments.SourceFromFile)[i + 0].second);
       }
     }
     // TODO: ensure sources is not empty
@@ -547,17 +586,21 @@
     // Detect languages to enable.
     cmGlobalGenerator* gg = this->Makefile->GetGlobalGenerator();
     std::set<std::string> testLangs;
-    for (std::string const& si : sources) {
+    for (auto const& source : sources) {
+      auto const& si = source.first;
       std::string ext = cmSystemTools::GetFilenameLastExtension(si);
       std::string lang = gg->GetLanguageFromExtension(ext.c_str());
       if (!lang.empty()) {
         testLangs.insert(lang);
       } else {
         std::ostringstream err;
-        err << "Unknown extension \"" << ext << "\" for file\n"
-            << "  " << si << "\n"
-            << "try_compile() works only for enabled languages.  "
-            << "Currently these are:\n  ";
+        err << "Unknown extension \"" << ext
+            << "\" for file\n"
+               "  "
+            << si
+            << "\n"
+               "try_compile() works only for enabled languages.  "
+               "Currently these are:\n  ";
         std::vector<std::string> langs;
         gg->GetEnabledLanguages(langs);
         err << cmJoin(langs, " ");
@@ -586,7 +629,7 @@
       std::ostringstream e;
       /* clang-format off */
       e << "Failed to open\n"
-        << "  " << outFileName << "\n"
+           "  " << outFileName << "\n"
         << cmSystemTools::GetLastSystemError();
       /* clang-format on */
       this->Makefile->IssueMessage(MessageType::FATAL_ERROR, e.str());
@@ -654,9 +697,9 @@
 
     std::string projectLangs;
     for (std::string const& li : testLangs) {
-      projectLangs += " " + li;
+      projectLangs += cmStrCat(' ', li);
       std::string rulesOverrideBase = "CMAKE_USER_MAKE_RULES_OVERRIDE";
-      std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, "_", li);
+      std::string rulesOverrideLang = cmStrCat(rulesOverrideBase, '_', li);
       if (cmValue rulesOverridePath =
             this->Makefile->GetDefinition(rulesOverrideLang)) {
         fprintf(fout, "set(%s \"%s\")\n", rulesOverrideLang.c_str(),
@@ -681,15 +724,21 @@
       // The link and compile lines for ABI detection step need to not use
       // response files so we can extract implicit includes given to
       // the underlying host compiler
-      if (testLangs.find("CUDA") != testLangs.end()) {
-        fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n");
-        fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n");
-        fprintf(fout, "set(CMAKE_CUDA_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n");
+      static std::array<std::string, 2> const noRSP{ { "CUDA", "HIP" } };
+      for (std::string const& lang : noRSP) {
+        if (testLangs.find(lang) != testLangs.end()) {
+          fprintf(fout, "set(CMAKE_%s_USE_RESPONSE_FILE_FOR_INCLUDES OFF)\n",
+                  lang.c_str());
+          fprintf(fout, "set(CMAKE_%s_USE_RESPONSE_FILE_FOR_LIBRARIES OFF)\n",
+                  lang.c_str());
+          fprintf(fout, "set(CMAKE_%s_USE_RESPONSE_FILE_FOR_OBJECTS OFF)\n",
+                  lang.c_str());
+        }
       }
     }
     fprintf(fout, "set(CMAKE_VERBOSE_MAKEFILE 1)\n");
     for (std::string const& li : testLangs) {
-      std::string langFlags = "CMAKE_" + li + "_FLAGS";
+      std::string langFlags = cmStrCat("CMAKE_", li, "_FLAGS");
       cmValue flags = this->Makefile->GetDefinition(langFlags);
       fprintf(fout, "set(CMAKE_%s_FLAGS %s)\n", li.c_str(),
               cmOutputConverter::EscapeForCMake(*flags).c_str());
@@ -793,10 +842,10 @@
     }
 
     if (!targets.empty()) {
-      std::string fname = "/" + std::string(targetName) + "Targets.cmake";
+      std::string fname = cmStrCat('/', targetName, "Targets.cmake");
       cmExportTryCompileFileGenerator tcfg(gg, targets, this->Makefile,
                                            testLangs);
-      tcfg.SetExportFile((this->BinaryDirectory + fname).c_str());
+      tcfg.SetExportFile(cmStrCat(this->BinaryDirectory, fname).c_str());
       tcfg.SetConfig(tcConfig);
 
       if (!tcfg.GenerateImportFile()) {
@@ -823,6 +872,13 @@
               ? "NEW"
               : "OLD");
 
+    /* Set the appropriate policy information for C++ module support */
+    fprintf(fout, "cmake_policy(SET CMP0155 %s)\n",
+            this->Makefile->GetPolicyStatus(cmPolicies::CMP0155) ==
+                cmPolicies::NEW
+              ? "NEW"
+              : "OLD");
+
     // Workaround for -Wl,-headerpad_max_install_names issue until we can avoid
     // adding that flag in the platform and compiler language files
     fprintf(fout,
@@ -834,17 +890,43 @@
       fprintf(fout, "set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"%s\")\n",
               this->BinaryDirectory.c_str());
       /* Create the actual executable.  */
-      fprintf(fout, "add_executable(%s", targetName.c_str());
+      fprintf(fout, "add_executable(%s)\n", targetName.c_str());
     } else // if (targetType == cmStateEnums::STATIC_LIBRARY)
     {
       /* Put the static library at a known location (for COPY_FILE).  */
       fprintf(fout, "set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"%s\")\n",
               this->BinaryDirectory.c_str());
       /* Create the actual static library.  */
-      fprintf(fout, "add_library(%s STATIC", targetName.c_str());
+      fprintf(fout, "add_library(%s STATIC)\n", targetName.c_str());
     }
-    for (std::string const& si : sources) {
-      fprintf(fout, " \"%s\"", si.c_str());
+    fprintf(fout, "target_sources(%s PRIVATE\n", targetName.c_str());
+    std::string file_set_name;
+    bool in_file_set = false;
+    for (auto const& source : sources) {
+      auto const& si = source.first;
+      switch (source.second) {
+        case Arguments::SourceType::Normal: {
+          if (in_file_set) {
+            fprintf(fout, "  PRIVATE\n");
+            in_file_set = false;
+          }
+        } break;
+        case Arguments::SourceType::CxxModule: {
+          if (!in_file_set) {
+            file_set_name += 'a';
+            fprintf(fout,
+                    "  PRIVATE FILE_SET %s TYPE CXX_MODULES BASE_DIRS \"%s\" "
+                    "FILES\n",
+                    file_set_name.c_str(),
+                    this->Makefile->GetCurrentSourceDirectory().c_str());
+            in_file_set = true;
+          }
+        } break;
+        case Arguments::SourceType::Directory:
+          /* Handled elsewhere. */
+          break;
+      }
+      fprintf(fout, "  \"%s\"\n", si.c_str());
 
       // Add dependencies on any non-temporary sources.
       if (!IsTemporary(si)) {
@@ -964,7 +1046,7 @@
     if (arguments.LinkLibraries) {
       std::string libsToLink = " ";
       for (std::string const& i : *arguments.LinkLibraries) {
-        libsToLink += "\"" + cmTrimWhitespace(i) + "\" ";
+        libsToLink += cmStrCat('"', cmTrimWhitespace(i), "\" ");
       }
       fprintf(fout, "target_link_libraries(%s %s)\n", targetName.c_str(),
               libsToLink.c_str());
@@ -1001,10 +1083,12 @@
                 &swift_properties[lang_property_start + lang_property_size]);
     vars.insert(kCMAKE_CUDA_ARCHITECTURES);
     vars.insert(kCMAKE_CUDA_RUNTIME_LIBRARY);
+    vars.insert(kCMAKE_CXX_SCAN_FOR_MODULES);
     vars.insert(kCMAKE_ENABLE_EXPORTS);
     vars.insert(kCMAKE_EXECUTABLE_ENABLE_EXPORTS);
     vars.insert(kCMAKE_SHARED_LIBRARY_ENABLE_EXPORTS);
     vars.insert(kCMAKE_HIP_ARCHITECTURES);
+    vars.insert(kCMAKE_HIP_PLATFORM);
     vars.insert(kCMAKE_HIP_RUNTIME_LIBRARY);
     vars.insert(kCMAKE_ISPC_INSTRUCTION_SETS);
     vars.insert(kCMAKE_ISPC_HEADER_SUFFIX);
@@ -1022,6 +1106,7 @@
     vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
     vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
     vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
+    vars.emplace("CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS"_s);
 
     if (cmValue varListStr = this->Makefile->GetDefinition(
           kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
@@ -1063,14 +1148,33 @@
     if (cmValue tcArchs = this->Makefile->GetDefinition(
           kCMAKE_TRY_COMPILE_OSX_ARCHITECTURES)) {
       vars.erase(kCMAKE_OSX_ARCHITECTURES);
-      std::string flag = "-DCMAKE_OSX_ARCHITECTURES=" + *tcArchs;
+      std::string flag = cmStrCat("-DCMAKE_OSX_ARCHITECTURES=", *tcArchs);
       arguments.CMakeFlags.emplace_back(std::move(flag));
       cmakeVariables.emplace("CMAKE_OSX_ARCHITECTURES", *tcArchs);
     }
 
+    // Pass down CMAKE_EXPERIMENTAL_* feature flags
+    for (std::size_t i = 0;
+         i < static_cast<std::size_t>(cmExperimental::Feature::Sentinel);
+         i++) {
+      auto const& data = cmExperimental::DataForFeature(
+        static_cast<cmExperimental::Feature>(i));
+      if (data.ForwardThroughTryCompile ==
+            cmExperimental::TryCompileCondition::Always ||
+          (data.ForwardThroughTryCompile ==
+             cmExperimental::TryCompileCondition::SkipCompilerChecks &&
+           arguments.CMakeInternal != "ABI"_s &&
+           arguments.CMakeInternal != "FEATURE_TESTING"_s)) {
+        vars.insert(data.Variable);
+        for (auto const& var : data.TryCompileVariables) {
+          vars.insert(var);
+        }
+      }
+    }
+
     for (std::string const& var : vars) {
       if (cmValue val = this->Makefile->GetDefinition(var)) {
-        std::string flag = "-D" + var + "=" + *val;
+        std::string flag = cmStrCat("-D", var, '=', *val);
         arguments.CMakeFlags.emplace_back(std::move(flag));
         cmakeVariables.emplace(var, *val);
       }
@@ -1081,7 +1185,7 @@
     // Forward the GHS variables to the inner project cache.
     for (std::string const& var : ghs_platform_vars) {
       if (cmValue val = this->Makefile->GetDefinition(var)) {
-        std::string flag = "-D" + var + "=" + "'" + *val + "'";
+        std::string flag = cmStrCat("-D", var, "=\'", *val, '\'');
         arguments.CMakeFlags.emplace_back(std::move(flag));
         cmakeVariables.emplace(var, *val);
       }
@@ -1143,11 +1247,11 @@
         }
         /* clang-format off */
         err = cmStrCat(
-          "Cannot copy output executable\n",
-          "  '", this->OutputFile, "'\n",
-          "to destination specified by COPY_FILE:\n",
-          "  '", copyFile, "'\n",
-          "because:\n",
+          "Cannot copy output executable\n"
+          "  '", this->OutputFile, "'\n"
+          "to destination specified by COPY_FILE:\n"
+          "  '", copyFile, "'\n"
+          "because:\n"
           "  ", err, "\n",
           this->FindErrorMessage);
         /* clang-format on */
@@ -1192,10 +1296,10 @@
   }
 
   if (!IsTemporary(binDir)) {
-    cmSystemTools::Error(
+    cmSystemTools::Error(cmStrCat(
       "TRY_COMPILE attempt to remove -rf directory that does not contain "
-      "CMakeTmp or CMakeScratch: \"" +
-      binDir + "\"");
+      "CMakeTmp or CMakeScratch: \"",
+      binDir, '"'));
     return;
   }
 
@@ -1208,8 +1312,7 @@
         // Do not delete NFS temporary files.
         !cmHasPrefix(fileName, ".nfs")) {
       if (deletedFiles.insert(fileName).second) {
-        std::string const fullPath =
-          std::string(binDir).append("/").append(fileName);
+        std::string const fullPath = cmStrCat(binDir, '/', fileName);
         if (cmSystemTools::FileIsSymlink(fullPath)) {
           cmSystemTools::RemoveFile(fullPath);
         } else if (cmSystemTools::FileIsDirectory(fullPath)) {
@@ -1293,12 +1396,12 @@
 {
   if (!cmSystemTools::GetFilenamePath(filename).empty()) {
     const auto& msg =
-      cmStrCat(command, " given invalid filename \"", filename, "\"");
+      cmStrCat(command, " given invalid filename \"", filename, '"');
     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
     return {};
   }
 
-  auto filepath = cmStrCat(this->BinaryDirectory, "/", filename);
+  auto filepath = cmStrCat(this->BinaryDirectory, '/', filename);
   cmsys::ofstream file{ filepath.c_str(), std::ios::out };
   if (!file) {
     const auto& msg =
@@ -1309,7 +1412,7 @@
 
   file << content;
   if (!file) {
-    const auto& msg = cmStrCat(command, " failed to write \"", filename, "\"");
+    const auto& msg = cmStrCat(command, " failed to write \"", filename, '"');
     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, msg);
     return {};
   }
diff --git a/Source/cmCoreTryCompile.h b/Source/cmCoreTryCompile.h
index c185c68..3217a1b 100644
--- a/Source/cmCoreTryCompile.h
+++ b/Source/cmCoreTryCompile.h
@@ -6,9 +6,11 @@
 
 #include <map>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <cm/optional>
+#include <cm/string_view>
 
 #include "cmArgumentParser.h"
 #include "cmArgumentParserTypes.h"
@@ -51,17 +53,36 @@
 
   struct Arguments : public ArgumentParser::ParseResult
   {
+    Arguments(cmMakefile const* mf)
+      : Makefile(mf)
+    {
+    }
+
+    cmMakefile const* Makefile;
+
+    enum class SourceType
+    {
+      Normal,
+      CxxModule,
+      Directory,
+    };
+
     cm::optional<std::string> CompileResultVariable;
     cm::optional<std::string> BinaryDirectory;
     cm::optional<std::string> SourceDirectoryOrFile;
     cm::optional<std::string> ProjectName;
     cm::optional<std::string> TargetName;
-    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Sources;
-    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+    cm::optional<ArgumentParser::NonEmpty<
+      std::vector<std::pair<std::string, SourceType>>>>
+      Sources;
+    cm::optional<ArgumentParser::NonEmpty<
+      std::vector<std::pair<std::string, SourceType>>>>
       SourceFromContent;
-    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+    cm::optional<ArgumentParser::NonEmpty<
+      std::vector<std::pair<std::string, SourceType>>>>
       SourceFromVar;
-    cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>>
+    cm::optional<ArgumentParser::NonEmpty<
+      std::vector<std::pair<std::string, SourceType>>>>
       SourceFromFile;
     ArgumentParser::MaybeEmpty<std::vector<std::string>> CMakeFlags{
       1, "CMAKE_FLAGS"
@@ -79,6 +100,10 @@
     bool NoCache = false;
     bool NoLog = false;
 
+    ArgumentParser::Continue SetSourceType(cm::string_view sourceType);
+    SourceType SourceTypeContext = SourceType::Normal;
+    std::string SourceTypeError;
+
     // Argument for try_run only.
     // Keep in sync with warnings in cmCoreTryCompile::ParseArgs.
     cm::optional<std::string> CompileOutputVariable;
diff --git a/Source/cmCustomCommand.cxx b/Source/cmCustomCommand.cxx
index e12cf70..9958e4d 100644
--- a/Source/cmCustomCommand.cxx
+++ b/Source/cmCustomCommand.cxx
@@ -194,6 +194,16 @@
   this->JobPool = job_pool;
 }
 
+bool cmCustomCommand::GetJobserverAware() const
+{
+  return this->JobserverAware;
+}
+
+void cmCustomCommand::SetJobserverAware(bool b)
+{
+  this->JobserverAware = b;
+}
+
 #define DEFINE_CC_POLICY_ACCESSOR(P)                                          \
   cmPolicies::PolicyStatus cmCustomCommand::Get##P##Status() const            \
   {                                                                           \
diff --git a/Source/cmCustomCommand.h b/Source/cmCustomCommand.h
index 1e68dbf..167e601 100644
--- a/Source/cmCustomCommand.h
+++ b/Source/cmCustomCommand.h
@@ -115,6 +115,11 @@
   const std::string& GetJobPool() const;
   void SetJobPool(const std::string& job_pool);
 
+  /** Set/Get whether this custom command should be given access to the
+      jobserver (if possible).  */
+  bool GetJobserverAware() const;
+  void SetJobserverAware(bool b);
+
 #define DECLARE_CC_POLICY_ACCESSOR(P)                                         \
   cmPolicies::PolicyStatus Get##P##Status() const;
   CM_FOR_EACH_CUSTOM_COMMAND_POLICY(DECLARE_CC_POLICY_ACCESSOR)
@@ -139,6 +144,7 @@
   std::string WorkingDirectory;
   std::string Depfile;
   std::string JobPool;
+  bool JobserverAware = false;
   bool HaveComment = false;
   bool EscapeAllowMakeVars = false;
   bool EscapeOldStyle = true;
diff --git a/Source/cmCustomCommandGenerator.cxx b/Source/cmCustomCommandGenerator.cxx
index 2c1480a..634b63b 100644
--- a/Source/cmCustomCommandGenerator.cxx
+++ b/Source/cmCustomCommandGenerator.cxx
@@ -174,12 +174,6 @@
   , EmulatorsWithArguments(cc.GetCommandLines().size())
   , ComputeInternalDepfile(std::move(computeInternalDepfile))
 {
-  if (!this->ComputeInternalDepfile) {
-    this->ComputeInternalDepfile =
-      [this](const std::string& cfg, const std::string& file) -> std::string {
-      return this->GetInternalDepfileName(cfg, file);
-    };
-  }
 
   cmGeneratorExpression ge(*lg->GetCMakeInstance(), cc.GetBacktrace());
   cmGeneratorTarget const* target{ lg->FindGeneratorTargetToUse(
@@ -445,7 +439,7 @@
 }
 
 std::string cmCustomCommandGenerator::GetInternalDepfileName(
-  const std::string& /*config*/, const std::string& depfile)
+  const std::string& /*config*/, const std::string& depfile) const
 {
   cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
   std::string extension;
@@ -469,7 +463,10 @@
     return "";
   }
 
-  return this->ComputeInternalDepfile(this->OutputConfig, depfile);
+  if (this->ComputeInternalDepfile) {
+    return this->ComputeInternalDepfile(this->OutputConfig, depfile);
+  }
+  return this->GetInternalDepfileName(this->OutputConfig, depfile);
 }
 
 cm::optional<std::string> cmCustomCommandGenerator::GetComment() const
diff --git a/Source/cmCustomCommandGenerator.h b/Source/cmCustomCommandGenerator.h
index 4453654..43bdd10 100644
--- a/Source/cmCustomCommandGenerator.h
+++ b/Source/cmCustomCommandGenerator.h
@@ -20,7 +20,8 @@
 
 class cmCustomCommandGenerator
 {
-  std::string GetInternalDepfileName(const std::string&, const std::string&);
+  std::string GetInternalDepfileName(const std::string&,
+                                     const std::string&) const;
 
   cmCustomCommand const* CC;
   std::string OutputConfig;
diff --git a/Source/cmCxxModuleMapper.cxx b/Source/cmCxxModuleMapper.cxx
index e836a2a..e6c10c6 100644
--- a/Source/cmCxxModuleMapper.cxx
+++ b/Source/cmCxxModuleMapper.cxx
@@ -74,8 +74,62 @@
 
 namespace {
 
+struct TransitiveUsage
+{
+  TransitiveUsage(std::string name, std::string location, LookupMethod method)
+    : LogicalName(std::move(name))
+    , Location(std::move(location))
+    , Method(method)
+  {
+  }
+
+  std::string LogicalName;
+  std::string Location;
+  LookupMethod Method;
+};
+
+std::vector<TransitiveUsage> GetTransitiveUsages(
+  CxxModuleLocations const& loc, std::vector<cmSourceReqInfo> const& required,
+  CxxModuleUsage const& usages)
+{
+  std::set<std::string> transitive_usage_directs;
+  std::set<std::string> transitive_usage_names;
+
+  std::vector<TransitiveUsage> all_usages;
+
+  for (auto const& r : required) {
+    auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
+    if (bmi_loc.IsKnown()) {
+      all_usages.emplace_back(r.LogicalName, bmi_loc.Location(), r.Method);
+      transitive_usage_directs.insert(r.LogicalName);
+
+      // Insert transitive usages.
+      auto transitive_usages = usages.Usage.find(r.LogicalName);
+      if (transitive_usages != usages.Usage.end()) {
+        transitive_usage_names.insert(transitive_usages->second.begin(),
+                                      transitive_usages->second.end());
+      }
+    }
+  }
+
+  for (auto const& transitive_name : transitive_usage_names) {
+    if (transitive_usage_directs.count(transitive_name)) {
+      continue;
+    }
+
+    auto module_ref = usages.Reference.find(transitive_name);
+    if (module_ref != usages.Reference.end()) {
+      all_usages.emplace_back(transitive_name, module_ref->second.Path,
+                              module_ref->second.Method);
+    }
+  }
+
+  return all_usages;
+}
+
 std::string CxxModuleMapContentClang(CxxModuleLocations const& loc,
-                                     cmScanDepInfo const& obj)
+                                     cmScanDepInfo const& obj,
+                                     CxxModuleUsage const& usages)
 {
   std::stringstream mm;
 
@@ -98,12 +152,11 @@
       break;
     }
   }
-  for (auto const& r : obj.Requires) {
-    auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
-    if (bmi_loc.IsKnown()) {
-      mm << "-fmodule-file=" << r.LogicalName << "=" << bmi_loc.Location()
-         << '\n';
-    }
+
+  auto all_usages = GetTransitiveUsages(loc, obj.Requires, usages);
+  for (auto const& usage : all_usages) {
+    mm << "-fmodule-file=" << usage.LogicalName << '=' << usage.Location
+       << '\n';
   }
 
   return mm.str();
@@ -122,7 +175,7 @@
   // generate any).
 
   // Write the root directory to use for module paths.
-  mm << "$root " << loc.RootDirectory << "\n";
+  mm << "$root " << loc.RootDirectory << '\n';
 
   for (auto const& p : obj.Provides) {
     auto bmi_loc = loc.BmiGeneratorPathForModule(p.LogicalName);
@@ -180,37 +233,11 @@
     }
   }
 
-  std::set<std::string> transitive_usage_directs;
-  std::set<std::string> transitive_usage_names;
+  auto all_usages = GetTransitiveUsages(loc, obj.Requires, usages);
+  for (auto const& usage : all_usages) {
+    auto flag = flag_for_method(usage.Method);
 
-  for (auto const& r : obj.Requires) {
-    auto bmi_loc = loc.BmiGeneratorPathForModule(r.LogicalName);
-    if (bmi_loc.IsKnown()) {
-      auto flag = flag_for_method(r.Method);
-
-      mm << flag << ' ' << r.LogicalName << '=' << bmi_loc.Location() << "\n";
-      transitive_usage_directs.insert(r.LogicalName);
-
-      // Insert transitive usages.
-      auto transitive_usages = usages.Usage.find(r.LogicalName);
-      if (transitive_usages != usages.Usage.end()) {
-        transitive_usage_names.insert(transitive_usages->second.begin(),
-                                      transitive_usages->second.end());
-      }
-    }
-  }
-
-  for (auto const& transitive_name : transitive_usage_names) {
-    if (transitive_usage_directs.count(transitive_name)) {
-      continue;
-    }
-
-    auto module_ref = usages.Reference.find(transitive_name);
-    if (module_ref != usages.Reference.end()) {
-      auto flag = flag_for_method(module_ref->second.Method);
-      mm << flag << ' ' << transitive_name << '=' << module_ref->second.Path
-         << "\n";
-    }
+    mm << flag << ' ' << usage.LogicalName << '=' << usage.Location << '\n';
   }
 
   return mm.str();
@@ -393,7 +420,7 @@
 {
   switch (format) {
     case CxxModuleMapFormat::Clang:
-      return CxxModuleMapContentClang(loc, obj);
+      return CxxModuleMapContentClang(loc, obj, usages);
     case CxxModuleMapFormat::Gcc:
       return CxxModuleMapContentGcc(loc, obj);
     case CxxModuleMapFormat::Msvc:
diff --git a/Source/cmCxxModuleMapper.h b/Source/cmCxxModuleMapper.h
index ef01e48..34111f7 100644
--- a/Source/cmCxxModuleMapper.h
+++ b/Source/cmCxxModuleMapper.h
@@ -13,7 +13,8 @@
 #include <cm/optional>
 #include <cmext/string_view>
 
-#include "cmScanDepFormat.h"
+enum class LookupMethod;
+struct cmScanDepInfo;
 
 enum class CxxModuleMapFormat
 {
diff --git a/Source/cmCxxModuleUsageEffects.cxx b/Source/cmCxxModuleUsageEffects.cxx
new file mode 100644
index 0000000..eea5245
--- /dev/null
+++ b/Source/cmCxxModuleUsageEffects.cxx
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmCxxModuleUsageEffects.h"
+
+cmCxxModuleUsageEffects::cmCxxModuleUsageEffects(
+  cmGeneratorTarget const* /*gt*/)
+  : Hash("0000000000000000000000000000000000000000")
+{
+  // TODO: collect information from the generator target as to what might
+  // affect module consumption.
+}
+
+void cmCxxModuleUsageEffects::ApplyToTarget(cmTarget* /*tgt*/)
+{
+  // TODO: apply the information collected in the constructor
+}
+
+std::string const& cmCxxModuleUsageEffects::GetHash() const
+{
+  return this->Hash;
+}
diff --git a/Source/cmCxxModuleUsageEffects.h b/Source/cmCxxModuleUsageEffects.h
new file mode 100644
index 0000000..a63919c
--- /dev/null
+++ b/Source/cmCxxModuleUsageEffects.h
@@ -0,0 +1,22 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <string>
+
+class cmGeneratorTarget;
+class cmTarget;
+
+class cmCxxModuleUsageEffects
+{
+public:
+  cmCxxModuleUsageEffects(cmGeneratorTarget const* gt);
+
+  void ApplyToTarget(cmTarget* tgt);
+  std::string const& GetHash() const;
+
+private:
+  std::string Hash;
+};
diff --git a/Source/cmDebugTools.h b/Source/cmDebugTools.h
new file mode 100644
index 0000000..99c0c6b
--- /dev/null
+++ b/Source/cmDebugTools.h
@@ -0,0 +1,23 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <iostream>
+
+#define CM_DBG(expr) cm::dbg_impl(__FILE__, __LINE__, #expr, expr)
+
+namespace cm {
+
+namespace {
+
+template <typename T>
+T dbg_impl(const char* fname, int line, const char* expr, T value)
+{
+  std::cerr << fname << ':' << line << ": " << expr << " = " << value
+            << std::endl;
+  return value;
+}
+
+} // namespace
+
+} // namespace cm
diff --git a/Source/cmDebuggerAdapter.h b/Source/cmDebuggerAdapter.h
index f261d88..3403b36 100644
--- a/Source/cmDebuggerAdapter.h
+++ b/Source/cmDebuggerAdapter.h
@@ -16,7 +16,7 @@
 
 #include <cm3p/cppdap/io.h> // IWYU pragma: keep
 
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 
 class cmListFileFunction;
 class cmMakefile;
diff --git a/Source/cmDebuggerBreakpointManager.cxx b/Source/cmDebuggerBreakpointManager.cxx
index 4ae6728..db18c6e 100644
--- a/Source/cmDebuggerBreakpointManager.cxx
+++ b/Source/cmDebuggerBreakpointManager.cxx
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <cstddef>
 #include <cstdint>
-#include <memory>
 #include <utility>
 
 #include <cm3p/cppdap/optional.h>
diff --git a/Source/cmDebuggerBreakpointManager.h b/Source/cmDebuggerBreakpointManager.h
index 747722f..815262a 100644
--- a/Source/cmDebuggerBreakpointManager.h
+++ b/Source/cmDebuggerBreakpointManager.h
@@ -14,11 +14,9 @@
 
 #include <cm3p/cppdap/protocol.h>
 
-class cmListFileFunction;
+#include "cmDebuggerSourceBreakpoint.h"
 
-namespace cmDebugger {
-class cmDebuggerSourceBreakpoint;
-}
+class cmListFileFunction;
 
 namespace dap {
 class Session;
diff --git a/Source/cmDebuggerExceptionManager.h b/Source/cmDebuggerExceptionManager.h
index b819128..4c044d7 100644
--- a/Source/cmDebuggerExceptionManager.h
+++ b/Source/cmDebuggerExceptionManager.h
@@ -5,7 +5,6 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <cstddef>
-#include <functional>
 #include <mutex>
 #include <string>
 #include <unordered_map>
@@ -14,7 +13,7 @@
 
 #include <cm3p/cppdap/protocol.h>
 
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 
 namespace dap {
 class Session;
diff --git a/Source/cmDebuggerPipeConnection.cxx b/Source/cmDebuggerPipeConnection.cxx
deleted file mode 100644
index 1b54346..0000000
--- a/Source/cmDebuggerPipeConnection.cxx
+++ /dev/null
@@ -1,293 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmDebuggerPipeConnection.h"
-
-#include <algorithm>
-#include <cassert>
-#include <cstring>
-#include <stdexcept>
-#include <utility>
-
-namespace cmDebugger {
-
-struct write_req_t
-{
-  uv_write_t req;
-  uv_buf_t buf;
-};
-
-cmDebuggerPipeBase::cmDebuggerPipeBase(std::string name)
-  : PipeName(std::move(name))
-{
-  Loop.init();
-  LoopExit.init(
-    *Loop, [](uv_async_t* handle) { uv_stop((uv_loop_t*)handle->data); },
-    Loop);
-  WriteEvent.init(
-    *Loop,
-    [](uv_async_t* handle) {
-      auto* conn = static_cast<cmDebuggerPipeBase*>(handle->data);
-      conn->WriteInternal();
-    },
-    this);
-  PipeClose.init(
-    *Loop,
-    [](uv_async_t* handle) {
-      auto* conn = static_cast<cmDebuggerPipeBase*>(handle->data);
-      if (conn->Pipe.get()) {
-        conn->Pipe->data = nullptr;
-        conn->Pipe.reset();
-      }
-    },
-    this);
-}
-
-void cmDebuggerPipeBase::WaitForConnection()
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-  Connected.wait(lock, [this] { return isOpen() || FailedToOpen; });
-  if (FailedToOpen) {
-    throw std::runtime_error("Failed to open debugger connection.");
-  }
-}
-
-void cmDebuggerPipeBase::close()
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-
-  CloseConnection();
-  PipeClose.send();
-  lock.unlock();
-  ReadReady.notify_all();
-}
-
-size_t cmDebuggerPipeBase::read(void* buffer, size_t n)
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-  ReadReady.wait(lock, [this] { return !isOpen() || !ReadBuffer.empty(); });
-
-  if (!isOpen() && ReadBuffer.empty()) {
-    return 0;
-  }
-
-  auto size = std::min(n, ReadBuffer.size());
-  memcpy(buffer, ReadBuffer.data(), size);
-  ReadBuffer.erase(0, size);
-  return size;
-}
-
-bool cmDebuggerPipeBase::write(const void* buffer, size_t n)
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-  WriteBuffer.append(static_cast<const char*>(buffer), n);
-  lock.unlock();
-  WriteEvent.send();
-
-  lock.lock();
-  WriteComplete.wait(lock, [this] { return WriteBuffer.empty(); });
-  return true;
-}
-
-void cmDebuggerPipeBase::StopLoop()
-{
-  LoopExit.send();
-
-  if (LoopThread.joinable()) {
-    LoopThread.join();
-  }
-}
-
-void cmDebuggerPipeBase::BufferData(const std::string& data)
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-  ReadBuffer += data;
-  lock.unlock();
-  ReadReady.notify_all();
-}
-
-void cmDebuggerPipeBase::WriteInternal()
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-  auto n = WriteBuffer.length();
-  assert(this->Pipe.get());
-  write_req_t* req = new write_req_t;
-  req->req.data = &WriteComplete;
-  char* rawBuffer = new char[n];
-  req->buf = uv_buf_init(rawBuffer, static_cast<unsigned int>(n));
-  memcpy(req->buf.base, WriteBuffer.data(), n);
-  WriteBuffer.clear();
-  lock.unlock();
-
-  uv_write(
-    reinterpret_cast<uv_write_t*>(req), this->Pipe, &req->buf, 1,
-    [](uv_write_t* cb_req, int status) {
-      (void)status; // We need to free memory even if the write failed.
-      write_req_t* wr = reinterpret_cast<write_req_t*>(cb_req);
-      reinterpret_cast<std::condition_variable*>(wr->req.data)->notify_all();
-      delete[] (wr->buf.base);
-      delete wr;
-    });
-
-#ifdef __clang_analyzer__
-  // Tell clang-analyzer that 'rawBuffer' does not leak.
-  // We pass ownership to the closure.
-  delete[] rawBuffer;
-#endif
-}
-
-cmDebuggerPipeConnection::cmDebuggerPipeConnection(std::string name)
-  : cmDebuggerPipeBase(std::move(name))
-{
-  ServerPipeClose.init(
-    *Loop,
-    [](uv_async_t* handle) {
-      auto* conn = static_cast<cmDebuggerPipeConnection*>(handle->data);
-      if (conn->ServerPipe.get()) {
-        conn->ServerPipe->data = nullptr;
-        conn->ServerPipe.reset();
-      }
-    },
-    this);
-}
-
-cmDebuggerPipeConnection::~cmDebuggerPipeConnection()
-{
-  StopLoop();
-}
-
-bool cmDebuggerPipeConnection::StartListening(std::string& errorMessage)
-{
-  this->ServerPipe.init(*Loop, 0,
-                        static_cast<cmDebuggerPipeConnection*>(this));
-
-  int r;
-  if ((r = uv_pipe_bind(this->ServerPipe, this->PipeName.c_str())) != 0) {
-    errorMessage =
-      "Internal Error with " + this->PipeName + ": " + uv_err_name(r);
-    return false;
-  }
-
-  r = uv_listen(this->ServerPipe, 1, [](uv_stream_t* stream, int status) {
-    if (status >= 0) {
-      auto* conn = static_cast<cmDebuggerPipeConnection*>(stream->data);
-      if (conn) {
-        conn->Connect(stream);
-      }
-    }
-  });
-
-  if (r != 0) {
-    errorMessage =
-      "Internal Error listening on " + this->PipeName + ": " + uv_err_name(r);
-    return false;
-  }
-
-  // Start the libuv event loop thread so that a client can connect.
-  LoopThread = std::thread([this] { uv_run(Loop, UV_RUN_DEFAULT); });
-
-  StartedListening.set_value();
-
-  return true;
-}
-
-std::shared_ptr<dap::Reader> cmDebuggerPipeConnection::GetReader()
-{
-  return std::static_pointer_cast<dap::Reader>(shared_from_this());
-}
-
-std::shared_ptr<dap::Writer> cmDebuggerPipeConnection::GetWriter()
-{
-  return std::static_pointer_cast<dap::Writer>(shared_from_this());
-}
-
-bool cmDebuggerPipeConnection::isOpen()
-{
-  return this->Pipe.get() != nullptr;
-}
-
-void cmDebuggerPipeConnection::CloseConnection()
-{
-  ServerPipeClose.send();
-}
-
-void cmDebuggerPipeConnection::Connect(uv_stream_t* server)
-{
-  if (this->Pipe.get()) {
-    // Accept and close all pipes but the first:
-    cm::uv_pipe_ptr rejectPipe;
-
-    rejectPipe.init(*Loop, 0);
-    uv_accept(server, rejectPipe);
-
-    return;
-  }
-
-  cm::uv_pipe_ptr ClientPipe;
-  ClientPipe.init(*Loop, 0, static_cast<cmDebuggerPipeConnection*>(this));
-
-  if (uv_accept(server, ClientPipe) != 0) {
-    return;
-  }
-
-  StartReading<cmDebuggerPipeConnection>(ClientPipe);
-
-  std::unique_lock<std::mutex> lock(Mutex);
-  Pipe = std::move(ClientPipe);
-  lock.unlock();
-  Connected.notify_all();
-}
-
-cmDebuggerPipeClient::~cmDebuggerPipeClient()
-{
-  StopLoop();
-}
-
-void cmDebuggerPipeClient::Start()
-{
-  this->Pipe.init(*Loop, 0, static_cast<cmDebuggerPipeClient*>(this));
-
-  uv_connect_t* connect = new uv_connect_t;
-  connect->data = this;
-  uv_pipe_connect(
-    connect, Pipe, PipeName.c_str(), [](uv_connect_t* cb_connect, int status) {
-      auto* conn = static_cast<cmDebuggerPipeClient*>(cb_connect->data);
-      if (status >= 0) {
-        conn->Connect();
-      } else {
-        conn->FailConnection();
-      }
-      delete cb_connect;
-    });
-
-  // Start the libuv event loop so that the pipe can connect.
-  LoopThread = std::thread([this] { uv_run(Loop, UV_RUN_DEFAULT); });
-}
-
-bool cmDebuggerPipeClient::isOpen()
-{
-  return IsConnected;
-}
-
-void cmDebuggerPipeClient::CloseConnection()
-{
-  IsConnected = false;
-}
-
-void cmDebuggerPipeClient::Connect()
-{
-  StartReading<cmDebuggerPipeClient>(Pipe);
-  std::unique_lock<std::mutex> lock(Mutex);
-  IsConnected = true;
-  lock.unlock();
-  Connected.notify_all();
-}
-
-void cmDebuggerPipeClient::FailConnection()
-{
-  std::unique_lock<std::mutex> lock(Mutex);
-  FailedToOpen = true;
-  lock.unlock();
-  Connected.notify_all();
-}
-
-} // namespace cmDebugger
diff --git a/Source/cmDebuggerPipeConnection.h b/Source/cmDebuggerPipeConnection.h
deleted file mode 100644
index 0991ff7..0000000
--- a/Source/cmDebuggerPipeConnection.h
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-   file Copyright.txt or https://cmake.org/licensing for details.  */
-#pragma once
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include <condition_variable>
-#include <cstddef>
-#include <future>
-#include <memory>
-#include <mutex>
-#include <string>
-#include <thread>
-
-#include <cm3p/cppdap/io.h>
-#include <cm3p/uv.h>
-
-#include "cmDebuggerAdapter.h"
-#include "cmUVHandlePtr.h"
-
-namespace cmDebugger {
-
-class cmDebuggerPipeBase : public dap::ReaderWriter
-{
-public:
-  cmDebuggerPipeBase(std::string name);
-
-  void WaitForConnection();
-
-  // dap::ReaderWriter implementation
-
-  void close() final;
-  size_t read(void* buffer, size_t n) final;
-  bool write(const void* buffer, size_t n) final;
-
-protected:
-  virtual void CloseConnection(){};
-  template <typename T>
-  void StartReading(uv_stream_t* stream)
-  {
-    uv_read_start(
-      stream,
-      // alloc_cb
-      [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
-        (void)handle;
-        char* rawBuffer = new char[suggested_size];
-        *buf =
-          uv_buf_init(rawBuffer, static_cast<unsigned int>(suggested_size));
-      },
-      // read_cb
-      [](uv_stream_t* readStream, ssize_t nread, const uv_buf_t* buf) {
-        auto conn = static_cast<T*>(readStream->data);
-        if (conn) {
-          if (nread >= 0) {
-            conn->BufferData(std::string(buf->base, buf->base + nread));
-          } else {
-            conn->close();
-          }
-        }
-        delete[] (buf->base);
-      });
-  }
-  void StopLoop();
-
-  const std::string PipeName;
-  std::thread LoopThread;
-  cm::uv_loop_ptr Loop;
-  cm::uv_pipe_ptr Pipe;
-  std::mutex Mutex;
-  std::condition_variable Connected;
-  bool FailedToOpen = false;
-
-private:
-  void BufferData(const std::string& data);
-  void WriteInternal();
-
-  cm::uv_async_ptr LoopExit;
-  cm::uv_async_ptr WriteEvent;
-  cm::uv_async_ptr PipeClose;
-  std::string WriteBuffer;
-  std::string ReadBuffer;
-  std::condition_variable ReadReady;
-  std::condition_variable WriteComplete;
-};
-
-class cmDebuggerPipeConnection
-  : public cmDebuggerPipeBase
-  , public cmDebuggerConnection
-  , public std::enable_shared_from_this<cmDebuggerPipeConnection>
-{
-public:
-  cmDebuggerPipeConnection(std::string name);
-  ~cmDebuggerPipeConnection() override;
-
-  void WaitForConnection() override
-  {
-    cmDebuggerPipeBase::WaitForConnection();
-  }
-
-  bool StartListening(std::string& errorMessage) override;
-  std::shared_ptr<dap::Reader> GetReader() override;
-  std::shared_ptr<dap::Writer> GetWriter() override;
-
-  // dap::ReaderWriter implementation
-
-  bool isOpen() override;
-
-  // Used for unit test synchronization
-  std::promise<void> StartedListening;
-
-private:
-  void CloseConnection() override;
-  void Connect(uv_stream_t* server);
-
-  cm::uv_pipe_ptr ServerPipe;
-  cm::uv_async_ptr ServerPipeClose;
-};
-
-class cmDebuggerPipeClient : public cmDebuggerPipeBase
-{
-public:
-  using cmDebuggerPipeBase::cmDebuggerPipeBase;
-  ~cmDebuggerPipeClient() override;
-
-  void Start();
-
-  // dap::ReaderWriter implementation
-
-  bool isOpen() override;
-
-private:
-  void CloseConnection() override;
-  void Connect();
-  void FailConnection();
-
-  bool IsConnected = false;
-};
-
-} // namespace cmDebugger
diff --git a/Source/cmDebuggerPosixPipeConnection.cxx b/Source/cmDebuggerPosixPipeConnection.cxx
new file mode 100644
index 0000000..0587450
--- /dev/null
+++ b/Source/cmDebuggerPosixPipeConnection.cxx
@@ -0,0 +1,205 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmDebuggerPosixPipeConnection.h"
+
+#include <cerrno>
+#include <cstring>
+#include <stdexcept>
+#include <utility>
+
+#include <unistd.h>
+
+#include <sys/socket.h>
+
+namespace cmDebugger {
+
+#ifndef _WIN32
+
+cmDebuggerPipeConnection_POSIX::cmDebuggerPipeConnection_POSIX(
+  std::string name)
+  : PipeName(std::move(name))
+{
+  addr.sun_path[0] = '\0';
+}
+
+cmDebuggerPipeConnection_POSIX::~cmDebuggerPipeConnection_POSIX()
+{
+  if (isOpen()) {
+    close();
+  }
+}
+
+bool cmDebuggerPipeConnection_POSIX::StartListening(std::string& errorMessage)
+{
+  listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (listen_fd < 0) {
+    errorMessage = "Failed to create socket: ";
+    errorMessage += strerror(errno);
+    return false;
+  }
+
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, PipeName.c_str(), sizeof(addr.sun_path));
+  addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
+  if (bind(listen_fd, (sockaddr*)&addr, sizeof(addr)) == -1) {
+    errorMessage = "Failed to bind name '";
+    errorMessage += addr.sun_path;
+    errorMessage += "' to socket: ";
+    errorMessage += strerror(errno);
+    close_listen();
+    return false;
+  }
+
+  if (listen(listen_fd, 1) == -1) {
+    errorMessage = "Failed to listen on socket: ";
+    errorMessage += strerror(errno);
+    close_listen();
+    return false;
+  }
+
+  StartedListening.set_value();
+  return true;
+}
+
+std::shared_ptr<dap::Reader> cmDebuggerPipeConnection_POSIX::GetReader()
+{
+  return std::static_pointer_cast<dap::Reader>(shared_from_this());
+}
+
+std::shared_ptr<dap::Writer> cmDebuggerPipeConnection_POSIX::GetWriter()
+{
+  return std::static_pointer_cast<dap::Writer>(shared_from_this());
+}
+
+bool cmDebuggerPipeConnection_POSIX::isOpen()
+{
+  return rw_pipe >= 0;
+}
+
+void cmDebuggerPipeConnection_POSIX::close()
+{
+  close_listen();
+  ::close(rw_pipe);
+  rw_pipe = -1;
+}
+
+void cmDebuggerPipeConnection_POSIX::close_listen()
+{
+  if (strlen(addr.sun_path) > 0) {
+    unlink(addr.sun_path);
+    addr.sun_path[0] = '\0';
+  }
+  ::close(listen_fd);
+  listen_fd = -1;
+}
+
+void cmDebuggerPipeConnection_POSIX::WaitForConnection()
+{
+  sockaddr_un laddr;
+  socklen_t len = sizeof(laddr);
+  rw_pipe = accept(listen_fd, (sockaddr*)&laddr, &len);
+  if (rw_pipe < 0) {
+    close();
+    return;
+  }
+
+  close_listen(); // no longer need the listen resources
+}
+
+size_t cmDebuggerPipeConnection_POSIX::read(void* buffer, size_t n)
+{
+  size_t result = 0;
+  if (rw_pipe >= 0) {
+    result = ::read(rw_pipe, buffer, n);
+    if (result == 0) {
+      close();
+    }
+  }
+
+  return result;
+}
+
+bool cmDebuggerPipeConnection_POSIX::write(void const* buffer, size_t n)
+{
+  bool result = false;
+  if (rw_pipe >= 0) {
+    result = ::write(rw_pipe, buffer, n) >= 0;
+    if (!result) {
+      close();
+    }
+  }
+
+  return result;
+}
+
+cmDebuggerPipeClient_POSIX::cmDebuggerPipeClient_POSIX(std::string name)
+  : PipeName(std::move(name))
+{
+}
+
+cmDebuggerPipeClient_POSIX::~cmDebuggerPipeClient_POSIX()
+{
+  close();
+}
+
+void cmDebuggerPipeClient_POSIX::WaitForConnection()
+{
+  rw_pipe = socket(AF_UNIX, SOCK_STREAM, 0);
+  if (rw_pipe < 0) {
+    throw std::runtime_error(std::string("Failed to create socket: ") +
+                             strerror(errno));
+  }
+
+  sockaddr_un addr;
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, PipeName.c_str(), sizeof(addr.sun_path));
+  addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
+  if (connect(rw_pipe, (sockaddr*)&addr, sizeof(addr)) == -1) {
+    close();
+    throw std::runtime_error(
+      std::string("Failed to connect path to socket: ") + strerror(errno));
+  }
+}
+
+bool cmDebuggerPipeClient_POSIX::isOpen()
+{
+  return rw_pipe >= 0;
+}
+
+void cmDebuggerPipeClient_POSIX::close()
+{
+  if (isOpen()) {
+    ::close(rw_pipe);
+    rw_pipe = -1;
+  }
+}
+
+size_t cmDebuggerPipeClient_POSIX::read(void* buffer, size_t n)
+{
+  int count = 0;
+  if (isOpen()) {
+    count = static_cast<int>(::read(rw_pipe, buffer, n));
+    if (count == 0) {
+      close();
+    }
+  }
+
+  return count;
+}
+
+bool cmDebuggerPipeClient_POSIX::write(void const* buffer, size_t n)
+{
+  int count = 0;
+  if (isOpen()) {
+    count = static_cast<int>(::write(rw_pipe, buffer, n));
+    if (count < 0) {
+      close();
+    }
+  }
+
+  return count > 0;
+}
+
+#endif // !_WIN32
+
+} // namespace cmDebugger
diff --git a/Source/cmDebuggerPosixPipeConnection.h b/Source/cmDebuggerPosixPipeConnection.h
new file mode 100644
index 0000000..42642fa
--- /dev/null
+++ b/Source/cmDebuggerPosixPipeConnection.h
@@ -0,0 +1,81 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <cstddef>
+#include <future>
+#include <memory>
+#include <string>
+
+#include <cm3p/cppdap/io.h>
+
+#include <sys/un.h>
+
+#include "cmDebuggerAdapter.h"
+
+namespace cmDebugger {
+
+#ifndef _WIN32
+
+class cmDebuggerPipeConnection_POSIX
+  : public dap::ReaderWriter
+  , public cmDebuggerConnection
+  , public std::enable_shared_from_this<cmDebuggerPipeConnection_POSIX>
+{
+public:
+  cmDebuggerPipeConnection_POSIX(std::string name);
+  ~cmDebuggerPipeConnection_POSIX() override;
+
+  void WaitForConnection() override;
+
+  bool StartListening(std::string& errorMessage) override;
+  std::shared_ptr<dap::Reader> GetReader() override;
+  std::shared_ptr<dap::Writer> GetWriter() override;
+
+  // dap::ReaderWriter implementation
+
+  bool isOpen() override;
+  void close() override;
+  size_t read(void* buffer, size_t n) override;
+  bool write(void const* buffer, size_t n) override;
+
+  // Used for unit test synchronization
+  std::promise<void> StartedListening;
+
+private:
+  void close_listen(); // release listen resources
+
+  std::string const PipeName;
+  sockaddr_un addr;
+  int listen_fd = -1; // listen fd
+  int rw_pipe = -1;   // rw fd
+};
+
+using cmDebuggerPipeConnection = cmDebuggerPipeConnection_POSIX;
+
+class cmDebuggerPipeClient_POSIX
+  : public dap::ReaderWriter
+  , public std::enable_shared_from_this<cmDebuggerPipeClient_POSIX>
+{
+public:
+  cmDebuggerPipeClient_POSIX(std::string name);
+  ~cmDebuggerPipeClient_POSIX() override;
+  void WaitForConnection();
+
+  bool isOpen() override;
+  void close() override;
+  size_t read(void* buffer, size_t n) override;
+  bool write(void const* buffer, size_t n) override;
+
+private:
+  std::string const PipeName;
+  int rw_pipe = -1;
+};
+
+using cmDebuggerPipeClient = cmDebuggerPipeClient_POSIX;
+
+#endif // !_WIN32
+
+} // namespace cmDebugger
diff --git a/Source/cmDebuggerProtocol.h b/Source/cmDebuggerProtocol.h
index 4334aed..d4b1a96 100644
--- a/Source/cmDebuggerProtocol.h
+++ b/Source/cmDebuggerProtocol.h
@@ -4,7 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <vector>
+#include <string>
 
 #include <cm3p/cppdap/protocol.h>
 
diff --git a/Source/cmDebuggerVariablesHelper.cxx b/Source/cmDebuggerVariablesHelper.cxx
index 1322b20..73f9111 100644
--- a/Source/cmDebuggerVariablesHelper.cxx
+++ b/Source/cmDebuggerVariablesHelper.cxx
@@ -7,10 +7,11 @@
 #include <cstddef>
 #include <functional>
 #include <iomanip>
+#include <iterator>
 #include <map>
 #include <sstream>
 
-#include "cm_codecvt.hxx"
+#include "cm_codecvt_Encoding.hxx"
 
 #include "cmDebuggerStackFrame.h"
 #include "cmDebuggerVariables.h"
@@ -578,17 +579,17 @@
     return {};
   }
 
-  auto makeFileEncodingString = [](codecvt::Encoding encoding) {
+  auto makeFileEncodingString = [](codecvt_Encoding encoding) {
     switch (encoding) {
-      case codecvt::Encoding::None:
+      case codecvt_Encoding::None:
         return "None";
-      case codecvt::Encoding::UTF8:
+      case codecvt_Encoding::UTF8:
         return "UTF8";
-      case codecvt::Encoding::UTF8_WITH_BOM:
+      case codecvt_Encoding::UTF8_WITH_BOM:
         return "UTF8_WITH_BOM";
-      case codecvt::Encoding::ANSI:
+      case codecvt_Encoding::ANSI:
         return "ANSI";
-      case codecvt::Encoding::ConsoleOutput:
+      case codecvt_Encoding::ConsoleOutput:
         return "ConsoleOutput";
       default:
         return "Unknown";
diff --git a/Source/cmDebuggerVariablesManager.h b/Source/cmDebuggerVariablesManager.h
index c219164..9a64f10 100644
--- a/Source/cmDebuggerVariablesManager.h
+++ b/Source/cmDebuggerVariablesManager.h
@@ -7,14 +7,9 @@
 #include <cstdint>
 #include <functional>
 #include <unordered_map>
-#include <vector>
 
-#include <cm3p/cppdap/types.h> // IWYU pragma: keep
-
-namespace dap {
-struct Variable;
-struct VariablesRequest;
-}
+#include <cm3p/cppdap/protocol.h>
+#include <cm3p/cppdap/types.h>
 
 namespace cmDebugger {
 
diff --git a/Source/cmDebuggerWindowsPipeConnection.cxx b/Source/cmDebuggerWindowsPipeConnection.cxx
new file mode 100644
index 0000000..5f6f848
--- /dev/null
+++ b/Source/cmDebuggerWindowsPipeConnection.cxx
@@ -0,0 +1,287 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmDebuggerWindowsPipeConnection.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <stdexcept>
+#include <utility>
+
+namespace cmDebugger {
+
+#ifdef _WIN32
+
+DuplexPipe_WIN32::DuplexPipe_WIN32(HANDLE pipe)
+  : hPipe(pipe)
+{
+  readOp.Offset = readOp.OffsetHigh = 0;
+  readOp.hEvent = CreateEvent(NULL, true, false, NULL);
+  writeOp.Offset = readOp.OffsetHigh = 0;
+  writeOp.hEvent = CreateEvent(NULL, true, false, NULL);
+}
+
+DuplexPipe_WIN32::~DuplexPipe_WIN32()
+{
+  close();
+}
+
+size_t DuplexPipe_WIN32::read(void* buffer, size_t n)
+{
+  if (hPipe != INVALID_HANDLE_VALUE) {
+    readOp.Offset = readOp.OffsetHigh = 0;
+    ResetEvent(readOp.hEvent);
+    auto r = ReadFile(hPipe, buffer, n, NULL, &readOp);
+    auto err = GetLastError();
+    if (r || err == ERROR_IO_PENDING) {
+      DWORD nRead = 0;
+      if (GetOverlappedResult(hPipe, &readOp, &nRead, true)) {
+        return nRead;
+      }
+    }
+  }
+
+  return 0;
+}
+
+bool DuplexPipe_WIN32::write(void const* buffer, size_t n)
+{
+  if (hPipe != INVALID_HANDLE_VALUE) {
+    writeOp.Offset = writeOp.OffsetHigh = 0;
+    ResetEvent(writeOp.hEvent);
+    auto w = WriteFile(hPipe, buffer, n, NULL, &writeOp);
+    auto err = GetLastError();
+    if (w || err == ERROR_IO_PENDING) {
+      DWORD nWrite = 0;
+      if (GetOverlappedResult(hPipe, &writeOp, &nWrite, true)) {
+        return n == nWrite;
+      }
+    }
+  }
+
+  return false;
+}
+
+void DuplexPipe_WIN32::close()
+{
+  CloseHandle(hPipe);
+  hPipe = INVALID_HANDLE_VALUE;
+  CloseHandle(readOp.hEvent);
+  CloseHandle(writeOp.hEvent);
+  readOp.hEvent = writeOp.hEvent = INVALID_HANDLE_VALUE;
+}
+
+bool DuplexPipe_WIN32::WaitForConnection()
+{
+  auto connect = ConnectNamedPipe(hPipe, &readOp);
+  auto err = GetLastError();
+  if (!connect && err == ERROR_IO_PENDING) {
+    DWORD ignored;
+    if (GetOverlappedResult(hPipe, &readOp, &ignored, true)) {
+      return true;
+    }
+  }
+
+  return connect || err == ERROR_PIPE_CONNECTED;
+}
+
+cmDebuggerPipeConnection_WIN32::cmDebuggerPipeConnection_WIN32(
+  std::string name)
+  : PipeName(std::move(name))
+  , pipes(nullptr)
+{
+}
+
+cmDebuggerPipeConnection_WIN32::~cmDebuggerPipeConnection_WIN32()
+{
+  if (isOpen()) {
+    pipes = nullptr;
+  }
+}
+
+bool cmDebuggerPipeConnection_WIN32::StartListening(std::string& errorMessage)
+{
+  bool result = true;
+
+  auto hPipe = CreateNamedPipeA(
+    PipeName.c_str(),
+    PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
+    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS, 1,
+    1024 * 16, 1024 * 16, NMPWAIT_USE_DEFAULT_WAIT, NULL);
+
+  if (hPipe == INVALID_HANDLE_VALUE) {
+    auto err = GetLastError();
+    errorMessage = GetErrorMessage(err);
+    result = false;
+  }
+
+  if (result) {
+    pipes = std::make_unique<DuplexPipe_WIN32>(hPipe);
+  }
+
+  StartedListening.set_value();
+  return result;
+}
+
+std::string cmDebuggerPipeConnection_WIN32::GetErrorMessage(DWORD errorCode)
+{
+  LPSTR message = nullptr;
+  DWORD size = FormatMessageA(
+    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+      FORMAT_MESSAGE_IGNORE_INSERTS,
+    nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+    (LPSTR)&message, 0, nullptr);
+  std::string errorMessage = "Internal Error with " + this->PipeName + ": " +
+    std::string(message, size);
+  LocalFree(message);
+  return errorMessage;
+}
+
+std::shared_ptr<dap::Reader> cmDebuggerPipeConnection_WIN32::GetReader()
+{
+  return std::static_pointer_cast<dap::Reader>(shared_from_this());
+}
+
+std::shared_ptr<dap::Writer> cmDebuggerPipeConnection_WIN32::GetWriter()
+{
+  return std::static_pointer_cast<dap::Writer>(shared_from_this());
+}
+
+bool cmDebuggerPipeConnection_WIN32::isOpen()
+{
+  return pipes != nullptr;
+}
+
+void cmDebuggerPipeConnection_WIN32::close()
+{
+  CloseConnection();
+}
+
+void cmDebuggerPipeConnection_WIN32::CloseConnection()
+{
+  if (isOpen()) {
+    pipes->close();
+    pipes = nullptr;
+  }
+}
+
+void cmDebuggerPipeConnection_WIN32::WaitForConnection()
+{
+  if (!isOpen()) {
+    return;
+  }
+
+  if (pipes->WaitForConnection()) {
+    return;
+  }
+
+  CloseConnection();
+}
+
+size_t cmDebuggerPipeConnection_WIN32::read(void* buffer, size_t n)
+{
+  size_t result = 0;
+  if (isOpen()) {
+    result = pipes->read(buffer, n);
+    if (result == 0) {
+      CloseConnection();
+    }
+  }
+
+  return result;
+}
+
+bool cmDebuggerPipeConnection_WIN32::write(void const* buffer, size_t n)
+{
+  bool result = false;
+  if (isOpen()) {
+    result = pipes->write(buffer, n);
+    if (!result) {
+      CloseConnection();
+    }
+  }
+
+  return result;
+}
+
+cmDebuggerPipeClient_WIN32::cmDebuggerPipeClient_WIN32(std::string name)
+  : PipeName(std::move(name))
+{
+}
+
+cmDebuggerPipeClient_WIN32::~cmDebuggerPipeClient_WIN32()
+{
+  close();
+}
+
+void cmDebuggerPipeClient_WIN32::WaitForConnection()
+{
+  if (!isOpen()) {
+    auto hPipe = CreateFileA(PipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
+                             NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+    if (hPipe == INVALID_HANDLE_VALUE) {
+      auto err = GetLastError();
+      throw std::runtime_error(std::string("CreateFile failed for pipe ") +
+                               GetErrorMessage(err));
+    }
+
+    pipes = std::make_unique<DuplexPipe_WIN32>(hPipe);
+  }
+}
+
+std::string cmDebuggerPipeClient_WIN32::GetErrorMessage(DWORD errorCode)
+{
+  LPSTR message = nullptr;
+  DWORD size = FormatMessageA(
+    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+      FORMAT_MESSAGE_IGNORE_INSERTS,
+    nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+    (LPSTR)&message, 0, nullptr);
+  std::string errorMessage =
+    this->PipeName + ": " + std::string(message, size);
+  LocalFree(message);
+  return errorMessage;
+}
+
+bool cmDebuggerPipeClient_WIN32::isOpen()
+{
+  return pipes != nullptr;
+}
+
+void cmDebuggerPipeClient_WIN32::close()
+{
+  if (isOpen()) {
+    pipes->close();
+    pipes = nullptr;
+  }
+}
+
+size_t cmDebuggerPipeClient_WIN32::read(void* buffer, size_t n)
+{
+  size_t result = 0;
+  if (isOpen()) {
+    result = pipes->read(buffer, n);
+    if (result == 0) {
+      close();
+    }
+  }
+
+  return result;
+}
+
+bool cmDebuggerPipeClient_WIN32::write(void const* buffer, size_t n)
+{
+  bool result = false;
+  if (isOpen()) {
+    result = pipes->write(buffer, n);
+    if (!result) {
+      close();
+    }
+  }
+
+  return result;
+}
+
+#endif // _WIN32
+
+} // namespace cmDebugger
diff --git a/Source/cmDebuggerWindowsPipeConnection.h b/Source/cmDebuggerWindowsPipeConnection.h
new file mode 100644
index 0000000..6a57435
--- /dev/null
+++ b/Source/cmDebuggerWindowsPipeConnection.h
@@ -0,0 +1,103 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <condition_variable>
+#include <cstddef>
+#include <future>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include <windows.h>
+
+#include <cm3p/cppdap/io.h>
+
+#include "cmDebuggerAdapter.h"
+
+namespace cmDebugger {
+
+#ifdef _WIN32
+
+class DuplexPipe_WIN32
+{
+public:
+  DuplexPipe_WIN32(HANDLE read);
+  ~DuplexPipe_WIN32();
+
+  void close();
+  size_t read(void* buffer, size_t n);
+  bool write(void const* buffer, size_t n);
+
+  bool WaitForConnection();
+
+private:
+  HANDLE hPipe;
+  OVERLAPPED readOp;
+  OVERLAPPED writeOp;
+};
+
+class cmDebuggerPipeConnection_WIN32
+  : public dap::ReaderWriter
+  , public cmDebuggerConnection
+  , public std::enable_shared_from_this<cmDebuggerPipeConnection_WIN32>
+{
+public:
+  cmDebuggerPipeConnection_WIN32(std::string name);
+  ~cmDebuggerPipeConnection_WIN32() override;
+
+  void WaitForConnection() override;
+
+  bool StartListening(std::string& errorMessage) override;
+  std::shared_ptr<dap::Reader> GetReader() override;
+  std::shared_ptr<dap::Writer> GetWriter() override;
+
+  // dap::ReaderWriter implementation
+
+  bool isOpen() override;
+  void close() override;
+  size_t read(void* buffer, size_t n) override;
+  bool write(void const* buffer, size_t n) override;
+
+  // Used for unit test synchronization
+  std::promise<void> StartedListening;
+
+private:
+  void CloseConnection();
+  std::string GetErrorMessage(DWORD errorCode);
+
+  std::string const PipeName;
+  std::unique_ptr<DuplexPipe_WIN32> pipes;
+};
+
+using cmDebuggerPipeConnection = cmDebuggerPipeConnection_WIN32;
+
+class cmDebuggerPipeClient_WIN32
+  : public dap::ReaderWriter
+  , public std::enable_shared_from_this<cmDebuggerPipeClient_WIN32>
+{
+public:
+  cmDebuggerPipeClient_WIN32(std::string name);
+  ~cmDebuggerPipeClient_WIN32();
+  void WaitForConnection();
+
+  bool isOpen() override;
+  void close() override;
+  size_t read(void* buffer, size_t n) override;
+  bool write(void const* buffer, size_t n) override;
+
+private:
+  std::string GetErrorMessage(DWORD errorCode);
+
+  std::string const PipeName;
+  std::unique_ptr<DuplexPipe_WIN32> pipes;
+};
+
+using cmDebuggerPipeClient = cmDebuggerPipeClient_WIN32;
+
+#endif // _WIN32
+
+} // namespace cmDebugger
diff --git a/Source/cmDefinitions.cxx b/Source/cmDefinitions.cxx
index a5f8aab..453c0d1 100644
--- a/Source/cmDefinitions.cxx
+++ b/Source/cmDefinitions.cxx
@@ -3,7 +3,6 @@
 #include "cmDefinitions.h"
 
 #include <cassert>
-#include <functional>
 #include <unordered_set>
 #include <utility>
 
diff --git a/Source/cmDefinitions.h b/Source/cmDefinitions.h
index 22fef80..11d8947 100644
--- a/Source/cmDefinitions.h
+++ b/Source/cmDefinitions.h
@@ -4,7 +4,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <functional>
 #include <string>
 #include <unordered_map>
 #include <vector>
diff --git a/Source/cmDependsCompiler.cxx b/Source/cmDependsCompiler.cxx
index c8061c3..27f328d 100644
--- a/Source/cmDependsCompiler.cxx
+++ b/Source/cmDependsCompiler.cxx
@@ -6,7 +6,6 @@
 #include <algorithm>
 #include <iterator>
 #include <map>
-#include <memory>
 #include <string>
 #include <unordered_set>
 #include <utility>
diff --git a/Source/cmDependsFortran.cxx b/Source/cmDependsFortran.cxx
index d038db7..2a50565 100644
--- a/Source/cmDependsFortran.cxx
+++ b/Source/cmDependsFortran.cxx
@@ -6,6 +6,7 @@
 #include <cstdlib>
 #include <iostream>
 #include <map>
+#include <type_traits>
 #include <utility>
 
 #include "cmsys/FStream.hxx"
diff --git a/Source/cmDocumentation.cxx b/Source/cmDocumentation.cxx
index 77c5295..db2a606 100644
--- a/Source/cmDocumentation.cxx
+++ b/Source/cmDocumentation.cxx
@@ -9,17 +9,20 @@
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
+#include "cmsys/RegularExpression.hxx"
 
 #include "cmDocumentationEntry.h"
 #include "cmDocumentationSection.h"
 #include "cmRST.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVersion.h"
 
 namespace {
-const cmDocumentationEntry cmDocumentationStandardOptions[20] = {
+const cmDocumentationEntry cmDocumentationStandardOptions[21] = {
   { "-h,-H,--help,-help,-usage,/?", "Print usage information and exit." },
   { "--version,-version,/V [<file>]", "Print version number and exit." },
+  { "--help <keyword> [<file>]", "Print help for one keyword and exit." },
   { "--help-full [<file>]", "Print all help manuals and exit." },
   { "--help-manual <man> [<file>]", "Print one help manual and exit." },
   { "--help-manual-list [<file>]", "List help manuals available and exit." },
@@ -92,6 +95,8 @@
       return this->PrintHelp(os);
     case cmDocumentation::Full:
       return this->PrintHelpFull(os);
+    case cmDocumentation::OneArbitrary:
+      return this->PrintHelpOneArbitrary(os);
     case cmDocumentation::OneManual:
       return this->PrintHelpOneManual(os);
     case cmDocumentation::OneCommand:
@@ -176,6 +181,30 @@
   }
 }
 
+std::string cmDocumentation::GeneralizeKeyword(std::string cname)
+{
+  std::map<std::string, const std::vector<std::string>> conversions;
+  std::vector<std::string> languages = {
+    "C",      "CXX",      "CSharp",      "CUDA",     "OBJC",
+    "OBJCXX", "Fortran",  "HIP",         "ISPC",     "Swift",
+    "ASM",    "ASM_NASM", "ASM_MARMASM", "ASM_MASM", "ASM-ATT"
+  };
+  std::vector<std::string> configs = { "DEBUG", "RELEASE", "RELWITHDEBINFO",
+                                       "MINSIZEREL" };
+  conversions.emplace("LANG", std::move(languages));
+  conversions.emplace("CONFIG", std::move(configs));
+  for (auto const& it : conversions) {
+    for (auto const& to_replace : it.second) {
+      cmsys::RegularExpression reg(
+        cmStrCat("(^|_)(", to_replace, ")(\\.|$|_)"));
+      if (reg.find(cname)) {
+        cname.replace(reg.start(2), to_replace.length(), it.first);
+      }
+    }
+  }
+  return cname;
+}
+
 void cmDocumentation::addCommonStandardDocSections()
 {
   cmDocumentationSection sec{ "Options" };
@@ -237,10 +266,10 @@
         (strcmp(argv[i], "-h") == 0) || (strcmp(argv[i], "-H") == 0)) {
       help.HelpType = cmDocumentation::Help;
       i += int(get_opt_argument(i + 1, help.Argument));
-      help.Argument = cmSystemTools::LowerCase(help.Argument);
-      // special case for single command
+      // special case for arbitrary keyword help
       if (!help.Argument.empty()) {
-        help.HelpType = cmDocumentation::OneCommand;
+        help.HelpType = cmDocumentation::OneArbitrary;
+        i += int(get_opt_argument(i + 1, help.Filename));
       }
     } else if (strcmp(argv[i], "--help-properties") == 0) {
       help.HelpType = cmDocumentation::OneManual;
@@ -400,7 +429,7 @@
 {
   cmsys::Glob gl;
   std::string findExpr =
-    cmSystemTools::GetCMakeRoot() + "/Help/" + pattern + ".rst";
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Help/", pattern, ".rst");
   if (gl.FindFiles(findExpr)) {
     files = gl.GetFiles();
   }
@@ -452,8 +481,8 @@
   if (mlen > 3 && mname[mlen - 3] == '(' && mname[mlen - 1] == ')') {
     mname = mname.substr(0, mlen - 3) + "." + mname[mlen - 2];
   }
-  if (this->PrintFiles(os, "manual/" + mname) ||
-      this->PrintFiles(os, "manual/" + mname + ".[0-9]")) {
+  if (this->PrintFiles(os, cmStrCat("manual/", mname)) ||
+      this->PrintFiles(os, cmStrCat("manual/", mname, ".[0-9]"))) {
     return true;
   }
   // Argument was not a manual.  Complain.
@@ -469,10 +498,43 @@
   return true;
 }
 
+bool cmDocumentation::PrintHelpOneArbitrary(std::ostream& os)
+{
+  std::string word = cmSystemTools::HelpFileName(this->CurrentArgument);
+  std::string word_m = GeneralizeKeyword(word);
+
+  // Support legacy style uppercase commands, with LANG and CONFIG
+  // substitutions
+  bool found = this->PrintFiles(os, cmStrCat("*/", word));
+  if (found) {
+    os << "\n";
+  }
+  found = this->PrintFiles(
+            os, cmStrCat("command/", cmSystemTools::LowerCase(word))) ||
+    found;
+  if (found) {
+    return true;
+  }
+  found = this->PrintFiles(os, cmStrCat("*/", word_m));
+  if (found) {
+    os << "\n";
+  }
+  found = this->PrintFiles(
+            os, cmStrCat("command/", cmSystemTools::LowerCase(word_m))) ||
+    found;
+  if (found) {
+    return true;
+  }
+  os << "Argument \"" << this->CurrentArgument
+     << "\" to --help did not match any keywords.  "
+        "Use --help without any arguments to print CMake help information.\n";
+  return false;
+}
+
 bool cmDocumentation::PrintHelpOneCommand(std::ostream& os)
 {
   std::string cname = cmSystemTools::LowerCase(this->CurrentArgument);
-  if (this->PrintFiles(os, "command/" + cname)) {
+  if (this->PrintFiles(os, cmStrCat("command/", cname))) {
     return true;
   }
   // Argument was not a command.  Complain.
@@ -491,7 +553,7 @@
 bool cmDocumentation::PrintHelpOneModule(std::ostream& os)
 {
   std::string mname = this->CurrentArgument;
-  if (this->PrintFiles(os, "module/" + mname)) {
+  if (this->PrintFiles(os, cmStrCat("module/", mname))) {
     return true;
   }
   // Argument was not a module.  Complain.
@@ -519,7 +581,7 @@
 bool cmDocumentation::PrintHelpOneProperty(std::ostream& os)
 {
   std::string pname = cmSystemTools::HelpFileName(this->CurrentArgument);
-  if (this->PrintFiles(os, "prop_*/" + pname)) {
+  if (this->PrintFiles(os, cmStrCat("prop_*/", pname))) {
     return true;
   }
   // Argument was not a property.  Complain.
@@ -539,7 +601,7 @@
 {
   std::string pname = this->CurrentArgument;
   std::vector<std::string> files;
-  if (this->PrintFiles(os, "policy/" + pname)) {
+  if (this->PrintFiles(os, cmStrCat("policy/", pname))) {
     return true;
   }
 
@@ -567,7 +629,7 @@
 bool cmDocumentation::PrintHelpOneVariable(std::ostream& os)
 {
   std::string vname = cmSystemTools::HelpFileName(this->CurrentArgument);
-  if (this->PrintFiles(os, "variable/" + vname)) {
+  if (this->PrintFiles(os, cmStrCat("variable/", vname))) {
     return true;
   }
   // Argument was not a variable.  Complain.
diff --git a/Source/cmDocumentation.h b/Source/cmDocumentation.h
index 6930986..3e6bdfb 100644
--- a/Source/cmDocumentation.h
+++ b/Source/cmDocumentation.h
@@ -34,6 +34,7 @@
     ListVariables,
     ListPolicies,
     ListGenerators,
+    OneArbitrary,
     OneManual,
     OneCommand,
     OneModule,
@@ -118,6 +119,7 @@
   bool PrintUsage(std::ostream& os);
   bool PrintHelp(std::ostream& os);
   bool PrintHelpFull(std::ostream& os);
+  bool PrintHelpOneArbitrary(std::ostream& os);
   bool PrintHelpOneManual(std::ostream& os);
   bool PrintHelpOneCommand(std::ostream& os);
   bool PrintHelpOneModule(std::ostream& os);
@@ -154,4 +156,5 @@
   cmDocumentationFormatter Formatter;
 
   static void WarnFormFromFilename(RequestedHelpItem& request, bool& result);
+  static std::string GeneralizeKeyword(std::string word);
 };
diff --git a/Source/cmDocumentationFormatter.cxx b/Source/cmDocumentationFormatter.cxx
index 70ba1fc..b85b2f5 100644
--- a/Source/cmDocumentationFormatter.cxx
+++ b/Source/cmDocumentationFormatter.cxx
@@ -2,7 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmDocumentationFormatter.h"
 
-#include <algorithm>
+#include <algorithm> // IWYU pragma: keep
 #include <cassert>
 #include <iomanip>
 #include <ostream>
diff --git a/Source/cmDocumentationFormatter.h b/Source/cmDocumentationFormatter.h
index e269f6a..9d35e0a 100644
--- a/Source/cmDocumentationFormatter.h
+++ b/Source/cmDocumentationFormatter.h
@@ -4,6 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
 #include <iosfwd>
 #include <string>
 
diff --git a/Source/cmDyndepCollation.cxx b/Source/cmDyndepCollation.cxx
index b50718c..75f88b2 100644
--- a/Source/cmDyndepCollation.cxx
+++ b/Source/cmDyndepCollation.cxx
@@ -73,20 +73,36 @@
                                   gt->LocalGenerator, config, gt);
     }
 
-    std::map<std::string, cmSourceFile const*> sf_map;
+    enum class CompileType
     {
-      std::vector<cmSourceFile const*> objectSources;
-      gt->GetObjectSources(objectSources, config);
-      for (auto const* sf : objectSources) {
+      ObjectAndBmi,
+      BmiOnly,
+    };
+    std::map<std::string, std::pair<cmSourceFile const*, CompileType>> sf_map;
+    {
+      auto fill_sf_map = [gt, tgt, &sf_map](cmSourceFile const* sf,
+                                            CompileType type) {
         auto full_path = sf->GetFullPath();
         if (full_path.empty()) {
           gt->Makefile->IssueMessage(
             MessageType::INTERNAL_ERROR,
             cmStrCat("Target \"", tgt->GetName(),
                      "\" has a full path-less source file."));
-          continue;
+          return;
         }
-        sf_map[full_path] = sf;
+        sf_map[full_path] = std::make_pair(sf, type);
+      };
+
+      std::vector<cmSourceFile const*> objectSources;
+      gt->GetObjectSources(objectSources, config);
+      for (auto const* sf : objectSources) {
+        fill_sf_map(sf, CompileType::ObjectAndBmi);
+      }
+
+      std::vector<cmSourceFile const*> cxxModuleSources;
+      gt->GetCxxModuleSources(cxxModuleSources, config);
+      for (auto const* sf : cxxModuleSources) {
+        fill_sf_map(sf, CompileType::BmiOnly);
       }
     }
 
@@ -114,7 +130,8 @@
           continue;
         }
 
-        auto const* sf = lookup->second;
+        auto const* sf = lookup->second.first;
+        CompileType const ct = lookup->second.second;
 
         if (!sf) {
           gt->Makefile->IssueMessage(
@@ -124,11 +141,14 @@
           continue;
         }
 
-        auto obj_path = cb.ObjectFilePath(sf, config);
+        auto obj_path = ct == CompileType::ObjectAndBmi
+          ? cb.ObjectFilePath(sf, config)
+          : cb.BmiFilePath(sf, config);
         Json::Value& tdi_module_info = tdi_cxx_module_info[obj_path] =
           Json::objectValue;
 
         tdi_module_info["source"] = file;
+        tdi_module_info["bmi-only"] = ct == CompileType::BmiOnly;
         tdi_module_info["relative-directory"] = files_per_dir.first;
         tdi_module_info["name"] = file_set->GetName();
         tdi_module_info["type"] = file_set->GetType();
@@ -270,10 +290,11 @@
 struct CxxModuleFileSet
 {
   std::string Name;
+  bool BmiOnly = false;
   std::string RelativeDirectory;
   std::string SourcePath;
   std::string Type;
-  cmFileSetVisibility Visibility;
+  cmFileSetVisibility Visibility = cmFileSetVisibility::Private;
   cm::optional<std::string> Destination;
 };
 
@@ -357,6 +378,7 @@
       CxxModuleFileSet& fsi = export_info->ObjectToFileSet[i.key().asString()];
       auto const& tdi_cxx_module_info = *i;
       fsi.Name = tdi_cxx_module_info["name"].asString();
+      fsi.BmiOnly = tdi_cxx_module_info["bmi-only"].asBool();
       fsi.RelativeDirectory =
         tdi_cxx_module_info["relative-directory"].asString();
       if (!fsi.RelativeDirectory.empty() &&
@@ -645,3 +667,16 @@
   auto const& file_set = fileset_info_itr->second;
   return !cmFileSetVisibilityIsForInterface(file_set.Visibility);
 }
+
+bool cmDyndepCollation::IsBmiOnly(cmCxxModuleExportInfo const& exportInfo,
+                                  std::string const& object)
+{
+#ifdef _WIN32
+  auto object_path = object;
+  cmSystemTools::ConvertToUnixSlashes(object_path);
+#else
+  auto const& object_path = object;
+#endif
+  auto fs = exportInfo.ObjectToFileSet.find(object_path);
+  return (fs != exportInfo.ObjectToFileSet.end()) && fs->second.BmiOnly;
+}
diff --git a/Source/cmDyndepCollation.h b/Source/cmDyndepCollation.h
index 48afe2b..b193467 100644
--- a/Source/cmDyndepCollation.h
+++ b/Source/cmDyndepCollation.h
@@ -23,6 +23,8 @@
 {
   std::function<std::string(cmSourceFile const* sf, std::string const& config)>
     ObjectFilePath;
+  std::function<std::string(cmSourceFile const* sf, std::string const& config)>
+    BmiFilePath;
 };
 
 struct cmDyndepMetadataCallbacks
@@ -51,4 +53,7 @@
                                   cmDyndepMetadataCallbacks const& cb);
   static bool IsObjectPrivate(std::string const& object,
                               cmCxxModuleExportInfo const& export_info);
+
+  static bool IsBmiOnly(cmCxxModuleExportInfo const& exportInfo,
+                        std::string const& object);
 };
diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx
index 7fbd826..9b5f2b4 100644
--- a/Source/cmExecuteProcessCommand.cxx
+++ b/Source/cmExecuteProcessCommand.cxx
@@ -2,8 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExecuteProcessCommand.h"
 
-#include <algorithm>
 #include <cctype> /* isspace */
+#include <cstdint>
 #include <cstdio>
 #include <iostream>
 #include <map>
@@ -16,15 +16,21 @@
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
-#include "cmsys/Process.h"
+#include <cm3p/uv.h>
+
+#include "cm_fileno.hxx"
 
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmProcessOutput.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 namespace {
 bool cmExecuteProcessCommandIsWhitespace(char c)
@@ -35,7 +41,7 @@
 void cmExecuteProcessCommandFixText(std::vector<char>& output,
                                     bool strip_trailing_whitespace);
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   int length);
+                                   std::size_t length);
 }
 
 // cmExecuteProcessCommand
@@ -142,57 +148,61 @@
     }
   }
   // Create a process instance.
-  std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
-    cmsysProcess_New(), cmsysProcess_Delete);
-  cmsysProcess* cp = cp_ptr.get();
+  cmUVProcessChainBuilder builder;
 
   // Set the command sequence.
   for (std::vector<std::string> const& cmd : arguments.Commands) {
-    std::vector<const char*> argv(cmd.size() + 1);
-    std::transform(cmd.begin(), cmd.end(), argv.begin(),
-                   [](std::string const& s) { return s.c_str(); });
-    argv.back() = nullptr;
-    cmsysProcess_AddCommand(cp, argv.data());
+    builder.AddCommand(cmd);
   }
 
   // Set the process working directory.
   if (!arguments.WorkingDirectory.empty()) {
-    cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str());
+    builder.SetWorkingDirectory(arguments.WorkingDirectory);
   }
 
-  // Always hide the process window.
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-
   // Check the output variables.
-  bool merge_output = false;
+  std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose);
   if (!arguments.InputFile.empty()) {
-    cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN,
-                             arguments.InputFile.c_str());
+    inputFile.reset(cmsys::SystemTools::Fopen(arguments.InputFile, "rb"));
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
+                              cm_fileno(inputFile.get()));
+  } else {
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
+                              cm_fileno(stdin));
   }
+
+  std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose);
   if (!arguments.OutputFile.empty()) {
-    cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
-                             arguments.OutputFile.c_str());
-  }
-  if (!arguments.ErrorFile.empty()) {
-    if (arguments.ErrorFile == arguments.OutputFile) {
-      merge_output = true;
+    outputFile.reset(cmsys::SystemTools::Fopen(arguments.OutputFile, "wb"));
+    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                              cm_fileno(outputFile.get()));
+  } else {
+    if (arguments.OutputVariable == arguments.ErrorVariable &&
+        !arguments.ErrorVariable.empty()) {
+      builder.SetMergedBuiltinStreams();
     } else {
-      cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
-                               arguments.ErrorFile.c_str());
+      builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
     }
   }
-  if (!arguments.OutputVariable.empty() &&
-      arguments.OutputVariable == arguments.ErrorVariable) {
-    merge_output = true;
-  }
-  if (merge_output) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
+
+  std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose);
+  if (!arguments.ErrorFile.empty()) {
+    if (arguments.ErrorFile == arguments.OutputFile) {
+      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                                cm_fileno(outputFile.get()));
+    } else {
+      errorFile.reset(cmsys::SystemTools::Fopen(arguments.ErrorFile, "wb"));
+      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                                cm_fileno(errorFile.get()));
+    }
+  } else if (arguments.ErrorVariable.empty() ||
+             (!arguments.ErrorVariable.empty() &&
+              arguments.OutputVariable != arguments.ErrorVariable)) {
+    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
   }
 
   // Set the timeout if any.
-  if (timeout >= 0) {
-    cmsysProcess_SetTimeout(cp, timeout);
-  }
+  int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0);
 
   bool echo_stdout = false;
   bool echo_stderr = false;
@@ -240,36 +250,86 @@
     }
   }
   // Start the process.
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
+
+  bool timedOut = false;
+  cm::uv_timer_ptr timer;
+
+  if (timeoutMillis >= 0) {
+    timer.init(chain.GetLoop(), &timedOut);
+    timer.start(
+      [](uv_timer_t* handle) {
+        auto* timeoutPtr = static_cast<bool*>(handle->data);
+        *timeoutPtr = true;
+      },
+      timeoutMillis, 0);
+  }
 
   // Read the process output.
-  std::vector<char> tempOutput;
-  std::vector<char> tempError;
-  int length;
-  char* data;
-  int p;
+  struct ReadData
+  {
+    bool Finished = false;
+    std::vector<char> Output;
+    cm::uv_pipe_ptr Stream;
+  };
+  ReadData outputData;
+  ReadData errorData;
   cmProcessOutput processOutput(
     cmProcessOutput::FindEncoding(arguments.Encoding));
   std::string strdata;
-  while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
-    // Put the output in the right place.
-    if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) {
-      if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) {
-        processOutput.DecodeText(data, length, strdata, 1);
-        cmSystemTools::Stdout(strdata);
-      }
-      if (!arguments.OutputVariable.empty()) {
-        cmExecuteProcessCommandAppend(tempOutput, data, length);
-      }
-    } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) {
-      if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
-        processOutput.DecodeText(data, length, strdata, 2);
-        cmSystemTools::Stderr(strdata);
-      }
-      if (!arguments.ErrorVariable.empty()) {
-        cmExecuteProcessCommandAppend(tempError, data, length);
-      }
-    }
+
+  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
+  if (chain.OutputStream() >= 0) {
+    outputData.Stream.init(chain.GetLoop(), 0);
+    uv_pipe_open(outputData.Stream, chain.OutputStream());
+    outputHandle = cmUVStreamRead(
+      outputData.Stream,
+      [&arguments, &processOutput, &outputData,
+       &strdata](std::vector<char> data) {
+        if (!arguments.OutputQuiet) {
+          if (arguments.OutputVariable.empty() ||
+              arguments.EchoOutputVariable) {
+            processOutput.DecodeText(data.data(), data.size(), strdata, 1);
+            cmSystemTools::Stdout(strdata);
+          }
+          if (!arguments.OutputVariable.empty()) {
+            cmExecuteProcessCommandAppend(outputData.Output, data.data(),
+                                          data.size());
+          }
+        }
+      },
+      [&outputData]() { outputData.Finished = true; });
+  } else {
+    outputData.Finished = true;
+  }
+  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
+  if (chain.ErrorStream() >= 0 &&
+      chain.ErrorStream() != chain.OutputStream()) {
+    errorData.Stream.init(chain.GetLoop(), 0);
+    uv_pipe_open(errorData.Stream, chain.ErrorStream());
+    errorHandle = cmUVStreamRead(
+      errorData.Stream,
+      [&arguments, &processOutput, &errorData,
+       &strdata](std::vector<char> data) {
+        if (!arguments.ErrorQuiet) {
+          if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
+            processOutput.DecodeText(data.data(), data.size(), strdata, 2);
+            cmSystemTools::Stderr(strdata);
+          }
+          if (!arguments.ErrorVariable.empty()) {
+            cmExecuteProcessCommandAppend(errorData.Output, data.data(),
+                                          data.size());
+          }
+        }
+      },
+      [&errorData]() { errorData.Finished = true; });
+  } else {
+    errorData.Finished = true;
+  }
+
+  while (chain.Valid() && !timedOut &&
+         !(chain.Finished() && outputData.Finished && errorData.Finished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
   }
   if (!arguments.OutputQuiet &&
       (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
@@ -286,151 +346,102 @@
     }
   }
 
-  // All output has been read.  Wait for the process to exit.
-  cmsysProcess_WaitForExit(cp, nullptr);
-  processOutput.DecodeText(tempOutput, tempOutput);
-  processOutput.DecodeText(tempError, tempError);
+  // All output has been read.
+  processOutput.DecodeText(outputData.Output, outputData.Output);
+  processOutput.DecodeText(errorData.Output, errorData.Output);
 
   // Fix the text in the output strings.
-  cmExecuteProcessCommandFixText(tempOutput,
+  cmExecuteProcessCommandFixText(outputData.Output,
                                  arguments.OutputStripTrailingWhitespace);
-  cmExecuteProcessCommandFixText(tempError,
+  cmExecuteProcessCommandFixText(errorData.Output,
                                  arguments.ErrorStripTrailingWhitespace);
 
   // Store the output obtained.
-  if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
+  if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) {
     status.GetMakefile().AddDefinition(arguments.OutputVariable,
-                                       tempOutput.data());
+                                       outputData.Output.data());
   }
-  if (!merge_output && !arguments.ErrorVariable.empty() &&
-      !tempError.empty()) {
+  if (arguments.ErrorVariable != arguments.OutputVariable &&
+      !arguments.ErrorVariable.empty() && !errorData.Output.empty()) {
     status.GetMakefile().AddDefinition(arguments.ErrorVariable,
-                                       tempError.data());
+                                       errorData.Output.data());
   }
 
   // Store the result of running the process.
   if (!arguments.ResultVariable.empty()) {
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        int v = cmsysProcess_GetExitValue(cp);
-        char buf[16];
-        snprintf(buf, sizeof(buf), "%d", v);
-        status.GetMakefile().AddDefinition(arguments.ResultVariable, buf);
-      } break;
-      case cmsysProcess_State_Exception:
+    if (timedOut) {
+      status.GetMakefile().AddDefinition(arguments.ResultVariable,
+                                         "Process terminated due to timeout");
+    } else {
+      auto const* lastStatus = chain.GetStatus().back();
+      auto exception = lastStatus->GetException();
+      if (exception.first == cmUVProcessChain::ExceptionCode::None) {
         status.GetMakefile().AddDefinition(
-          arguments.ResultVariable, cmsysProcess_GetExceptionString(cp));
-        break;
-      case cmsysProcess_State_Error:
+          arguments.ResultVariable,
+          std::to_string(static_cast<int>(lastStatus->ExitStatus)));
+      } else {
         status.GetMakefile().AddDefinition(arguments.ResultVariable,
-                                           cmsysProcess_GetErrorString(cp));
-        break;
-      case cmsysProcess_State_Expired:
-        status.GetMakefile().AddDefinition(
-          arguments.ResultVariable, "Process terminated due to timeout");
-        break;
+                                           exception.second);
+      }
     }
   }
   // Store the result of running the processes.
   if (!arguments.ResultsVariable.empty()) {
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        std::vector<std::string> res;
-        for (size_t i = 0; i < arguments.Commands.size(); ++i) {
-          switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
-            case kwsysProcess_StateByIndex_Exited: {
-              int exitCode =
-                cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i));
-              char buf[16];
-              snprintf(buf, sizeof(buf), "%d", exitCode);
-              res.emplace_back(buf);
-            } break;
-            case kwsysProcess_StateByIndex_Exception:
-              res.emplace_back(cmsysProcess_GetExceptionStringByIndex(
-                cp, static_cast<int>(i)));
-              break;
-            case kwsysProcess_StateByIndex_Error:
-            default:
-              res.emplace_back("Error getting the child return code");
-              break;
-          }
+    if (timedOut) {
+      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                         "Process terminated due to timeout");
+    } else {
+      std::vector<std::string> res;
+      for (auto const* processStatus : chain.GetStatus()) {
+        auto exception = processStatus->GetException();
+        if (exception.first == cmUVProcessChain::ExceptionCode::None) {
+          res.emplace_back(
+            std::to_string(static_cast<int>(processStatus->ExitStatus)));
+        } else {
+          res.emplace_back(exception.second);
         }
-        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
-                                           cmJoin(res, ";"));
-      } break;
-      case cmsysProcess_State_Exception:
-        status.GetMakefile().AddDefinition(
-          arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp));
-        break;
-      case cmsysProcess_State_Error:
-        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
-                                           cmsysProcess_GetErrorString(cp));
-        break;
-      case cmsysProcess_State_Expired:
-        status.GetMakefile().AddDefinition(
-          arguments.ResultsVariable, "Process terminated due to timeout");
-        break;
+      }
+      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                         cmList::to_string(res));
     }
   }
 
-  auto queryProcessStatusByIndex = [&cp](int index) -> std::string {
-    std::string processStatus;
-    switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(index))) {
-      case kwsysProcess_StateByIndex_Exited: {
-        int exitCode = cmsysProcess_GetExitValueByIndex(cp, index);
-        if (exitCode) {
-          processStatus = "Child return code: " + std::to_string(exitCode);
-        }
-      } break;
-      case kwsysProcess_StateByIndex_Exception: {
-        processStatus = cmStrCat(
-          "Abnormal exit with child return code: ",
-          cmsysProcess_GetExceptionStringByIndex(cp, static_cast<int>(index)));
-        break;
+  auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string {
+    auto const& processStatus = chain.GetStatus(index);
+    auto exception = processStatus.GetException();
+    if (exception.first == cmUVProcessChain::ExceptionCode::None) {
+      if (processStatus.ExitStatus) {
+        return cmStrCat("Child return code: ", processStatus.ExitStatus);
       }
-      case kwsysProcess_StateByIndex_Error:
-      default:
-        processStatus = "Error getting the child return code";
-        break;
+      return "";
     }
-    return processStatus;
+    return cmStrCat("Abnormal exit with child return code: ",
+                    exception.second);
   };
 
   if (arguments.CommandErrorIsFatal == "ANY"_s) {
     bool ret = true;
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        std::map<int, std::string> failureIndices;
-        for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) {
-          std::string processStatus = queryProcessStatusByIndex(i);
-          if (!processStatus.empty()) {
-            failureIndices[i] = processStatus;
-          }
-          if (!failureIndices.empty()) {
-            std::ostringstream oss;
-            oss << "failed command indexes:\n";
-            for (auto const& e : failureIndices) {
-              oss << "  " << e.first + 1 << ": \"" << e.second << "\"\n";
-            }
-            status.SetError(oss.str());
-            ret = false;
-          }
+    if (timedOut) {
+      status.SetError("Process terminated due to timeout");
+      ret = false;
+    } else {
+      std::map<std::size_t, std::string> failureIndices;
+      auto statuses = chain.GetStatus();
+      for (std::size_t i = 0; i < statuses.size(); ++i) {
+        std::string processStatus = queryProcessStatusByIndex(i);
+        if (!processStatus.empty()) {
+          failureIndices[i] = processStatus;
         }
-      } break;
-      case cmsysProcess_State_Exception:
-        status.SetError(
-          cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
+      }
+      if (!failureIndices.empty()) {
+        std::ostringstream oss;
+        oss << "failed command indexes:\n";
+        for (auto const& e : failureIndices) {
+          oss << "  " << e.first + 1 << ": \"" << e.second << "\"\n";
+        }
+        status.SetError(oss.str());
         ret = false;
-        break;
-      case cmsysProcess_State_Error:
-        status.SetError(cmStrCat("error getting child return code: ",
-                                 cmsysProcess_GetErrorString(cp)));
-        ret = false;
-        break;
-      case cmsysProcess_State_Expired:
-        status.SetError("Process terminated due to timeout");
-        ret = false;
-        break;
+      }
     }
 
     if (!ret) {
@@ -441,29 +452,23 @@
 
   if (arguments.CommandErrorIsFatal == "LAST"_s) {
     bool ret = true;
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
+    if (timedOut) {
+      status.SetError("Process terminated due to timeout");
+      ret = false;
+    } else {
+      auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1);
+      auto exception = lastStatus.GetException();
+      if (exception.first != cmUVProcessChain::ExceptionCode::None) {
+        status.SetError(cmStrCat("Abnormal exit: ", exception.second));
+        ret = false;
+      } else {
         int lastIndex = static_cast<int>(arguments.Commands.size() - 1);
         const std::string processStatus = queryProcessStatusByIndex(lastIndex);
         if (!processStatus.empty()) {
           status.SetError("last command failed");
           ret = false;
         }
-      } break;
-      case cmsysProcess_State_Exception:
-        status.SetError(
-          cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
-        ret = false;
-        break;
-      case cmsysProcess_State_Error:
-        status.SetError(cmStrCat("Error getting child return code: ",
-                                 cmsysProcess_GetErrorString(cp)));
-        ret = false;
-        break;
-      case cmsysProcess_State_Expired:
-        status.SetError("Process terminated due to timeout");
-        ret = false;
-        break;
+      }
     }
     if (!ret) {
       cmSystemTools::SetFatalErrorOccurred();
@@ -506,7 +511,7 @@
 }
 
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   int length)
+                                   std::size_t length)
 {
 #if defined(__APPLE__)
   // HACK on Apple to work around bug with inserting at the
diff --git a/Source/cmExperimental.cxx b/Source/cmExperimental.cxx
index 2f26627..d75879f 100644
--- a/Source/cmExperimental.cxx
+++ b/Source/cmExperimental.cxx
@@ -18,36 +18,37 @@
  * Search for other instances to keep the documentation and test suite
  * up-to-date.
  */
-
-struct FeatureData
-{
-  std::string const Uuid;
-  std::string const Variable;
-  std::string const Description;
-  bool Warned;
-} LookupTable[] = {
-  // CxxModuleCMakeApi
-  { "aa1f7df0-828a-4fcd-9afc-2dc80491aca7",
-    "CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API",
-    "CMake's C++ module support is experimental. It is meant only for "
-    "experimentation and feedback to CMake developers.",
+cmExperimental::FeatureData LookupTable[] = {
+  // WindowsKernelModeDriver
+  { "WindowsKernelModeDriver",
+    "5c2d848d-4efa-4529-a768-efd57171bf68",
+    "CMAKE_EXPERIMENTAL_WINDOWS_KERNEL_MODE_DRIVER",
+    "CMake's Windows kernel-mode driver support is experimental. It is meant "
+    "only for experimentation and feedback to CMake developers.",
+    {},
+    cmExperimental::TryCompileCondition::Always,
     false },
 };
 static_assert(sizeof(LookupTable) / sizeof(LookupTable[0]) ==
                 static_cast<size_t>(cmExperimental::Feature::Sentinel),
               "Experimental feature lookup table mismatch");
 
-FeatureData& DataForFeature(cmExperimental::Feature f)
+cmExperimental::FeatureData& DataForFeature(cmExperimental::Feature f)
 {
   assert(f != cmExperimental::Feature::Sentinel);
   return LookupTable[static_cast<size_t>(f)];
 }
 }
 
+const cmExperimental::FeatureData& cmExperimental::DataForFeature(Feature f)
+{
+  return ::DataForFeature(f);
+}
+
 bool cmExperimental::HasSupportEnabled(cmMakefile const& mf, Feature f)
 {
   bool enabled = false;
-  auto& data = DataForFeature(f);
+  auto& data = ::DataForFeature(f);
 
   auto value = mf.GetDefinition(data.Variable);
   if (value == data.Uuid) {
diff --git a/Source/cmExperimental.h b/Source/cmExperimental.h
index 26e0d17..e4c1448 100644
--- a/Source/cmExperimental.h
+++ b/Source/cmExperimental.h
@@ -5,6 +5,9 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <string>
+#include <vector>
+
 class cmMakefile;
 
 class cmExperimental
@@ -12,10 +15,29 @@
 public:
   enum class Feature
   {
-    CxxModuleCMakeApi,
+    WindowsKernelModeDriver,
 
     Sentinel,
   };
 
+  enum class TryCompileCondition
+  {
+    Always,
+    SkipCompilerChecks,
+    Never,
+  };
+
+  struct FeatureData
+  {
+    std::string const Name;
+    std::string const Uuid;
+    std::string const Variable;
+    std::string const Description;
+    std::vector<std::string> const TryCompileVariables;
+    TryCompileCondition const ForwardThroughTryCompile;
+    bool Warned;
+  };
+
+  static const FeatureData& DataForFeature(Feature f);
   static bool HasSupportEnabled(cmMakefile const& mf, Feature f);
 };
diff --git a/Source/cmExportBuildAndroidMKGenerator.cxx b/Source/cmExportBuildAndroidMKGenerator.cxx
index 5e7008b..c54e6ac 100644
--- a/Source/cmExportBuildAndroidMKGenerator.cxx
+++ b/Source/cmExportBuildAndroidMKGenerator.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportBuildAndroidMKGenerator.h"
 
+#include <map>
 #include <sstream>
 #include <utility>
 #include <vector>
diff --git a/Source/cmExportBuildAndroidMKGenerator.h b/Source/cmExportBuildAndroidMKGenerator.h
index 1a9a626..7067488 100644
--- a/Source/cmExportBuildAndroidMKGenerator.h
+++ b/Source/cmExportBuildAndroidMKGenerator.h
@@ -8,7 +8,6 @@
 #include <string>
 
 #include "cmExportBuildFileGenerator.h"
-#include "cmExportFileGenerator.h"
 #include "cmStateTypes.h"
 
 class cmGeneratorTarget;
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index df26bad..69572f4 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -19,6 +19,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
+#include "cmList.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -125,6 +126,15 @@
                                     properties);
 
     std::string errorMessage;
+    if (!this->PopulateCxxModuleExportProperties(
+          gte, properties, cmGeneratorExpression::BuildInterface,
+          errorMessage)) {
+      this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
+        MessageType::FATAL_ERROR, errorMessage,
+        this->LG->GetMakefile()->GetBacktrace());
+      return false;
+    }
+
     if (!this->PopulateExportProperties(gte, properties, errorMessage)) {
       this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
         MessageType::FATAL_ERROR, errorMessage,
@@ -237,7 +247,7 @@
     }
 
     // Store the property.
-    properties[prop] = cmJoin(objects, ";");
+    properties[prop] = cmList::to_string(objects);
   } else {
     // Add the main target file.
     {
@@ -418,7 +428,7 @@
         resultVector.push_back(
           cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
       } else {
-        resultVector.push_back(cmStrCat('"', dest, '"'));
+        resultVector.emplace_back(cmStrCat('"', dest, '"'));
         break;
       }
     }
@@ -477,7 +487,7 @@
           resultVector.push_back(
             cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
         } else {
-          resultVector.push_back(cmStrCat('"', escapedFile, '"'));
+          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
         }
       }
     }
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index a58f2b7..e78b869 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -14,8 +14,8 @@
 
 #include "cmArgumentParser.h"
 #include "cmArgumentParserTypes.h"
+#include "cmCryptoHash.h"
 #include "cmExecutionStatus.h"
-#include "cmExperimental.h"
 #include "cmExportBuildAndroidMKGenerator.h"
 #include "cmExportBuildFileGenerator.h"
 #include "cmExportSet.h"
@@ -68,15 +68,11 @@
     bool ExportOld = false;
   };
 
-  auto parser = cmArgumentParser<Arguments>{}
-                  .Bind("NAMESPACE"_s, &Arguments::Namespace)
-                  .Bind("FILE"_s, &Arguments::Filename);
-
-  bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
-    status.GetMakefile(), cmExperimental::Feature::CxxModuleCMakeApi);
-  if (supportCxx20FileSetTypes) {
-    parser.Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory);
-  }
+  auto parser =
+    cmArgumentParser<Arguments>{}
+      .Bind("NAMESPACE"_s, &Arguments::Namespace)
+      .Bind("FILE"_s, &Arguments::Filename)
+      .Bind("CXX_MODULES_DIRECTORY"_s, &Arguments::CxxModulesDirectory);
 
   if (args[0] == "EXPORT") {
     parser.Bind("EXPORT"_s, &Arguments::ExportSetName);
@@ -310,7 +306,8 @@
   // named by a hash of its own content.  This is deterministic and is
   // unique with high probability.
   const std::string& outDir = mf.GetCurrentBinaryDirectory();
-  std::string hash = cmSystemTools::ComputeStringMD5(outDir);
+  cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+  std::string hash = hasher.HashString(outDir);
   StorePackageRegistry(mf, package, outDir.c_str(), hash.c_str());
 
   return true;
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 22276ae..dae061b 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cmext/string_view>
 
 #include "cmsys/FStream.hxx"
 
@@ -383,7 +384,7 @@
   cmGeneratorExpression ge(*target->Makefile->GetCMakeInstance());
 
   std::string dirs = cmGeneratorExpression::Preprocess(
-    cmJoin(target->Target->GetInstallIncludeDirectoriesEntries(te), ";"),
+    cmList::to_string(target->Target->GetInstallIncludeDirectoriesEntries(te)),
     preprocessRule, true);
   this->ReplaceInstallPrefix(dirs);
   std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(dirs);
@@ -536,7 +537,7 @@
   const cmComputeLinkInformation::ItemVector& deps = info->GetItems();
 
   for (auto const& dep : deps) {
-    if (!dep.Target) {
+    if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
       continue;
     }
     getPropertyContents(dep.Target, "COMPATIBLE_INTERFACE_BOOL",
@@ -955,13 +956,13 @@
 
   // Isolate the file policy level.
   // Support CMake versions as far back as 2.6 but also support using NEW
-  // policy settings for up to CMake 3.25 (this upper limit may be reviewed
+  // policy settings for up to CMake 3.26 (this upper limit may be reviewed
   // and increased from time to time). This reduces the opportunity for CMake
   // warnings when an older export file is later used with newer CMake
   // versions.
   /* clang-format off */
   os << "cmake_policy(PUSH)\n"
-     << "cmake_policy(VERSION 2.8.3...3.25)\n";
+     << "cmake_policy(VERSION 2.8.3...3.26)\n";
   /* clang-format on */
 }
 
@@ -1255,6 +1256,94 @@
   os << ")\n\n";
 }
 
+enum class PropertyType
+{
+  Strings,
+  Paths,
+};
+
+struct ModulePropertyTable
+{
+  cm::static_string_view Name;
+  PropertyType Type;
+};
+
+bool cmExportFileGenerator::PopulateCxxModuleExportProperties(
+  cmGeneratorTarget const* gte, ImportPropertyMap& properties,
+  cmGeneratorExpression::PreprocessContext ctx, std::string& errorMessage)
+{
+  if (!gte->HaveCxx20ModuleSources(&errorMessage)) {
+    return true;
+  }
+
+  const cm::static_string_view exportedDirectModuleProperties[] = {
+    "CXX_EXTENSIONS"_s,
+  };
+  for (auto const& propName : exportedDirectModuleProperties) {
+    auto const propNameStr = std::string(propName);
+    cmValue prop = gte->Target->GetComputedProperty(
+      propNameStr, *gte->Target->GetMakefile());
+    if (!prop) {
+      prop = gte->Target->GetProperty(propNameStr);
+    }
+    if (prop) {
+      properties[propNameStr] = cmGeneratorExpression::Preprocess(*prop, ctx);
+    }
+  }
+
+  const ModulePropertyTable exportedModuleProperties[] = {
+    { "INCLUDE_DIRECTORIES"_s, PropertyType::Paths },
+    { "COMPILE_DEFINITIONS"_s, PropertyType::Strings },
+    { "COMPILE_OPTIONS"_s, PropertyType::Strings },
+    { "COMPILE_FEATURES"_s, PropertyType::Strings },
+  };
+  for (auto const& propEntry : exportedModuleProperties) {
+    auto const propNameStr = std::string(propEntry.Name);
+    cmValue prop = gte->Target->GetComputedProperty(
+      propNameStr, *gte->Target->GetMakefile());
+    if (!prop) {
+      prop = gte->Target->GetProperty(propNameStr);
+    }
+    if (prop) {
+      auto const exportedPropName =
+        cmStrCat("IMPORTED_CXX_MODULES_", propEntry.Name);
+      properties[exportedPropName] =
+        cmGeneratorExpression::Preprocess(*prop, ctx);
+      if (ctx == cmGeneratorExpression::InstallInterface &&
+          propEntry.Type == PropertyType::Paths) {
+        this->ReplaceInstallPrefix(properties[exportedPropName]);
+        prefixItems(properties[exportedPropName]);
+      }
+    }
+  }
+
+  const cm::static_string_view exportedLinkModuleProperties[] = {
+    "LINK_LIBRARIES"_s,
+  };
+  for (auto const& propName : exportedLinkModuleProperties) {
+    auto const propNameStr = std::string(propName);
+    cmValue prop = gte->Target->GetComputedProperty(
+      propNameStr, *gte->Target->GetMakefile());
+    if (!prop) {
+      prop = gte->Target->GetProperty(propNameStr);
+    }
+    if (prop) {
+      auto const exportedPropName =
+        cmStrCat("IMPORTED_CXX_MODULES_", propName);
+      auto value = cmGeneratorExpression::Preprocess(*prop, ctx);
+      this->ResolveTargetsInGeneratorExpressions(
+        value, gte, cmExportFileGenerator::ReplaceFreeTargets);
+      std::vector<std::string> wrappedValues;
+      for (auto& item : cmList{ value }) {
+        wrappedValues.push_back(cmStrCat("$<COMPILE_ONLY:", item, '>'));
+      }
+      properties[exportedPropName] = cmJoin(wrappedValues, ";");
+    }
+  }
+
+  return true;
+}
+
 bool cmExportFileGenerator::PopulateExportProperties(
   cmGeneratorTarget const* gte, ImportPropertyMap& properties,
   std::string& errorMessage)
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index fdda878..6fa19ee 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -175,6 +175,9 @@
   virtual void GenerateRequiredCMakeVersion(std::ostream& os,
                                             const char* versionString);
 
+  bool PopulateCxxModuleExportProperties(
+    cmGeneratorTarget const* gte, ImportPropertyMap& properties,
+    cmGeneratorExpression::PreprocessContext ctx, std::string& errorMessage);
   bool PopulateExportProperties(cmGeneratorTarget const* gte,
                                 ImportPropertyMap& properties,
                                 std::string& errorMessage);
diff --git a/Source/cmExportInstallAndroidMKGenerator.h b/Source/cmExportInstallAndroidMKGenerator.h
index c05751a..061358d 100644
--- a/Source/cmExportInstallAndroidMKGenerator.h
+++ b/Source/cmExportInstallAndroidMKGenerator.h
@@ -8,7 +8,6 @@
 #include <set>
 #include <string>
 
-#include "cmExportFileGenerator.h"
 #include "cmExportInstallFileGenerator.h"
 #include "cmStateTypes.h"
 
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 538c883..908bb31 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -19,6 +19,7 @@
 #include "cmInstallExportGenerator.h"
 #include "cmInstallFileSetGenerator.h"
 #include "cmInstallTargetGenerator.h"
+#include "cmList.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -125,6 +126,13 @@
       gt, cmGeneratorExpression::InstallInterface, properties);
 
     std::string errorMessage;
+    if (!this->PopulateCxxModuleExportProperties(
+          gt, properties, cmGeneratorExpression::InstallInterface,
+          errorMessage)) {
+      cmSystemTools::Error(errorMessage);
+      return false;
+    }
+
     if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
       cmSystemTools::Error(errorMessage);
       return false;
@@ -430,7 +438,7 @@
     }
 
     // Store the property.
-    properties[prop] = cmJoin(objects, ";");
+    properties[prop] = cmList::to_string(objects);
     importedLocations.insert(prop);
   } else {
     if (target->IsFrameworkOnApple() && target->HasImportLibrary(config)) {
@@ -590,10 +598,12 @@
   auto cge = ge.Parse(te->FileSetGenerators.at(fileSet)->GetDestination());
 
   for (auto const& config : configs) {
-    auto dest = cmStrCat("${_IMPORT_PREFIX}/",
-                         cmOutputConverter::EscapeForCMake(
-                           cge->Evaluate(gte->LocalGenerator, config, gte),
-                           cmOutputConverter::WrapQuotes::NoWrap));
+    auto unescapedDest = cge->Evaluate(gte->LocalGenerator, config, gte);
+    auto dest = cmOutputConverter::EscapeForCMake(
+      unescapedDest, cmOutputConverter::WrapQuotes::NoWrap);
+    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+      dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
+    }
 
     auto const& type = fileSet->GetType();
     // C++ modules do not support interface file sets which are dependent upon
@@ -613,7 +623,7 @@
       resultVector.push_back(
         cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
     } else {
-      resultVector.push_back(cmStrCat('"', dest, '"'));
+      resultVector.emplace_back(cmStrCat('"', dest, '"'));
       break;
     }
   }
@@ -645,11 +655,14 @@
       fileSet->EvaluateFileEntry(directories, files, entry,
                                  gte->LocalGenerator, config, gte);
     }
-    auto dest = cmStrCat("${_IMPORT_PREFIX}/",
-                         cmOutputConverter::EscapeForCMake(
-                           destCge->Evaluate(gte->LocalGenerator, config, gte),
-                           cmOutputConverter::WrapQuotes::NoWrap),
-                         '/');
+    auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte);
+    auto dest =
+      cmStrCat(cmOutputConverter::EscapeForCMake(
+                 unescapedDest, cmOutputConverter::WrapQuotes::NoWrap),
+               '/');
+    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
+      dest = cmStrCat("${_IMPORT_PREFIX}/", dest);
+    }
 
     bool const contextSensitive = destCge->GetHadContextSensitiveCondition() ||
       std::any_of(directoryEntries.begin(), directoryEntries.end(),
@@ -684,7 +697,7 @@
           resultVector.push_back(
             cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
         } else {
-          resultVector.push_back(cmStrCat('"', escapedFile, '"'));
+          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
         }
       }
     }
diff --git a/Source/cmExportLibraryDependenciesCommand.cxx b/Source/cmExportLibraryDependenciesCommand.cxx
index 8aec12b..02d0444 100644
--- a/Source/cmExportLibraryDependenciesCommand.cxx
+++ b/Source/cmExportLibraryDependenciesCommand.cxx
@@ -3,6 +3,7 @@
 #include "cmExportLibraryDependenciesCommand.h"
 
 #include <map>
+#include <unordered_map>
 #include <utility>
 
 #include <cm/memory>
diff --git a/Source/cmExportTryCompileFileGenerator.cxx b/Source/cmExportTryCompileFileGenerator.cxx
index f30c3c3..00c9173 100644
--- a/Source/cmExportTryCompileFileGenerator.cxx
+++ b/Source/cmExportTryCompileFileGenerator.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExportTryCompileFileGenerator.h"
 
-#include <map>
 #include <utility>
 
 #include <cm/memory>
@@ -13,7 +12,6 @@
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmList.h"
-#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
@@ -156,12 +154,12 @@
   cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/)
 {
   return cmOutputConverter::EscapeForCMake(
-    cmJoin(fileSet->GetDirectoryEntries(), ";"));
+    cmList::to_string(fileSet->GetDirectoryEntries()));
 }
 
 std::string cmExportTryCompileFileGenerator::GetFileSetFiles(
   cmGeneratorTarget* /*gte*/, cmFileSet* fileSet, cmTargetExport* /*te*/)
 {
   return cmOutputConverter::EscapeForCMake(
-    cmJoin(fileSet->GetFileEntries(), ";"));
+    cmList::to_string(fileSet->GetFileEntries()));
 }
diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx
index 8abb5a8..8b0f309 100644
--- a/Source/cmFileAPI.cxx
+++ b/Source/cmFileAPI.cxx
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <cassert>
 #include <chrono>
-#include <cstddef>
 #include <ctime>
 #include <iomanip>
 #include <sstream>
diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx
index 280ebb0..3c1bc14 100644
--- a/Source/cmFileAPICodemodel.cxx
+++ b/Source/cmFileAPICodemodel.cxx
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <cassert>
 #include <cstddef>
-#include <functional>
 #include <limits>
 #include <map>
 #include <memory>
@@ -1679,6 +1678,7 @@
   }
 
   switch (sk.Kind) {
+    case cmGeneratorTarget::SourceKindCxxModuleSource:
     case cmGeneratorTarget::SourceKindObjectSource: {
       source["compileGroupIndex"] =
         this->AddSourceCompileGroup(sk.Source.Value, si);
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 8f2ebf5..93bed9a 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -30,6 +30,7 @@
 
 #include "cmArgumentParser.h"
 #include "cmArgumentParserTypes.h"
+#include "cmCMakePath.h"
 #include "cmCryptoHash.h"
 #include "cmELF.h"
 #include "cmExecutionStatus.h"
@@ -42,6 +43,7 @@
 #include "cmGeneratorExpression.h"
 #include "cmGlobalGenerator.h"
 #include "cmHexFileConverter.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -805,7 +807,7 @@
 
   std::sort(files.begin(), files.end());
   files.erase(std::unique(files.begin(), files.end()), files.end());
-  status.GetMakefile().AddDefinition(variable, cmJoin(files, ";"));
+  status.GetMakefile().AddDefinition(variable, cmList::to_string(files));
   return true;
 }
 
@@ -1012,7 +1014,8 @@
   if (success) {
     if (changed) {
       std::string message =
-        cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"');
+        cmStrCat("Set non-toolchain portion of runtime path of \"", file,
+                 "\" to \"", *newRPath, '"');
       status.GetMakefile().DisplayStatus(message, -1);
     }
     ft.Store(file);
@@ -1066,7 +1069,8 @@
   if (success) {
     if (changed) {
       std::string message =
-        cmStrCat("Set runtime path of \"", file, "\" to \"", *newRPath, '"');
+        cmStrCat("Set non-toolchain portion of runtime path of \"", file,
+                 "\" to \"", *newRPath, '"');
       status.GetMakefile().DisplayStatus(message, -1);
     }
     ft.Store(file);
@@ -1277,9 +1281,58 @@
     }
   }
 
-  auto realPath =
-    cmSystemTools::CollapseFullPath(input, *arguments.BaseDirectory);
-  realPath = cmSystemTools::GetRealPath(realPath);
+  bool warnAbout152 = false;
+  bool use152New = true;
+  cmPolicies::PolicyStatus policyStatus =
+    status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0152);
+  switch (policyStatus) {
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::NEW:
+      break;
+    case cmPolicies::WARN:
+      use152New = false;
+      warnAbout152 = true;
+      break;
+    case cmPolicies::OLD:
+      use152New = false;
+      warnAbout152 = false;
+      break;
+  }
+
+  auto computeNewPath = [=](std::string const& in, std::string& result) {
+    auto path = cmCMakePath{ in };
+    if (path.IsRelative()) {
+      auto basePath = cmCMakePath{ *arguments.BaseDirectory };
+      path = basePath.Append(path);
+    }
+    result = cmSystemTools::GetActualCaseForPath(
+      cmSystemTools::GetRealPath(path.String()));
+  };
+
+  std::string realPath;
+  if (use152New) {
+    computeNewPath(input, realPath);
+  } else {
+    std::string oldPolicyPath =
+      cmSystemTools::CollapseFullPath(input, *arguments.BaseDirectory);
+    oldPolicyPath = cmSystemTools::GetRealPath(oldPolicyPath);
+    if (warnAbout152) {
+      computeNewPath(input, realPath);
+      if (oldPolicyPath != realPath) {
+        status.GetMakefile().IssueMessage(
+          MessageType::AUTHOR_WARNING,
+          cmStrCat(
+            cmPolicies::GetPolicyWarning(cmPolicies::CMP0152), '\n',
+            "From input path:\n  ", input,
+            "\nthe policy OLD behavior produces path:\n  ", oldPolicyPath,
+            "\nbut the policy NEW behavior produces path:\n  ", realPath,
+            "\nSince the policy is not set, CMake is using the OLD "
+            "behavior for compatibility."));
+      }
+    }
+    realPath = oldPolicyPath;
+  }
 
   status.GetMakefile().AddDefinition(args[2], realPath);
 
@@ -1556,7 +1609,7 @@
 #endif
   std::vector<std::string> path = cmSystemTools::SplitString(args[1], pathSep);
 
-  std::string value = cmJoin(cmMakeRange(path).transform(convert), ";");
+  std::string value = cmList::to_string(cmMakeRange(path).transform(convert));
   status.GetMakefile().AddDefinition(args[2], value);
   return true;
 }
@@ -2967,8 +3020,7 @@
   }
 
   // Check if the new file already exists and remove it.
-  if ((cmSystemTools::FileExists(newFileName) ||
-       cmSystemTools::FileIsSymlink(newFileName)) &&
+  if (cmSystemTools::PathExists(newFileName) &&
       !cmSystemTools::RemoveFile(newFileName)) {
     std::ostringstream e;
     e << "Failed to create link '" << newFileName
@@ -3158,7 +3210,7 @@
       if (!parsedArgs.RPathPrefix.empty()) {
         status.GetMakefile().AddDefinition(
           parsedArgs.RPathPrefix + "_" + firstPath,
-          cmJoin(archive.GetRPaths().at(firstPath), ";"));
+          cmList::to_string(archive.GetRPaths().at(firstPath)));
       }
     } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
       conflictingDeps.push_back(val.first);
@@ -3166,7 +3218,7 @@
       paths.insert(paths.begin(), val.second.begin(), val.second.end());
       std::string varName =
         parsedArgs.ConflictingDependenciesPrefix + "_" + val.first;
-      std::string pathsStr = cmJoin(paths, ";");
+      std::string pathsStr = cmList::to_string(paths);
       status.GetMakefile().AddDefinition(varName, pathsStr);
     } else {
       std::ostringstream e;
@@ -3197,17 +3249,17 @@
   }
 
   if (!parsedArgs.ResolvedDependenciesVar.empty()) {
-    std::string val = cmJoin(deps, ";");
+    std::string val = cmList::to_string(deps);
     status.GetMakefile().AddDefinition(parsedArgs.ResolvedDependenciesVar,
                                        val);
   }
   if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
-    std::string val = cmJoin(unresolvedDeps, ";");
+    std::string val = cmList::to_string(unresolvedDeps);
     status.GetMakefile().AddDefinition(parsedArgs.UnresolvedDependenciesVar,
                                        val);
   }
   if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
-    std::string val = cmJoin(conflictingDeps, ";");
+    std::string val = cmList::to_string(conflictingDeps);
     status.GetMakefile().AddDefinition(
       parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val);
   }
diff --git a/Source/cmFileLock.cxx b/Source/cmFileLock.cxx
index 5d197d2..548e327 100644
--- a/Source/cmFileLock.cxx
+++ b/Source/cmFileLock.cxx
@@ -12,11 +12,7 @@
 cmFileLock::cmFileLock(cmFileLock&& other) noexcept
 {
   this->File = other.File;
-#if defined(_WIN32)
-  other.File = INVALID_HANDLE_VALUE;
-#else
-  other.File = -1;
-#endif
+  other.File = (decltype(other.File))-1;
   this->Filename = std::move(other.Filename);
 }
 
@@ -32,11 +28,7 @@
 cmFileLock& cmFileLock::operator=(cmFileLock&& other) noexcept
 {
   this->File = other.File;
-#if defined(_WIN32)
-  other.File = INVALID_HANDLE_VALUE;
-#else
-  other.File = -1;
-#endif
+  other.File = (decltype(other.File))-1;
   this->Filename = std::move(other.Filename);
 
   return *this;
diff --git a/Source/cmFileLock.h b/Source/cmFileLock.h
index 94baea1..0f2e7d9 100644
--- a/Source/cmFileLock.h
+++ b/Source/cmFileLock.h
@@ -7,7 +7,7 @@
 #include <string>
 
 #if defined(_WIN32)
-#  include <windows.h> // HANDLE
+using HANDLE = void*;
 #endif
 
 class cmFileLockResult;
@@ -53,8 +53,8 @@
   cmFileLockResult LockWithTimeout(unsigned long timeoutSec);
 
 #if defined(_WIN32)
-  HANDLE File = INVALID_HANDLE_VALUE;
-  BOOL LockFile(DWORD flags);
+  HANDLE File = (HANDLE)-1;
+  int LockFile(int flags);
 #else
   int File = -1;
   int LockFile(int cmd, int type) const;
diff --git a/Source/cmFileLockResult.cxx b/Source/cmFileLockResult.cxx
index b7f7f38..632c0e9 100644
--- a/Source/cmFileLockResult.cxx
+++ b/Source/cmFileLockResult.cxx
@@ -5,6 +5,10 @@
 #include <cerrno>
 #include <cstring>
 
+#ifdef _WIN32
+#  include <Windows.h>
+#endif
+
 cmFileLockResult cmFileLockResult::MakeOk()
 {
   return { OK, 0 };
diff --git a/Source/cmFileLockResult.h b/Source/cmFileLockResult.h
index 8a58d1f..e252de7 100644
--- a/Source/cmFileLockResult.h
+++ b/Source/cmFileLockResult.h
@@ -6,10 +6,6 @@
 
 #include <string>
 
-#if defined(_WIN32)
-#  include <windows.h> // DWORD
-#endif
-
 /**
  * @brief Result of the locking/unlocking file.
  * @note See @c cmFileLock
@@ -17,11 +13,7 @@
 class cmFileLockResult
 {
 public:
-#if defined(_WIN32)
-  using Error = DWORD;
-#else
   using Error = int;
-#endif
 
   /**
    * @brief Successful lock/unlock.
diff --git a/Source/cmFileLockWin32.cxx b/Source/cmFileLockWin32.cxx
index 7bee5f2..244ade2 100644
--- a/Source/cmFileLockWin32.cxx
+++ b/Source/cmFileLockWin32.cxx
@@ -78,7 +78,7 @@
   }
 }
 
-BOOL cmFileLock::LockFile(DWORD flags)
+int cmFileLock::LockFile(int flags)
 {
   const DWORD reserved = 0;
   const unsigned long len = static_cast<unsigned long>(-1);
diff --git a/Source/cmFileSet.cxx b/Source/cmFileSet.cxx
index 48a2570..bcf7fba 100644
--- a/Source/cmFileSet.cxx
+++ b/Source/cmFileSet.cxx
@@ -7,6 +7,7 @@
 #include <utility>
 #include <vector>
 
+#include <cmext/algorithm>
 #include <cmext/string_view>
 
 #include "cmsys/RegularExpression.hxx"
@@ -88,6 +89,12 @@
 {
 }
 
+void cmFileSet::CopyEntries(cmFileSet const* fs)
+{
+  cm::append(this->DirectoryEntries, fs->DirectoryEntries);
+  cm::append(this->FileEntries, fs->FileEntries);
+}
+
 void cmFileSet::ClearDirectoryEntries()
 {
   this->DirectoryEntries.clear();
diff --git a/Source/cmFileSet.h b/Source/cmFileSet.h
index 54d430c..c508e2b 100644
--- a/Source/cmFileSet.h
+++ b/Source/cmFileSet.h
@@ -41,6 +41,8 @@
   const std::string& GetType() const { return this->Type; }
   cmFileSetVisibility GetVisibility() const { return this->Visibility; }
 
+  void CopyEntries(cmFileSet const* fs);
+
   void ClearDirectoryEntries();
   void AddDirectoryEntry(BT<std::string> directories);
   const std::vector<BT<std::string>>& GetDirectoryEntries() const
diff --git a/Source/cmFileTime.cxx b/Source/cmFileTime.cxx
index 0606baf..3d103d3 100644
--- a/Source/cmFileTime.cxx
+++ b/Source/cmFileTime.cxx
@@ -2,7 +2,6 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmFileTime.h"
 
-#include <ctime>
 #include <string>
 
 // Use a platform-specific API to get file times efficiently.
diff --git a/Source/cmFindBase.cxx b/Source/cmFindBase.cxx
index 5e92dd0..8840cdc 100644
--- a/Source/cmFindBase.cxx
+++ b/Source/cmFindBase.cxx
@@ -6,6 +6,7 @@
 #include <cstddef>
 #include <deque>
 #include <functional>
+#include <iterator>
 #include <map>
 #include <utility>
 
@@ -328,9 +329,6 @@
   // Add LIB or INCLUDE
   if (!this->EnvironmentPath.empty()) {
     paths.AddEnvPath(this->EnvironmentPath);
-#if defined(_WIN32) || defined(__CYGWIN__)
-    paths.AddEnvPrefixPath("PATH", true);
-#endif
   }
   // Add PATH
   paths.AddEnvPath("PATH");
diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx
index 9eb0603..df77ad0 100644
--- a/Source/cmFindLibraryCommand.cxx
+++ b/Source/cmFindLibraryCommand.cxx
@@ -251,8 +251,14 @@
   void DebugLibraryFailed(std::string const& name, std::string const& path)
   {
     if (this->DebugMode) {
-      auto regexName =
-        cmStrCat(this->PrefixRegexStr, name, this->SuffixRegexStr);
+      // To improve readability of the debug output, if there is only one
+      // prefix/suffix, use the plain prefix/suffix instead of the regex.
+      const auto& prefix = (this->Prefixes.size() == 1) ? this->Prefixes[0]
+                                                        : this->PrefixRegexStr;
+      const auto& suffix = (this->Suffixes.size() == 1) ? this->Suffixes[0]
+                                                        : this->SuffixRegexStr;
+
+      auto regexName = cmStrCat(prefix, name, suffix);
       this->DebugSearches.FailedAt(path, regexName);
     }
   }
@@ -552,6 +558,14 @@
   // Search for all names in each search path.
   for (std::string const& d : this->SearchPaths) {
     for (std::string const& n : this->Names) {
+      fwPath = cmStrCat(d, n, ".xcframework");
+      if (cmSystemTools::FileIsDirectory(fwPath)) {
+        auto finalPath = cmSystemTools::CollapseFullPath(fwPath);
+        if (this->Validate(finalPath)) {
+          return finalPath;
+        }
+      }
+
       fwPath = cmStrCat(d, n, ".framework");
       if (cmSystemTools::FileIsDirectory(fwPath)) {
         auto finalPath = cmSystemTools::CollapseFullPath(fwPath);
@@ -572,6 +586,14 @@
   // Search for each name in all search paths.
   for (std::string const& n : this->Names) {
     for (std::string const& d : this->SearchPaths) {
+      fwPath = cmStrCat(d, n, ".xcframework");
+      if (cmSystemTools::FileIsDirectory(fwPath)) {
+        auto finalPath = cmSystemTools::CollapseFullPath(fwPath);
+        if (this->Validate(finalPath)) {
+          return finalPath;
+        }
+      }
+
       fwPath = cmStrCat(d, n, ".framework");
       if (cmSystemTools::FileIsDirectory(fwPath)) {
         auto finalPath = cmSystemTools::CollapseFullPath(fwPath);
diff --git a/Source/cmFindPackageCommand.cxx b/Source/cmFindPackageCommand.cxx
index f863a51..30458cd 100644
--- a/Source/cmFindPackageCommand.cxx
+++ b/Source/cmFindPackageCommand.cxx
@@ -11,7 +11,6 @@
 #include <sstream>
 #include <utility>
 
-#include <cm/memory>
 #include <cm/optional>
 #include <cmext/string_view>
 
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index f4768b6..8d2d972 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -11,6 +11,7 @@
 
 #include "cmExecutionStatus.h"
 #include "cmFunctionBlocker.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
@@ -89,9 +90,9 @@
   }
 
   // define ARGV and ARGN
-  auto const argvDef = cmJoin(expandedArgs, ";");
+  auto const argvDef = cmList::to_string(expandedArgs);
   auto const eit = expandedArgs.begin() + (this->Args.size() - 1);
-  auto const argnDef = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
+  auto const argnDef = cmList::to_string(cmMakeRange(eit, expandedArgs.end()));
   makefile.AddDefinition(ARGV, argvDef);
   makefile.MarkVariableAsUsed(ARGV);
   makefile.AddDefinition(ARGN, argnDef);
diff --git a/Source/cmGccDepfileLexerHelper.cxx b/Source/cmGccDepfileLexerHelper.cxx
index 87377de..a375904 100644
--- a/Source/cmGccDepfileLexerHelper.cxx
+++ b/Source/cmGccDepfileLexerHelper.cxx
@@ -4,7 +4,6 @@
 
 #include <algorithm>
 #include <cstdio>
-#include <memory>
 #include <string>
 #include <vector>
 
diff --git a/Source/cmGeneratedFileStream.cxx b/Source/cmGeneratedFileStream.cxx
index 133bf5f..37a2be4 100644
--- a/Source/cmGeneratedFileStream.cxx
+++ b/Source/cmGeneratedFileStream.cxx
@@ -3,6 +3,7 @@
 #include "cmGeneratedFileStream.h"
 
 #include <cstdio>
+#include <locale>
 
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -16,7 +17,7 @@
 cmGeneratedFileStream::cmGeneratedFileStream(Encoding encoding)
 {
 #ifndef CMAKE_BOOTSTRAP
-  if (encoding != codecvt::None) {
+  if (encoding != codecvt_Encoding::None) {
     this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
   }
 #else
@@ -35,13 +36,13 @@
     cmSystemTools::ReportLastSystemError("");
   }
 #ifndef CMAKE_BOOTSTRAP
-  if (encoding != codecvt::None) {
+  if (encoding != codecvt_Encoding::None) {
     this->imbue(std::locale(this->getloc(), new codecvt(encoding)));
   }
 #else
   static_cast<void>(encoding);
 #endif
-  if (encoding == codecvt::UTF8_WITH_BOM) {
+  if (encoding == codecvt_Encoding::UTF8_WITH_BOM) {
     // Write the BOM encoding header into the file
     char magic[] = { static_cast<char>(0xEF), static_cast<char>(0xBB),
                      static_cast<char>(0xBF) };
diff --git a/Source/cmGeneratedFileStream.h b/Source/cmGeneratedFileStream.h
index bfc121f..a26616d 100644
--- a/Source/cmGeneratedFileStream.h
+++ b/Source/cmGeneratedFileStream.h
@@ -8,7 +8,7 @@
 
 #include "cmsys/FStream.hxx"
 
-#include "cm_codecvt.hxx"
+#include "cm_codecvt_Encoding.hxx"
 
 // This is the first base class of cmGeneratedFileStream.  It will be
 // created before and destroyed after the ofstream portion and can
@@ -77,13 +77,13 @@
 {
 public:
   using Stream = cmsys::ofstream;
-  using Encoding = codecvt::Encoding;
+  using Encoding = codecvt_Encoding;
 
   /**
    * This constructor prepares a default stream.  The open method must
    * be used before writing to the stream.
    */
-  cmGeneratedFileStream(Encoding encoding = codecvt::None);
+  cmGeneratedFileStream(codecvt_Encoding encoding = codecvt_Encoding::None);
 
   /**
    * This constructor takes the name of the file to be generated.  It
@@ -92,7 +92,7 @@
    * second argument is set to true.
    */
   cmGeneratedFileStream(std::string const& name, bool quiet = false,
-                        Encoding encoding = codecvt::None);
+                        codecvt_Encoding encoding = codecvt_Encoding::None);
 
   /**
    * The destructor checks the stream status to be sure the temporary
@@ -151,5 +151,5 @@
    * Write a specific string using an alternate encoding.
    * Afterward, the original encoding is restored.
    */
-  void WriteAltEncoding(std::string const& data, Encoding encoding);
+  void WriteAltEncoding(std::string const& data, codecvt_Encoding encoding);
 };
diff --git a/Source/cmGeneratorExpressionEvaluator.cxx b/Source/cmGeneratorExpressionEvaluator.cxx
index b239408..50334ef 100644
--- a/Source/cmGeneratorExpressionEvaluator.cxx
+++ b/Source/cmGeneratorExpressionEvaluator.cxx
@@ -153,10 +153,12 @@
         return std::string();
       }
       std::string parameter;
-      for (const auto& pExprEval : *pit) {
-        parameter += pExprEval->Evaluate(context, dagChecker);
-        if (context->HadError) {
-          return std::string();
+      if (node->ShouldEvaluateNextParameter(parameters, parameter)) {
+        for (const auto& pExprEval : *pit) {
+          parameter += pExprEval->Evaluate(context, dagChecker);
+          if (context->HadError) {
+            return std::string();
+          }
         }
       }
       parameters.push_back(std::move(parameter));
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index 4c80d4b..57beb72 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -128,6 +128,16 @@
 
   int NumExpectedParameters() const override { return OneOrMoreParameters; }
 
+  bool ShouldEvaluateNextParameter(const std::vector<std::string>& parameters,
+                                   std::string& def_value) const override
+  {
+    if (!parameters.empty() && parameters[0] == failureVal) {
+      def_value = failureVal;
+      return false;
+    }
+    return true;
+  }
+
   std::string Evaluate(const std::vector<std::string>& parameters,
                        cmGeneratorExpressionContext* context,
                        const GeneratorExpressionContent* content,
@@ -195,6 +205,13 @@
 
   int NumExpectedParameters() const override { return 3; }
 
+  bool ShouldEvaluateNextParameter(const std::vector<std::string>& parameters,
+                                   std::string&) const override
+  {
+    return (parameters.empty() ||
+            parameters[0] != cmStrCat(parameters.size() - 1, ""));
+  }
+
   std::string Evaluate(const std::vector<std::string>& parameters,
                        cmGeneratorExpressionContext* context,
                        const GeneratorExpressionContent* content,
@@ -2542,7 +2559,7 @@
     list.front() = LL_BEGIN;
     list.push_back(LL_END);
 
-    return cmJoin(list, ";"_s);
+    return list.to_string();
   }
 } linkLibraryNode;
 
@@ -2611,7 +2628,7 @@
     list.front() = LG_BEGIN;
     list.push_back(LG_END);
 
-    return cmJoin(list, ";"_s);
+    return list.to_string();
   }
 } linkGroupNode;
 
@@ -2636,7 +2653,7 @@
     }
 
     return context->HeadTarget->IsDeviceLink() ? std::string()
-                                               : cmJoin(parameters, ";");
+                                               : cmList::to_string(parameters);
   }
 } hostLinkNode;
 
@@ -2671,7 +2688,7 @@
       list.insert(list.begin(), static_cast<std::string>(DL_BEGIN));
       list.push_back(static_cast<std::string>(DL_END));
 
-      return cmJoin(list, ";");
+      return list.to_string();
     }
 
     return std::string();
@@ -3108,7 +3125,7 @@
       mf->AddTargetObject(tgtName, o);
     }
 
-    return cmJoin(objects, ";");
+    return objects.to_string();
   }
 } targetObjectsNode;
 
@@ -3168,7 +3185,7 @@
     cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
   {
     std::vector<std::string> dlls = CollectDlls(parameters, context, content);
-    return cmJoin(dlls, ";");
+    return cmList::to_string(dlls);
   }
 } targetRuntimeDllsNode;
 
@@ -3191,7 +3208,7 @@
         dllDirs.push_back(directory);
       }
     }
-    return cmJoin(dllDirs, ";");
+    return cmList::to_string(dllDirs);
   }
 } targetRuntimeDllDirsNode;
 
diff --git a/Source/cmGeneratorExpressionNode.h b/Source/cmGeneratorExpressionNode.h
index f068b02..7a76289 100644
--- a/Source/cmGeneratorExpressionNode.h
+++ b/Source/cmGeneratorExpressionNode.h
@@ -33,6 +33,12 @@
 
   virtual int NumExpectedParameters() const { return 1; }
 
+  virtual bool ShouldEvaluateNextParameter(const std::vector<std::string>&,
+                                           std::string&) const
+  {
+    return true;
+  }
+
   virtual std::string Evaluate(
     const std::vector<std::string>& parameters,
     cmGeneratorExpressionContext* context,
diff --git a/Source/cmGeneratorExpressionParser.h b/Source/cmGeneratorExpressionParser.h
index 63273e4..490de0a 100644
--- a/Source/cmGeneratorExpressionParser.h
+++ b/Source/cmGeneratorExpressionParser.h
@@ -7,10 +7,9 @@
 #include <memory>
 #include <vector>
 
+#include "cmGeneratorExpressionEvaluator.h"
 #include "cmGeneratorExpressionLexer.h"
 
-struct cmGeneratorExpressionEvaluator;
-
 struct cmGeneratorExpressionParser
 {
   cmGeneratorExpressionParser(std::vector<cmGeneratorExpressionToken> tokens);
diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx
index 7d1fcf3..2ea18bd 100644
--- a/Source/cmGeneratorTarget.cxx
+++ b/Source/cmGeneratorTarget.cxx
@@ -13,6 +13,7 @@
 #include <iterator>
 #include <queue>
 #include <sstream>
+#include <type_traits>
 #include <unordered_set>
 #include <utility>
 
@@ -26,9 +27,10 @@
 
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
+#include "cmCryptoHash.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmCxxModuleUsageEffects.h"
 #include "cmEvaluatedTargetProperty.h"
-#include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmFileTimes.h"
 #include "cmGeneratedFileStream.h"
@@ -48,9 +50,11 @@
 #include "cmSourceFileLocation.h"
 #include "cmSourceFileLocationKind.h"
 #include "cmSourceGroup.h"
+#include "cmStandardLevel.h"
 #include "cmStandardLevelResolver.h"
 #include "cmState.h"
 #include "cmStringAlgorithms.h"
+#include "cmSyntheticTargetCache.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetLinkLibraryType.h"
@@ -190,7 +194,7 @@
     }
 
     static std::string filesStr;
-    filesStr = cmJoin(files, ";");
+    filesStr = cmList::to_string(files);
     return filesStr;
   }
 
@@ -322,8 +326,7 @@
     values.push_back(se->GetInput());
   }
   static std::string value;
-  value.clear();
-  value = cmJoin(values, ";");
+  value = cmList::to_string(values);
   return cmValue(value);
 }
 
@@ -794,10 +797,10 @@
       *dirs, lg, config, headTarget, dagChecker, depTgt, language));
   }
 
-  if (depTgt->Target->IsFrameworkOnApple()) {
+  if (depTgt->Target->IsFrameworkOnApple() ||
+      depTgt->IsImportedFrameworkFolderOnApple(config)) {
     if (auto fwDescriptor = depTgt->GetGlobalGenerator()->SplitFrameworkPath(
-          depTgt->GetLocation(config),
-          cmGlobalGenerator::FrameworkFormat::Strict)) {
+          depTgt->GetLocation(config))) {
       result.push_back(fwDescriptor->Directory);
       result.push_back(fwDescriptor->GetFrameworkPath());
     }
@@ -1071,6 +1074,12 @@
   IMPLEMENT_VISIT(SourceKindHeader);
 }
 
+void cmGeneratorTarget::GetCxxModuleSources(
+  std::vector<cmSourceFile const*>& data, const std::string& config) const
+{
+  IMPLEMENT_VISIT(SourceKindCxxModuleSource);
+}
+
 void cmGeneratorTarget::GetExtraSources(std::vector<cmSourceFile const*>& data,
                                         const std::string& config) const
 {
@@ -1224,6 +1233,11 @@
   return this->Target->IsNormal();
 }
 
+bool cmGeneratorTarget::IsRuntimeBinary() const
+{
+  return this->Target->IsRuntimeBinary();
+}
+
 bool cmGeneratorTarget::IsSynthetic() const
 {
   return this->Target->IsSynthetic();
@@ -1278,13 +1292,12 @@
   const std::string& dir, const std::string& config,
   const std::string& language) const
 {
-  assert(this->GetType() != cmStateEnums::INTERFACE_LIBRARY);
   std::string config_upper;
   if (!config.empty()) {
     config_upper = cmSystemTools::UpperCase(config);
   }
 
-  std::string key = cmStrCat(config_upper, "/", language);
+  std::string key = cmStrCat(config_upper, '/', language);
   auto iter = this->SystemIncludesCache.find(key);
 
   if (iter == this->SystemIncludesCache.end()) {
@@ -1493,9 +1506,9 @@
   }
 
   std::string directories;
-  if (const auto* interface = target->GetLinkInterfaceLibraries(
+  if (const auto* link_interface = target->GetLinkInterfaceLibraries(
         config, root, LinkInterfaceFor::Usage)) {
-    for (const cmLinkItem& library : interface->Libraries) {
+    for (const cmLinkItem& library : link_interface->Libraries) {
       if (const cmGeneratorTarget* dependency = library.Target) {
         if (cm::contains(dependency->GetAllConfigCompileLanguages(), lang)) {
           auto* lg = dependency->GetLocalGenerator();
@@ -1948,8 +1961,12 @@
     // Compute the kind (classification) of this source file.
     SourceKind kind;
     std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
+    cmFileSet const* fs = this->GetFileSetForSource(config, sf);
     if (sf->GetCustomCommand()) {
       kind = SourceKindCustomCommand;
+    } else if (!this->Target->IsNormal() && !this->Target->IsImported() &&
+               fs && (fs->GetType() == "CXX_MODULES"_s)) {
+      kind = SourceKindCxxModuleSource;
     } else if (this->Target->GetType() == cmStateEnums::UTILITY ||
                this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY
                // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
@@ -3369,12 +3386,12 @@
 }
 
 void cmGeneratorTarget::AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
-                                                 const std::string& config,
+                                                 std::string const& config,
                                                  std::string& flags) const
 {
-  std::string property = this->GetSafeProperty("CUDA_ARCHITECTURES");
+  std::string arch = this->GetSafeProperty("CUDA_ARCHITECTURES");
 
-  if (property.empty()) {
+  if (arch.empty()) {
     switch (this->GetPolicyStatusCMP0104()) {
       case cmPolicies::WARN:
         if (!this->LocalGenerator->GetCMakeInstance()->GetIsInTryCompile()) {
@@ -3396,48 +3413,60 @@
   }
 
   // If CUDA_ARCHITECTURES is false we don't add any architectures.
-  if (cmIsOff(property)) {
+  if (cmIsOff(arch)) {
     return;
   }
 
-  std::string const& compiler =
-    this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID");
-  const bool ipoEnabled = this->IsIPOEnabled("CUDA", config);
+  return this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "CUDA",
+                                            std::move(arch), flags);
+}
+
+void cmGeneratorTarget::AddCUDAArchitectureFlagsImpl(cmBuildStep compileOrLink,
+                                                     std::string const& config,
+                                                     std::string const& lang,
+                                                     std::string arch,
+                                                     std::string& flags) const
+{
+  std::string const& compiler = this->Makefile->GetSafeDefinition(
+    cmStrCat("CMAKE_", lang, "_COMPILER_ID"));
+  const bool ipoEnabled = this->IsIPOEnabled(lang, config);
 
   // Check for special modes: `all`, `all-major`.
-  if (property == "all" || property == "all-major") {
+  if (arch == "all" || arch == "all-major") {
     if (compiler == "NVIDIA" &&
-        cmSystemTools::VersionCompare(
-          cmSystemTools::OP_GREATER_EQUAL,
-          this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"),
-          "11.5")) {
-      flags = cmStrCat(flags, " -arch=", property);
+        cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
+                                      this->Makefile->GetDefinition(cmStrCat(
+                                        "CMAKE_", lang, "_COMPILER_VERSION")),
+                                      "11.5")) {
+      flags = cmStrCat(flags, " -arch=", arch);
       return;
     }
-    if (property == "all") {
-      property =
-        *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL");
-    } else if (property == "all-major") {
-      property =
-        *this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_ALL_MAJOR");
+    if (arch == "all") {
+      arch = *this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL"));
+    } else if (arch == "all-major") {
+      arch = *this->Makefile->GetDefinition(
+        cmStrCat("CMAKE_", lang, "_ARCHITECTURES_ALL_MAJOR"));
     }
-  } else if (property == "native") {
-    cmValue native =
-      this->Makefile->GetDefinition("CMAKE_CUDA_ARCHITECTURES_NATIVE");
+  } else if (arch == "native") {
+    cmValue native = this->Makefile->GetDefinition(
+      cmStrCat("CMAKE_", lang, "_ARCHITECTURES_NATIVE"));
     if (native.IsEmpty()) {
       this->Makefile->IssueMessage(
         MessageType::FATAL_ERROR,
-        "CUDA_ARCHITECTURES is set to \"native\", but no GPU was detected.");
+        cmStrCat(lang,
+                 "_ARCHITECTURES is set to \"native\", but no NVIDIA GPU was "
+                 "detected."));
     }
     if (compiler == "NVIDIA" &&
-        cmSystemTools::VersionCompare(
-          cmSystemTools::OP_GREATER_EQUAL,
-          this->Makefile->GetDefinition("CMAKE_CUDA_COMPILER_VERSION"),
-          "11.6")) {
-      flags = cmStrCat(flags, " -arch=", property);
+        cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
+                                      this->Makefile->GetDefinition(cmStrCat(
+                                        "CMAKE_", lang, "_COMPILER_VERSION")),
+                                      "11.6")) {
+      flags = cmStrCat(flags, " -arch=", arch);
       return;
     }
-    property = *native;
+    arch = *native;
   }
 
   struct CudaArchitecture
@@ -3449,7 +3478,7 @@
   std::vector<CudaArchitecture> architectures;
 
   {
-    cmList options(property);
+    cmList options(arch);
 
     for (auto& option : options) {
       CudaArchitecture architecture;
@@ -3482,8 +3511,8 @@
 
   if (compiler == "NVIDIA") {
     if (ipoEnabled && compileOrLink == cmBuildStep::Link) {
-      if (cmValue cudaIPOFlags =
-            this->Makefile->GetDefinition("CMAKE_CUDA_LINK_OPTIONS_IPO")) {
+      if (cmValue cudaIPOFlags = this->Makefile->GetDefinition(
+            cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_IPO"))) {
         flags += *cudaIPOFlags;
       }
     }
@@ -3531,10 +3560,10 @@
 
 void cmGeneratorTarget::AddISPCTargetFlags(std::string& flags) const
 {
-  const std::string& property = this->GetSafeProperty("ISPC_INSTRUCTION_SETS");
+  const std::string& arch = this->GetSafeProperty("ISPC_INSTRUCTION_SETS");
 
   // If ISPC_TARGET is false we don't add any architectures.
-  if (cmIsOff(property)) {
+  if (cmIsOff(arch)) {
     return;
   }
 
@@ -3542,29 +3571,36 @@
     this->Makefile->GetSafeDefinition("CMAKE_ISPC_COMPILER_ID");
 
   if (compiler == "Intel") {
-    cmList targets(property);
+    cmList targets(arch);
     if (!targets.empty()) {
       flags += cmStrCat(" --target=", cmWrap("", targets, "", ","));
     }
   }
 }
 
-void cmGeneratorTarget::AddHIPArchitectureFlags(std::string& flags) const
+void cmGeneratorTarget::AddHIPArchitectureFlags(cmBuildStep compileOrLink,
+                                                std::string const& config,
+                                                std::string& flags) const
 {
-  const std::string& property = this->GetSafeProperty("HIP_ARCHITECTURES");
+  std::string arch = this->GetSafeProperty("HIP_ARCHITECTURES");
 
-  if (property.empty()) {
+  if (arch.empty()) {
     this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
                                  "HIP_ARCHITECTURES is empty for target \"" +
                                    this->GetName() + "\".");
   }
 
   // If HIP_ARCHITECTURES is false we don't add any architectures.
-  if (cmIsOff(property)) {
+  if (cmIsOff(arch)) {
     return;
   }
 
-  cmList options(property);
+  if (this->Makefile->GetSafeDefinition("CMAKE_HIP_PLATFORM") == "nvidia") {
+    return this->AddCUDAArchitectureFlagsImpl(compileOrLink, config, "HIP",
+                                              std::move(arch), flags);
+  }
+
+  cmList options(arch);
 
   for (std::string& option : options) {
     flags += " --offload-arch=" + option;
@@ -3828,7 +3864,8 @@
         if (lib.Target == nullptr) {
           libDir = cmSystemTools::CollapseFullPath(
             lib.AsStr(), this->Makefile->GetHomeOutputDirectory());
-        } else if (lib.Target->Target->IsFrameworkOnApple()) {
+        } else if (lib.Target->Target->IsFrameworkOnApple() ||
+                   this->IsImportedFrameworkFolderOnApple(config)) {
           libDir = lib.Target->GetLocation(config);
         } else {
           continue;
@@ -4249,9 +4286,6 @@
         this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
     }
 
-    filename = cmStrCat(
-      generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(), "/");
-
     const std::map<std::string, std::string> languageToExtension = {
       { "C", ".h" },
       { "CXX", ".hxx" },
@@ -4259,8 +4293,7 @@
       { "OBJCXX", ".objcxx.hxx" }
     };
 
-    filename =
-      cmStrCat(filename, "CMakeFiles/", generatorTarget->GetName(), ".dir");
+    filename = generatorTarget->GetSupportDirectory();
 
     if (this->GetGlobalGenerator()->IsMultiConfig()) {
       filename = cmStrCat(filename, "/", config);
@@ -4353,9 +4386,7 @@
         this->GetGlobalGenerator()->FindGeneratorTarget(*pchReuseFrom);
     }
 
-    filename =
-      cmStrCat(generatorTarget->LocalGenerator->GetCurrentBinaryDirectory(),
-               "/CMakeFiles/", generatorTarget->GetName(), ".dir/cmake_pch");
+    filename = cmStrCat(generatorTarget->GetSupportDirectory(), "/cmake_pch");
 
     // For GCC the source extension will be transformed into .h[xx].gch
     if (!this->Makefile->IsOn("CMAKE_LINK_PCH")) {
@@ -4994,10 +5025,44 @@
   }
 }
 
-bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config) const
+cm::optional<cmStandardLevel> cmGeneratorTarget::GetExplicitStandardLevel(
+  std::string const& lang, std::string const& config) const
 {
-  // Compute the language standard based on the compile features.
+  cm::optional<cmStandardLevel> level;
+  std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
+  auto i = this->ExplicitStandardLevel.find(key);
+  if (i != this->ExplicitStandardLevel.end()) {
+    level = i->second;
+  }
+  return level;
+}
+
+void cmGeneratorTarget::UpdateExplicitStandardLevel(std::string const& lang,
+                                                    std::string const& config,
+                                                    cmStandardLevel level)
+{
+  auto e = this->ExplicitStandardLevel.emplace(
+    cmStrCat(cmSystemTools::UpperCase(config), '-', lang), level);
+  if (!e.second && e.first->second < level) {
+    e.first->second = level;
+  }
+}
+
+bool cmGeneratorTarget::ComputeCompileFeatures(std::string const& config)
+{
   cmStandardLevelResolver standardResolver(this->Makefile);
+
+  for (std::string const& lang :
+       this->Makefile->GetState()->GetEnabledLanguages()) {
+    if (cmValue languageStd = this->GetLanguageStandard(lang, config)) {
+      if (cm::optional<cmStandardLevel> langLevel =
+            standardResolver.LanguageStandardLevel(lang, *languageStd)) {
+        this->UpdateExplicitStandardLevel(lang, config, *langLevel);
+      }
+    }
+  }
+
+  // Compute the language standard based on the compile features.
   std::vector<BT<std::string>> features = this->GetCompileFeatures(config);
   for (BT<std::string> const& f : features) {
     std::string lang;
@@ -5009,13 +5074,18 @@
     std::string key = cmStrCat(cmSystemTools::UpperCase(config), '-', lang);
     cmValue currentLanguageStandard = this->GetLanguageStandard(lang, config);
 
+    cm::optional<cmStandardLevel> featureLevel;
     std::string newRequiredStandard;
     if (!standardResolver.GetNewRequiredStandard(
           this->Target->GetName(), f.Value, currentLanguageStandard,
-          newRequiredStandard)) {
+          featureLevel, newRequiredStandard)) {
       return false;
     }
 
+    if (featureLevel) {
+      this->UpdateExplicitStandardLevel(lang, config, *featureLevel);
+    }
+
     if (!newRequiredStandard.empty()) {
       BTs<std::string>& languageStandardProperty =
         this->LanguageStandardMap[key];
@@ -5031,7 +5101,7 @@
 }
 
 bool cmGeneratorTarget::ComputeCompileFeatures(
-  std::string const& config, std::set<LanguagePair> const& languagePairs) const
+  std::string const& config, std::set<LanguagePair> const& languagePairs)
 {
   for (const auto& language : languagePairs) {
     BTs<std::string> const* generatorTargetLanguageStandard =
@@ -5857,7 +5927,7 @@
   static const std::string strNumMax = "COMPATIBLE_INTERFACE_NUMBER_MAX";
 
   for (auto const& dep : deps) {
-    if (!dep.Target) {
+    if (!dep.Target || dep.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
       continue;
     }
 
@@ -8214,6 +8284,96 @@
 }
 }
 
+bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
+                                                 std::string const& config)
+{
+  cmOptionalLinkImplementation impl;
+  this->ComputeLinkImplementationLibraries(config, impl, this,
+                                           LinkInterfaceFor::Link);
+
+  cmCxxModuleUsageEffects usage(this);
+
+  auto& SyntheticDeps = this->Configs[config].SyntheticDeps;
+
+  for (auto const& entry : impl.Libraries) {
+    auto const* gt = entry.Target;
+    if (!gt || !gt->IsImported()) {
+      continue;
+    }
+
+    if (gt->HaveCxx20ModuleSources()) {
+      auto hasher = cmCryptoHash::New("SHA3_512");
+      constexpr size_t HASH_TRUNCATION = 12;
+      auto dirhash = hasher->HashString(
+        gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
+      std::string safeName = gt->GetName();
+      cmSystemTools::ReplaceString(safeName, ":", "_");
+      auto targetIdent =
+        hasher->HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash()));
+      std::string targetName =
+        cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION));
+
+      // Check the cache to see if this instance of the imported target has
+      // already been created.
+      auto cached = cache.CxxModuleTargets.find(targetName);
+      cmGeneratorTarget const* synthDep = nullptr;
+      if (cached == cache.CxxModuleTargets.end()) {
+        auto const* model = gt->Target;
+        auto* mf = gt->Makefile;
+        auto* lg = gt->GetLocalGenerator();
+        auto* tgt = mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY,
+                                             targetName);
+
+        // Copy relevant information from the existing IMPORTED target.
+
+        // Copy policies to the target.
+        tgt->CopyPolicyStatuses(model);
+
+        // Copy file sets.
+        {
+          auto fsNames = model->GetAllFileSetNames();
+          for (auto const& fsName : fsNames) {
+            auto const* fs = model->GetFileSet(fsName);
+            if (!fs) {
+              mf->IssueMessage(MessageType::INTERNAL_ERROR,
+                               cmStrCat("Failed to find file set named '",
+                                        fsName, "' on target '",
+                                        tgt->GetName(), '\''));
+              continue;
+            }
+            auto* newFs = tgt
+                            ->GetOrCreateFileSet(fs->GetName(), fs->GetType(),
+                                                 fs->GetVisibility())
+                            .first;
+            newFs->CopyEntries(fs);
+          }
+        }
+
+        // Copy imported C++ module properties.
+        tgt->CopyImportedCxxModulesEntries(model);
+
+        // Copy other properties which may affect the C++ module BMI
+        // generation.
+        tgt->CopyImportedCxxModulesProperties(model);
+
+        // Apply usage requirements to the target.
+        usage.ApplyToTarget(tgt);
+
+        // Create the generator target and attach it to the local generator.
+        auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg);
+        synthDep = gtp.get();
+        lg->AddGeneratorTarget(std::move(gtp));
+      } else {
+        synthDep = cached->second;
+      }
+
+      SyntheticDeps[gt].push_back(synthDep);
+    }
+  }
+
+  return true;
+}
+
 void cmGeneratorTarget::ComputeLinkImplementationLibraries(
   const std::string& config, cmOptionalLinkImplementation& impl,
   cmGeneratorTarget const* head, LinkInterfaceFor implFor) const
@@ -8221,6 +8381,7 @@
   cmLocalGenerator const* lg = this->LocalGenerator;
   cmMakefile const* mf = lg->GetMakefile();
   cmBTStringRange entryRange = this->Target->GetLinkImplementationEntries();
+  auto const& synthTargetsForConfig = this->Configs[config].SyntheticDeps;
   // Collect libraries directly linked in this configuration.
   for (auto const& entry : entryRange) {
     // Keep this logic in sync with ExpandLinkItems.
@@ -8310,7 +8471,15 @@
       // The entry is meant for this configuration.
       cmLinkItem item =
         this->ResolveLinkItem(BT<std::string>(name, entry.Backtrace), lg);
-      if (!item.Target) {
+      if (item.Target) {
+        auto depsForTarget = synthTargetsForConfig.find(item.Target);
+        if (depsForTarget != synthTargetsForConfig.end()) {
+          for (auto const* depForTarget : depsForTarget->second) {
+            cmLinkItem synthItem(depForTarget, item.Cross, item.Backtrace);
+            impl.Libraries.emplace_back(std::move(synthItem), false);
+          }
+        }
+      } else {
         // Report explicitly linked object files separately.
         std::string const& maybeObj = item.AsStr();
         if (cmSystemTools::FileIsFullPath(maybeObj)) {
@@ -8572,6 +8741,16 @@
   return this->Target->IsFrameworkOnApple();
 }
 
+bool cmGeneratorTarget::IsImportedFrameworkFolderOnApple(
+  const std::string& config) const
+{
+  return this->IsApple() && this->IsImported() &&
+    (this->GetType() == cmStateEnums::STATIC_LIBRARY ||
+     this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+     this->GetType() == cmStateEnums::UNKNOWN_LIBRARY) &&
+    cmSystemTools::IsPathToFramework(this->GetLocation(config));
+}
+
 bool cmGeneratorTarget::IsAppBundleOnApple() const
 {
   return this->Target->IsAppBundleOnApple();
@@ -8864,9 +9043,50 @@
   return filename;
 }
 
+std::string cmGeneratorTarget::GetImportedXcFrameworkPath(
+  const std::string& config) const
+{
+  if (!(this->IsApple() && this->IsImported() &&
+        (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+         this->GetType() == cmStateEnums::STATIC_LIBRARY ||
+         this->GetType() == cmStateEnums::UNKNOWN_LIBRARY))) {
+    return {};
+  }
+
+  std::string desiredConfig = config;
+  if (config.empty()) {
+    desiredConfig = "NOCONFIG";
+  }
+
+  std::string result;
+
+  cmValue loc = nullptr;
+  cmValue imp = nullptr;
+  std::string suffix;
+
+  if (this->Target->GetMappedConfig(desiredConfig, loc, imp, suffix)) {
+    if (loc) {
+      result = *loc;
+    } else {
+      std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
+      if (cmValue configLocation = this->GetProperty(impProp)) {
+        result = *configLocation;
+      } else if (cmValue location = this->GetProperty("IMPORTED_LOCATION")) {
+        result = *location;
+      }
+    }
+
+    if (cmSystemTools::IsPathToXcFramework(result)) {
+      return result;
+    }
+  }
+
+  return {};
+}
+
 bool cmGeneratorTarget::HaveFortranSources(std::string const& config) const
 {
-  auto sources = cmGeneratorTarget::GetSourceFiles(config);
+  auto sources = this->GetSourceFiles(config);
   return std::any_of(sources.begin(), sources.end(),
                      [](BT<cmSourceFile*> const& sf) -> bool {
                        return sf.Value->GetLanguage() == "Fortran"_s;
@@ -8882,24 +9102,28 @@
                      });
 }
 
-bool cmGeneratorTarget::HaveCxx20ModuleSources() const
+bool cmGeneratorTarget::HaveCxx20ModuleSources(std::string* errorMessage) const
 {
   auto const& fs_names = this->Target->GetAllFileSetNames();
-  return std::any_of(fs_names.begin(), fs_names.end(),
-                     [this](std::string const& name) -> bool {
-                       auto const* file_set = this->Target->GetFileSet(name);
-                       if (!file_set) {
-                         this->Makefile->IssueMessage(
-                           MessageType::INTERNAL_ERROR,
-                           cmStrCat("Target \"", this->Target->GetName(),
-                                    "\" is tracked to have file set \"", name,
-                                    "\", but it was not found."));
-                         return false;
-                       }
+  return std::any_of(
+    fs_names.begin(), fs_names.end(),
+    [this, errorMessage](std::string const& name) -> bool {
+      auto const* file_set = this->Target->GetFileSet(name);
+      if (!file_set) {
+        auto message = cmStrCat("Target \"", this->Target->GetName(),
+                                "\" is tracked to have file set \"", name,
+                                "\", but it was not found.");
+        if (errorMessage) {
+          *errorMessage = std::move(message);
+        } else {
+          this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, message);
+        }
+        return false;
+      }
 
-                       auto const& fs_type = file_set->GetType();
-                       return fs_type == "CXX_MODULES"_s;
-                     });
+      auto const& fs_type = file_set->GetType();
+      return fs_type == "CXX_MODULES"_s;
+    });
 }
 
 cmGeneratorTarget::Cxx20SupportLevel cmGeneratorTarget::HaveCxxModuleSupport(
@@ -8909,57 +9133,112 @@
   if (!state->GetLanguageEnabled("CXX")) {
     return Cxx20SupportLevel::MissingCxx;
   }
+
   cmValue standardDefault =
-    this->Target->GetMakefile()->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
-  if (standardDefault && !standardDefault->empty()) {
-    cmStandardLevelResolver standardResolver(this->Makefile);
-    if (!standardResolver.HaveStandardAvailable(this, "CXX", config,
-                                                "cxx_std_20")) {
-      return Cxx20SupportLevel::NoCxx20;
-    }
+    this->Makefile->GetDefinition("CMAKE_CXX_STANDARD_DEFAULT");
+  if (!standardDefault || standardDefault->empty()) {
+    // We do not know any meaningful C++ standard levels for this compiler.
+    return Cxx20SupportLevel::NoCxx20;
   }
-  // Else, an empty CMAKE_CXX_STANDARD_DEFAULT means CMake does not detect and
-  // set a default standard level for this compiler, so assume all standards
-  // are available.
-  if (!cmExperimental::HasSupportEnabled(
-        *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi)) {
-    return Cxx20SupportLevel::MissingExperimentalFlag;
+
+  cmStandardLevelResolver standardResolver(this->Makefile);
+  cmStandardLevel const cxxStd20 =
+    *standardResolver.LanguageStandardLevel("CXX", "20");
+  cm::optional<cmStandardLevel> explicitLevel =
+    this->GetExplicitStandardLevel("CXX", config);
+  if (!explicitLevel || *explicitLevel < cxxStd20) {
+    return Cxx20SupportLevel::NoCxx20;
+  }
+
+  cmValue scandepRule =
+    this->Makefile->GetDefinition("CMAKE_CXX_SCANDEP_SOURCE");
+  if (!scandepRule) {
+    return Cxx20SupportLevel::MissingRule;
   }
   return Cxx20SupportLevel::Supported;
 }
 
 void cmGeneratorTarget::CheckCxxModuleStatus(std::string const& config) const
 {
+  bool haveScannableSources = false;
+
   // Check for `CXX_MODULE*` file sets and a lack of support.
   if (this->HaveCxx20ModuleSources()) {
-    switch (this->HaveCxxModuleSupport(config)) {
-      case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
-        this->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat("The \"", this->GetName(),
-                   "\" target has C++ module sources but the \"CXX\" language "
-                   "has not been enabled"));
-        break;
-      case cmGeneratorTarget::Cxx20SupportLevel::MissingExperimentalFlag:
-        this->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat("The \"", this->GetName(),
-                   "\" target has C++ module sources but its experimental "
-                   "support has not been requested"));
-        break;
-      case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20:
-        this->Makefile->IssueMessage(
-          MessageType::FATAL_ERROR,
-          cmStrCat(
-            "The \"", this->GetName(),
-            "\" target has C++ module sources but is not using at least "
-            "\"cxx_std_20\""));
-        break;
-      case cmGeneratorTarget::Cxx20SupportLevel::Supported:
-        // All is well.
-        break;
+    haveScannableSources = true;
+  }
+
+  if (!haveScannableSources) {
+    // Check to see if there are regular sources that have requested scanning.
+    auto sources = this->GetSourceFiles(config);
+    for (auto const& source : sources) {
+      auto const* sf = source.Value;
+      auto const& lang = sf->GetLanguage();
+      if (lang != "CXX"_s) {
+        continue;
+      }
+      // Ignore sources which do not need dyndep.
+      if (this->NeedDyndepForSource(lang, config, sf)) {
+        haveScannableSources = true;
+      }
     }
   }
+
+  // If there isn't anything scannable, ignore it.
+  if (!haveScannableSources) {
+    return;
+  }
+
+  // If the generator doesn't support modules at all, error that we have
+  // sources that require the support.
+  if (!this->GetGlobalGenerator()->CheckCxxModuleSupport()) {
+    this->Makefile->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(
+        "The target named \"", this->GetName(),
+        "\" has C++ sources that may use modules, but modules are not "
+        "supported by this generator.  See the cmake-cxxmodules(7) manual "
+        "and the CMAKE_CXX_SCAN_FOR_MODULES variable."));
+    return;
+  }
+
+  switch (this->HaveCxxModuleSupport(config)) {
+    case cmGeneratorTarget::Cxx20SupportLevel::MissingCxx:
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("The target named \"", this->GetName(),
+                 "\" has C++ sources that use modules, but the \"CXX\" "
+                 "language has not been enabled."));
+      break;
+    case cmGeneratorTarget::Cxx20SupportLevel::NoCxx20: {
+      cmStandardLevelResolver standardResolver(this->Makefile);
+      auto effStandard =
+        standardResolver.GetEffectiveStandard(this, "CXX", config);
+      if (effStandard.empty()) {
+        effStandard = "; no C++ standard found";
+      } else {
+        effStandard = cmStrCat("; found \"cxx_std_", effStandard, '"');
+      }
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat(
+          "The target named \"", this->GetName(),
+          "\" has C++ sources that use modules, but does not include "
+          "\"cxx_std_20\" (or newer) among its `target_compile_features`",
+          effStandard, '.'));
+    } break;
+    case cmGeneratorTarget::Cxx20SupportLevel::MissingRule: {
+      this->Makefile->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("The target named \"", this->GetName(),
+                 "\" has C++ sources that may use modules, but the compiler "
+                 "does not provide a way to discover the import graph "
+                 "dependencies.  See the cmake-cxxmodules(7) manual "
+                 "and the CMAKE_CXX_SCAN_FOR_MODULES variable."));
+    } break;
+    case cmGeneratorTarget::Cxx20SupportLevel::Supported:
+      // All is well.
+      break;
+  }
 }
 
 bool cmGeneratorTarget::NeedCxxModuleSupport(std::string const& lang,
@@ -8997,14 +9276,30 @@
                                             std::string const& config,
                                             cmSourceFile const* sf) const
 {
-  bool const needDyndep = this->NeedDyndep(lang, config);
-  if (!needDyndep) {
+  // Fortran always needs to be scanned.
+  if (lang == "Fortran"_s) {
+    return true;
+  }
+  // Only C++ code needs scanned otherwise.
+  if (lang != "CXX"_s) {
     return false;
   }
+
+  // Any file in `CXX_MODULES` file sets need scanned (it being `CXX` is
+  // enforced elsewhere).
   auto const* fs = this->GetFileSetForSource(config, sf);
   if (fs && fs->GetType() == "CXX_MODULES"_s) {
     return true;
   }
+
+  switch (this->HaveCxxModuleSupport(config)) {
+    case Cxx20SupportLevel::MissingCxx:
+    case Cxx20SupportLevel::NoCxx20:
+      return false;
+    case Cxx20SupportLevel::MissingRule:
+    case Cxx20SupportLevel::Supported:
+      break;
+  }
   auto const sfProp = sf->GetProperty("CXX_SCAN_FOR_MODULES");
   if (sfProp.IsSet()) {
     return sfProp.IsOn();
@@ -9013,7 +9308,22 @@
   if (tgtProp.IsSet()) {
     return tgtProp.IsOn();
   }
-  return true;
+
+  bool policyAnswer = false;
+  switch (this->GetPolicyStatusCMP0155()) {
+    case cmPolicies::WARN:
+    case cmPolicies::OLD:
+      // The OLD behavior is to not scan the source.
+      policyAnswer = false;
+      break;
+    case cmPolicies::REQUIRED_ALWAYS:
+    case cmPolicies::REQUIRED_IF_USED:
+    case cmPolicies::NEW:
+      // The NEW behavior is to scan the source.
+      policyAnswer = true;
+      break;
+  }
+  return policyAnswer;
 }
 
 void cmGeneratorTarget::BuildFileSetInfoCache(std::string const& config) const
diff --git a/Source/cmGeneratorTarget.h b/Source/cmGeneratorTarget.h
index dca69fd..bf49914 100644
--- a/Source/cmGeneratorTarget.h
+++ b/Source/cmGeneratorTarget.h
@@ -17,20 +17,22 @@
 #include <cm/optional>
 
 #include "cmAlgorithms.h"
+#include "cmComputeLinkInformation.h"
 #include "cmLinkItem.h"
 #include "cmListFileCache.h"
 #include "cmPolicies.h"
+#include "cmStandardLevel.h"
 #include "cmStateTypes.h"
 #include "cmValue.h"
 
 enum class cmBuildStep;
-class cmComputeLinkInformation;
 class cmCustomCommand;
 class cmFileSet;
 class cmGlobalGenerator;
 class cmLocalGenerator;
 class cmMakefile;
 class cmSourceFile;
+struct cmSyntheticTargetCache;
 class cmTarget;
 
 struct cmGeneratorExpressionContext;
@@ -51,6 +53,7 @@
 
   bool IsInBuildSystem() const;
   bool IsNormal() const;
+  bool IsRuntimeBinary() const;
   bool IsSynthetic() const;
   bool IsImported() const;
   bool IsImportedGloballyVisible() const;
@@ -115,6 +118,7 @@
     SourceKindCertificate,
     SourceKindCustomCommand,
     SourceKindExternalObject,
+    SourceKindCxxModuleSource,
     SourceKindExtra,
     SourceKindHeader,
     SourceKindIDL,
@@ -185,6 +189,8 @@
                           const std::string& config) const;
   void GetHeaderSources(std::vector<cmSourceFile const*>&,
                         const std::string& config) const;
+  void GetCxxModuleSources(std::vector<cmSourceFile const*>&,
+                           const std::string& config) const;
   void GetExtraSources(std::vector<cmSourceFile const*>&,
                        const std::string& config) const;
   void GetCustomCommands(std::vector<cmSourceFile const*>&,
@@ -491,11 +497,17 @@
                                 cmSourceFile const& sf) const;
 
   void AddCUDAArchitectureFlags(cmBuildStep compileOrLink,
-                                const std::string& config,
+                                std::string const& config,
                                 std::string& flags) const;
+  void AddCUDAArchitectureFlagsImpl(cmBuildStep compileOrLink,
+                                    std::string const& config,
+                                    std::string const& lang, std::string arch,
+                                    std::string& flags) const;
   void AddCUDAToolkitFlags(std::string& flags) const;
 
-  void AddHIPArchitectureFlags(std::string& flags) const;
+  void AddHIPArchitectureFlags(cmBuildStep compileOrLink,
+                               std::string const& config,
+                               std::string& flags) const;
 
   void AddISPCTargetFlags(std::string& flags) const;
 
@@ -602,12 +614,11 @@
   /** Add the target output files to the global generator manifest.  */
   void ComputeTargetManifest(const std::string& config) const;
 
-  bool ComputeCompileFeatures(std::string const& config) const;
+  bool ComputeCompileFeatures(std::string const& config);
 
   using LanguagePair = std::pair<std::string, std::string>;
-  bool ComputeCompileFeatures(
-    std::string const& config,
-    std::set<LanguagePair> const& languagePairs) const;
+  bool ComputeCompileFeatures(std::string const& config,
+                              std::set<LanguagePair> const& languagePairs);
 
   /**
    * Trace through the source files in this target and add al source files
@@ -812,6 +823,10 @@
       Apple.  */
   bool IsFrameworkOnApple() const;
 
+  /** Return whether this target is an IMPORTED library target on Apple
+      with a .framework folder as its location.  */
+  bool IsImportedFrameworkFolderOnApple(const std::string& config) const;
+
   /** Return whether this target is an executable Bundle on Apple.  */
   bool IsAppBundleOnApple() const;
 
@@ -922,6 +937,11 @@
     cmSourceFile& source, const std::string& dir,
     cm::optional<std::set<std::string>>& languages) const;
 
+  std::string GetImportedXcFrameworkPath(const std::string& config) const;
+
+  bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
+                                std::string const& config);
+
 private:
   void AddSourceCommon(const std::string& src, bool before = false);
 
@@ -1072,6 +1092,7 @@
     std::string SharedDeps;
   };
 
+  friend cmComputeLinkInformation;
   using ImportInfoMapType = std::map<std::string, ImportInfo>;
   mutable ImportInfoMapType ImportInfoMap;
   void ComputeImportInfo(std::string const& desired_config,
@@ -1220,7 +1241,14 @@
   bool GetRPATH(const std::string& config, const std::string& prop,
                 std::string& rpath) const;
 
-  mutable std::map<std::string, BTs<std::string>> LanguageStandardMap;
+  std::map<std::string, BTs<std::string>> LanguageStandardMap;
+
+  cm::optional<cmStandardLevel> GetExplicitStandardLevel(
+    std::string const& lang, std::string const& config) const;
+  void UpdateExplicitStandardLevel(std::string const& lang,
+                                   std::string const& config,
+                                   cmStandardLevel level);
+  std::map<std::string, cmStandardLevel> ExplicitStandardLevel;
 
   cmValue GetPropertyWithPairedLanguageSupport(std::string const& lang,
                                                const char* suffix) const;
@@ -1257,17 +1285,20 @@
    *
    * This will inspect the target itself to see if C++20 module
    * support is expected to work based on its sources.
+   *
+   * If `errorMessage` is given a non-`nullptr`, any error message will be
+   * stored in it, otherwise the error will be reported directly.
    */
-  bool HaveCxx20ModuleSources() const;
+  bool HaveCxx20ModuleSources(std::string* errorMessage = nullptr) const;
 
   enum class Cxx20SupportLevel
   {
     // C++ is not available.
     MissingCxx,
-    // The experimental feature is not available.
-    MissingExperimentalFlag,
     // The target does not require at least C++20.
     NoCxx20,
+    // C++20 module scanning rules are not present.
+    MissingRule,
     // C++20 modules are available and working.
     Supported,
   };
@@ -1294,6 +1325,8 @@
   {
     bool BuiltFileSetCache = false;
     std::map<std::string, cmFileSet const*> FileSetCache;
+    std::map<cmGeneratorTarget const*, std::vector<cmGeneratorTarget const*>>
+      SyntheticDeps;
   };
   mutable std::map<std::string, InfoByConfig> Configs;
 };
diff --git a/Source/cmGetCMakePropertyCommand.cxx b/Source/cmGetCMakePropertyCommand.cxx
index 42bd206..d2c4fd6 100644
--- a/Source/cmGetCMakePropertyCommand.cxx
+++ b/Source/cmGetCMakePropertyCommand.cxx
@@ -6,9 +6,9 @@
 
 #include "cmExecutionStatus.h"
 #include "cmGlobalGenerator.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmState.h"
-#include "cmStringAlgorithms.h"
 #include "cmValue.h"
 
 // cmGetCMakePropertyCommand
@@ -35,7 +35,7 @@
   } else if (args[1] == "COMPONENTS") {
     const std::set<std::string>* components =
       status.GetMakefile().GetGlobalGenerator()->GetInstallComponents();
-    output = cmJoin(*components, ";");
+    output = cmList::to_string(*components);
   } else {
     cmValue prop = nullptr;
     if (!args[1].empty()) {
diff --git a/Source/cmGetPropertyCommand.cxx b/Source/cmGetPropertyCommand.cxx
index 943ce1d..880756d 100644
--- a/Source/cmGetPropertyCommand.cxx
+++ b/Source/cmGetPropertyCommand.cxx
@@ -49,7 +49,8 @@
                       bool source_file_paths_should_be_absolute);
 bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
                     OutType infoType, const std::string& variable,
-                    const std::string& propertyName);
+                    const std::string& propertyName,
+                    cmMakefile& directory_makefile);
 bool HandleVariableMode(cmExecutionStatus& status, const std::string& name,
                         OutType infoType, const std::string& variable,
                         const std::string& propertyName);
@@ -81,6 +82,9 @@
   bool source_file_directory_option_enabled = false;
   bool source_file_target_option_enabled = false;
 
+  std::string test_directory;
+  bool test_directory_option_enabled = false;
+
   // Get the scope from which to get the property.
   cmProperty::ScopeType scope;
   if (args[1] == "GLOBAL") {
@@ -116,7 +120,8 @@
     DoingProperty,
     DoingType,
     DoingSourceDirectory,
-    DoingSourceTargetDirectory
+    DoingSourceTargetDirectory,
+    DoingTestDirectory,
   };
   Doing doing = DoingName;
   for (unsigned int i = 2; i < args.size(); ++i) {
@@ -145,12 +150,19 @@
                args[i] == "TARGET_DIRECTORY") {
       doing = DoingSourceTargetDirectory;
       source_file_target_option_enabled = true;
+    } else if (doing == DoingNone && scope == cmProperty::TEST &&
+               args[i] == "DIRECTORY") {
+      doing = DoingTestDirectory;
+      test_directory_option_enabled = true;
     } else if (doing == DoingSourceDirectory) {
       source_file_directories.push_back(args[i]);
       doing = DoingNone;
     } else if (doing == DoingSourceTargetDirectory) {
       source_file_target_directories.push_back(args[i]);
       doing = DoingNone;
+    } else if (doing == DoingTestDirectory) {
+      test_directory = args[i];
+      doing = DoingNone;
     } else if (doing == DoingProperty) {
       doing = DoingNone;
       propertyName = args[i];
@@ -167,12 +179,17 @@
   }
 
   std::vector<cmMakefile*> source_file_directory_makefiles;
-  bool file_scopes_handled =
+  bool source_file_scopes_handled =
     SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
       status, source_file_directory_option_enabled,
       source_file_target_option_enabled, source_file_directories,
       source_file_target_directories, source_file_directory_makefiles);
-  if (!file_scopes_handled) {
+  cmMakefile* test_directory_makefile;
+  bool test_scopes_handled =
+    SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
+      status, test_directory_option_enabled, test_directory,
+      test_directory_makefile);
+  if (!(source_file_scopes_handled && test_scopes_handled)) {
     return false;
   }
 
@@ -231,7 +248,8 @@
                                 directory_scope_mf,
                                 source_file_paths_should_be_absolute);
       case cmProperty::TEST:
-        return HandleTestMode(status, name, infoType, variable, propertyName);
+        return HandleTestMode(status, name, infoType, variable, propertyName,
+                              *test_directory_makefile);
       case cmProperty::VARIABLE:
         return HandleVariableMode(status, name, infoType, variable,
                                   propertyName);
@@ -404,7 +422,7 @@
 
 bool HandleTestMode(cmExecutionStatus& status, const std::string& name,
                     OutType infoType, const std::string& variable,
-                    const std::string& propertyName)
+                    const std::string& propertyName, cmMakefile& test_makefile)
 {
   if (name.empty()) {
     status.SetError("not given name for TEST scope.");
@@ -412,7 +430,7 @@
   }
 
   // Loop over all tests looking for matching names.
-  if (cmTest* test = status.GetMakefile().GetTest(name)) {
+  if (cmTest* test = test_makefile.GetTest(name)) {
     return StoreResult(infoType, status.GetMakefile(), variable,
                        test->GetProperty(propertyName));
   }
diff --git a/Source/cmGetTestPropertyCommand.cxx b/Source/cmGetTestPropertyCommand.cxx
index 36446c9..103471b 100644
--- a/Source/cmGetTestPropertyCommand.cxx
+++ b/Source/cmGetTestPropertyCommand.cxx
@@ -4,21 +4,42 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmSetPropertyCommand.h"
 #include "cmTest.h"
 #include "cmValue.h"
 
 bool cmGetTestPropertyCommand(std::vector<std::string> const& args,
                               cmExecutionStatus& status)
 {
-  if (args.size() < 3) {
+  std::vector<std::string>::size_type args_size = args.size();
+  if (args_size != 3 && args_size != 5) {
     status.SetError("called with incorrect number of arguments");
     return false;
   }
 
+  std::string test_directory;
+  bool test_directory_option_enabled = false;
+
+  int var_arg_index = 2;
+  if (args[2] == "DIRECTORY" && args_size == 5) {
+    var_arg_index = 4;
+    test_directory_option_enabled = true;
+    test_directory = args[3];
+  }
+
+  cmMakefile* test_directory_makefile = &status.GetMakefile();
+  bool file_scopes_handled =
+    SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
+      status, test_directory_option_enabled, test_directory,
+      test_directory_makefile);
+  if (!file_scopes_handled) {
+    return false;
+  }
+
   std::string const& testName = args[0];
-  std::string const& var = args[2];
+  std::string const& var = args[var_arg_index];
   cmMakefile& mf = status.GetMakefile();
-  cmTest* test = mf.GetTest(testName);
+  cmTest* test = test_directory_makefile->GetTest(testName);
   if (test) {
     cmValue prop;
     if (!args[1].empty()) {
diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx
index 2d35b8f..95e2187 100644
--- a/Source/cmGhsMultiTargetGenerator.cxx
+++ b/Source/cmGhsMultiTargetGenerator.cxx
@@ -6,6 +6,7 @@
 #include <memory>
 #include <ostream>
 #include <set>
+#include <type_traits>
 #include <utility>
 #include <vector>
 
diff --git a/Source/cmGlobalBorlandMakefileGenerator.h b/Source/cmGlobalBorlandMakefileGenerator.h
index 049d6ba..a2adbd0 100644
--- a/Source/cmGlobalBorlandMakefileGenerator.h
+++ b/Source/cmGlobalBorlandMakefileGenerator.h
@@ -54,6 +54,8 @@
   bool AllowDeleteOnError() const override { return false; }
   bool CanEscapeOctothorpe() const override { return true; }
 
+  bool IsGNUMakeJobServerAware() const override { return false; }
+
 protected:
   std::vector<GeneratedMakeCommand> GenerateBuildCommand(
     const std::string& makeProgram, const std::string& projectName,
diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx
index 5175aae..933e754 100644
--- a/Source/cmGlobalGenerator.cxx
+++ b/Source/cmGlobalGenerator.cxx
@@ -13,6 +13,7 @@
 #include <iomanip>
 #include <iterator>
 #include <sstream>
+#include <type_traits>
 #include <utility>
 
 #include <cm/memory>
@@ -24,13 +25,12 @@
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#  include <windows.h>
-#endif
+#include "cm_codecvt_Encoding.hxx"
 
 #include "cmAlgorithms.h"
 #include "cmCPackPropertiesGenerator.h"
 #include "cmComputeTargetDepends.h"
+#include "cmCryptoHash.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
 #include "cmDuration.h"
@@ -55,6 +55,7 @@
 #include "cmStateDirectory.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmSyntheticTargetCache.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
 #include "cmVersion.h"
@@ -65,7 +66,6 @@
 #  include <cm3p/json/value.h>
 #  include <cm3p/json/writer.h>
 
-#  include "cmCryptoHash.h"
 #  include "cmQtAutoGenGlobalInitializer.h"
 #endif
 
@@ -142,6 +142,10 @@
 {
   this->ClearGeneratorMembers();
 }
+codecvt_Encoding cmGlobalGenerator::GetMakefileEncoding() const
+{
+  return codecvt_Encoding::None;
+}
 
 #if !defined(CMAKE_BOOTSTRAP)
 Json::Value cmGlobalGenerator::GetJson() const
@@ -239,7 +243,8 @@
 
   if (!mf->GetDefinition(langComp)) {
     if (!optional) {
-      cmSystemTools::Error(langComp + " not set, after EnableLanguage");
+      cmSystemTools::Error(
+        cmStrCat(langComp, " not set, after EnableLanguage"));
     }
     return;
   }
@@ -636,11 +641,10 @@
 #if defined(_WIN32) && !defined(__CYGWIN__)
     cmSystemTools::WindowsVersion windowsVersion =
       cmSystemTools::GetWindowsVersion();
-    std::ostringstream windowsVersionString;
-    windowsVersionString << windowsVersion.dwMajorVersion << "."
-                         << windowsVersion.dwMinorVersion << "."
-                         << windowsVersion.dwBuildNumber;
-    mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString.str());
+    auto windowsVersionString = cmStrCat(windowsVersion.dwMajorVersion, '.',
+                                         windowsVersion.dwMinorVersion, '.',
+                                         windowsVersion.dwBuildNumber);
+    mf->AddDefinition("CMAKE_HOST_SYSTEM_VERSION", windowsVersionString);
 #endif
     // Read the DetermineSystem file
     std::string systemFile = mf->GetModulesFile("CMakeDetermineSystem.cmake");
@@ -695,22 +699,22 @@
         setupFile, mf->GetCurrentSourceDirectory());
       if (!cmSystemTools::FileExists(absSetupFile)) {
         cmSystemTools::Error(
-          "CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: " +
-          setupFile);
+          cmStrCat("CMAKE_PROJECT_TOP_LEVEL_INCLUDES file does not exist: ",
+                   setupFile));
         mf->GetState()->SetInTopLevelIncludes(false);
         return;
       }
       if (cmSystemTools::FileIsDirectory(absSetupFile)) {
         cmSystemTools::Error(
-          "CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: " +
-          setupFile);
+          cmStrCat("CMAKE_PROJECT_TOP_LEVEL_INCLUDES file is a directory: ",
+                   setupFile));
         mf->GetState()->SetInTopLevelIncludes(false);
         return;
       }
       if (!mf->ReadListFile(absSetupFile)) {
         cmSystemTools::Error(
-          "Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: " +
-          setupFile);
+          cmStrCat("Failed reading CMAKE_PROJECT_TOP_LEVEL_INCLUDES file: ",
+                   setupFile));
         mf->GetState()->SetInTopLevelIncludes(false);
         return;
       }
@@ -754,7 +758,8 @@
       // to avoid duplicate compiler tests.
       if (cmSystemTools::FileExists(fpath)) {
         if (!mf->ReadListFile(fpath)) {
-          cmSystemTools::Error("Could not find cmake module file: " + fpath);
+          cmSystemTools::Error(
+            cmStrCat("Could not find cmake module file: ", fpath));
         }
         // if this file was found then the language was already determined
         // to be working
@@ -778,8 +783,8 @@
         cmStrCat("CMakeDetermine", lang, "Compiler.cmake");
       std::string determineFile = mf->GetModulesFile(determineCompiler);
       if (!mf->ReadListFile(determineFile)) {
-        cmSystemTools::Error("Could not find cmake module file: " +
-                             determineCompiler);
+        cmSystemTools::Error(
+          cmStrCat("Could not find cmake module file: ", determineCompiler));
       }
       if (cmSystemTools::GetFatalErrorOccurred()) {
         return;
@@ -807,7 +812,8 @@
       // configures CMake(LANG)Compiler.cmake
       fpath = cmStrCat(rootBin, "/CMake", lang, "Compiler.cmake");
       if (!mf->ReadListFile(fpath)) {
-        cmSystemTools::Error("Could not find cmake module file: " + fpath);
+        cmSystemTools::Error(
+          cmStrCat("Could not find cmake module file: ", fpath));
       }
       this->SetLanguageEnabledFlag(lang, mf);
       needSetLanguageEnabledMaps[lang] = true;
@@ -890,10 +896,11 @@
       fpath = cmStrCat("CMake", lang, "Information.cmake");
       std::string informationFile = mf->GetModulesFile(fpath);
       if (informationFile.empty()) {
-        cmSystemTools::Error("Could not find cmake module file: " + fpath);
+        cmSystemTools::Error(
+          cmStrCat("Could not find cmake module file: ", fpath));
       } else if (!mf->ReadListFile(informationFile)) {
-        cmSystemTools::Error("Could not process cmake module file: " +
-                             informationFile);
+        cmSystemTools::Error(
+          cmStrCat("Could not process cmake module file: ", informationFile));
       }
     }
     if (needSetLanguageEnabledMaps[lang]) {
@@ -911,8 +918,8 @@
         std::string testLang = cmStrCat("CMakeTest", lang, "Compiler.cmake");
         std::string ifpath = mf->GetModulesFile(testLang);
         if (!mf->ReadListFile(ifpath)) {
-          cmSystemTools::Error("Could not find cmake module file: " +
-                               testLang);
+          cmSystemTools::Error(
+            cmStrCat("Could not find cmake module file: ", testLang));
         }
         std::string compilerWorks =
           cmStrCat("CMAKE_", lang, "_COMPILER_WORKS");
@@ -977,7 +984,7 @@
 void cmGlobalGenerator::CheckCompilerIdCompatibility(
   cmMakefile* mf, std::string const& lang) const
 {
-  std::string compilerIdVar = "CMAKE_" + lang + "_COMPILER_ID";
+  std::string compilerIdVar = cmStrCat("CMAKE_", lang, "_COMPILER_ID");
   std::string const compilerId = mf->GetSafeDefinition(compilerIdVar);
 
   if (compilerId == "AppleClang") {
@@ -1105,9 +1112,9 @@
         }
         {
           // Fix compiler versions.
-          std::string version = "CMAKE_" + lang + "_COMPILER_VERSION";
-          std::string emulated = "CMAKE_" + lang + "_SIMULATE_VERSION";
-          std::string emulatedId = "CMAKE_" + lang + "_SIMULATE_ID";
+          std::string version = cmStrCat("CMAKE_", lang, "_COMPILER_VERSION");
+          std::string emulated = cmStrCat("CMAKE_", lang, "_SIMULATE_VERSION");
+          std::string emulatedId = cmStrCat("CMAKE_", lang, "_SIMULATE_ID");
           std::string const& actual = mf->GetRequiredDefinition(emulated);
           mf->AddDefinition(version, actual);
           mf->RemoveDefinition(emulatedId);
@@ -1208,7 +1215,7 @@
     return;
   }
 
-  std::string linkerPrefVar = "CMAKE_" + l + "_LINKER_PREFERENCE";
+  std::string linkerPrefVar = cmStrCat("CMAKE_", l, "_LINKER_PREFERENCE");
   cmValue linkerPref = mf->GetDefinition(linkerPrefVar);
   int preference = 0;
   if (cmNonempty(linkerPref)) {
@@ -1234,7 +1241,7 @@
 
   this->LanguageToLinkerPreference[l] = preference;
 
-  std::string outputExtensionVar = "CMAKE_" + l + "_OUTPUT_EXTENSION";
+  std::string outputExtensionVar = cmStrCat("CMAKE_", l, "_OUTPUT_EXTENSION");
   if (cmValue p = mf->GetDefinition(outputExtensionVar)) {
     std::string outputExtension = *p;
     this->LanguageToOutputExtension[l] = outputExtension;
@@ -1251,7 +1258,7 @@
   this->FillExtensionToLanguageMap(l, mf);
 
   std::string ignoreExtensionsVar =
-    std::string("CMAKE_") + std::string(l) + std::string("_IGNORE_EXTENSIONS");
+    cmStrCat("CMAKE_", l, "_IGNORE_EXTENSIONS");
   std::string ignoreExts = mf->GetSafeDefinition(ignoreExtensionsVar);
   cmList extensionList{ ignoreExts };
   for (std::string const& i : extensionList) {
@@ -1262,8 +1269,7 @@
 void cmGlobalGenerator::FillExtensionToLanguageMap(const std::string& l,
                                                    cmMakefile* mf)
 {
-  std::string extensionsVar = std::string("CMAKE_") + std::string(l) +
-    std::string("_SOURCE_FILE_EXTENSIONS");
+  std::string extensionsVar = cmStrCat("CMAKE_", l, "_SOURCE_FILE_EXTENSIONS");
   const std::string& exts = mf->GetSafeDefinition(extensionsVar);
   cmList extensionList{ exts };
   for (std::string const& i : extensionList) {
@@ -1555,6 +1561,17 @@
   }
 #endif
 
+  // Iterate through all targets and set up C++20 module targets.
+  // Create target templates for each imported target with C++20 modules.
+  // INTERFACE library with BMI-generating rules and a collation step?
+  // Maybe INTERFACE libraries with modules files should just do BMI-only?
+  // Make `add_dependencies(imported_target
+  // $<$<TARGET_NAME_IF_EXISTS:uses_imported>:synth1>
+  // $<$<TARGET_NAME_IF_EXISTS:other_uses_imported>:synth2>)`
+  if (!this->DiscoverSyntheticTargets()) {
+    return false;
+  }
+
   // Add generator specific helper commands
   for (const auto& localGen : this->LocalGenerators) {
     localGen->AddHelperCommands();
@@ -1779,6 +1796,34 @@
   entry->second = index++;
 }
 
+bool cmGlobalGenerator::DiscoverSyntheticTargets()
+{
+  cmSyntheticTargetCache cache;
+
+  for (auto const& gen : this->LocalGenerators) {
+    // Because DiscoverSyntheticTargets() adds generator targets, we need to
+    // cache the existing list of generator targets before starting.
+    std::vector<cmGeneratorTarget*> genTargets;
+    genTargets.reserve(gen->GetGeneratorTargets().size());
+    for (auto const& tgt : gen->GetGeneratorTargets()) {
+      genTargets.push_back(tgt.get());
+    }
+
+    for (auto* tgt : genTargets) {
+      std::vector<std::string> const& configs =
+        tgt->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+
+      for (auto const& config : configs) {
+        if (!tgt->DiscoverSyntheticTargets(cache, config)) {
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
 bool cmGlobalGenerator::AddHeaderSetVerification()
 {
   for (auto const& gen : this->LocalGenerators) {
@@ -1886,7 +1931,7 @@
     std::set<std::string> standardIncludesSet;
     for (std::string const& li : langs) {
       std::string const standardIncludesVar =
-        "CMAKE_" + li + "_STANDARD_INCLUDE_DIRECTORIES";
+        cmStrCat("CMAKE_", li, "_STANDARD_INCLUDE_DIRECTORIES");
       std::string const& standardIncludesStr =
         mf->GetSafeDefinition(standardIncludesVar);
       cmList standardIncludesList{ standardIncludesStr };
@@ -2017,13 +2062,14 @@
     for (auto const& notFound : notFoundMap) {
       notFoundVars += notFound.first;
       notFoundVars += notFound.second;
-      notFoundVars += "\n";
+      notFoundVars += '\n';
     }
-    cmSystemTools::Error("The following variables are used in this project, "
-                         "but they are set to NOTFOUND.\n"
-                         "Please set them or make sure they are set and "
-                         "tested correctly in the CMake files:\n" +
-                         notFoundVars);
+    cmSystemTools::Error(
+      cmStrCat("The following variables are used in this project, "
+               "but they are set to NOTFOUND.\n"
+               "Please set them or make sure they are set and "
+               "tested correctly in the CMake files:\n",
+               notFoundVars));
   }
 }
 
@@ -2181,8 +2227,8 @@
                                          outputflag, timeout)) {
       cmSystemTools::SetRunCommandHideConsole(hideconsole);
       cmSystemTools::Error(
-        "Generator: execution of make failed. Make command was: " +
-        makeCommandStr);
+        cmStrCat("Generator: execution of make failed. Make command was: ",
+                 makeCommandStr));
       ostr << *outputPtr
            << "\nGenerator: execution of make failed. Make command was: "
            << outputMakeCommandStr << std::endl;
@@ -2644,13 +2690,9 @@
   return cm::nullopt;
 }
 
-bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
-                                     std::string const& reason) const
+static bool RaiseCMP0037Message(cmake* cm, cmTarget* tgt,
+                                std::string const& reason)
 {
-  cmTarget* tgt = this->FindTarget(targetName);
-  if (!tgt) {
-    return true;
-  }
   MessageType messageType = MessageType::AUTHOR_WARNING;
   std::ostringstream e;
   bool issueMessage = false;
@@ -2669,13 +2711,12 @@
       break;
   }
   if (issueMessage) {
-    e << "The target name \"" << targetName << "\" is reserved " << reason
+    e << "The target name \"" << tgt->GetName() << "\" is reserved " << reason
       << ".";
     if (messageType == MessageType::AUTHOR_WARNING) {
       e << "  It may result in undefined behavior.";
     }
-    this->GetCMakeInstance()->IssueMessage(messageType, e.str(),
-                                           tgt->GetBacktrace());
+    cm->IssueMessage(messageType, e.str(), tgt->GetBacktrace());
     if (messageType == MessageType::FATAL_ERROR) {
       return false;
     }
@@ -2683,6 +2724,16 @@
   return true;
 }
 
+bool cmGlobalGenerator::CheckCMP0037(std::string const& targetName,
+                                     std::string const& reason) const
+{
+  cmTarget* tgt = this->FindTarget(targetName);
+  if (!tgt) {
+    return true;
+  }
+  return RaiseCMP0037Message(this->GetCMakeInstance(), tgt, reason);
+}
+
 void cmGlobalGenerator::CreateDefaultGlobalTargets(
   std::vector<GlobalTargetInfo>& targets)
 {
@@ -3096,10 +3147,11 @@
   // by one or more of the cmake generators.
 
   // Adding additional targets to this list will require a policy!
-  const char* reservedTargets[] = { "all",       "ALL_BUILD",  "help",
-                                    "install",   "INSTALL",    "preinstall",
-                                    "clean",     "edit_cache", "rebuild_cache",
-                                    "ZERO_CHECK" };
+  static const cm::static_string_view reservedTargets[] = {
+    "all"_s,           "ALL_BUILD"_s,  "help"_s,  "install"_s,
+    "INSTALL"_s,       "preinstall"_s, "clean"_s, "edit_cache"_s,
+    "rebuild_cache"_s, "ZERO_CHECK"_s
+  };
 
   return cm::contains(reservedTargets, name);
 }
@@ -3216,7 +3268,6 @@
 void cmGlobalGenerator::AddRuleHash(const std::vector<std::string>& outputs,
                                     std::string const& content)
 {
-#if !defined(CMAKE_BOOTSTRAP)
   // Ignore if there are no outputs.
   if (outputs.empty()) {
     return;
@@ -3236,20 +3287,14 @@
 
   // Associate the hash with this output.
   this->RuleHashes[fname] = hash;
-#else
-  (void)outputs;
-  (void)content;
-#endif
 }
 
 void cmGlobalGenerator::CheckRuleHashes()
 {
-#if !defined(CMAKE_BOOTSTRAP)
   std::string home = this->GetCMakeInstance()->GetHomeOutputDirectory();
   std::string pfile = cmStrCat(home, "/CMakeFiles/CMakeRuleHashes.txt");
   this->CheckRuleHashes(pfile, home);
   this->WriteRuleHashes(pfile);
-#endif
 }
 
 void cmGlobalGenerator::CheckRuleHashes(std::string const& pfile,
@@ -3342,7 +3387,7 @@
   // Place the labels file in a per-target support directory.
   std::string dir = target->GetSupportDirectory();
   std::string file = cmStrCat(dir, "/Labels.txt");
-  std::string json_file = dir + "/Labels.json";
+  std::string json_file = cmStrCat(dir, "/Labels.json");
 
 #ifndef CMAKE_BOOTSTRAP
   // Check whether labels are enabled for this target.
diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h
index 9aefaff..6d29dc1 100644
--- a/Source/cmGlobalGenerator.h
+++ b/Source/cmGlobalGenerator.h
@@ -19,18 +19,16 @@
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
-#include "cm_codecvt.hxx"
-
 #include "cmBuildOptions.h"
 #include "cmCustomCommandLines.h"
 #include "cmDuration.h"
 #include "cmExportSet.h"
+#include "cmLocalGenerator.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmTarget.h"
 #include "cmTargetDepend.h"
-#include "cmTransformDepfile.h"
 #include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
@@ -41,13 +39,15 @@
 
 #define CMAKE_DIRECTORY_ID_SEP "::@"
 
+enum class cmDepfileFormat;
+enum class codecvt_Encoding;
+
 class cmDirectoryId;
 class cmExportBuildFileGenerator;
 class cmExternalMakefileProjectGenerator;
 class cmGeneratorTarget;
 class cmInstallRuntimeDependencySet;
 class cmLinkLineComputer;
-class cmLocalGenerator;
 class cmMakefile;
 class cmOutputConverter;
 class cmQtAutoGenGlobalInitializer;
@@ -120,10 +120,7 @@
   }
 
   /** Get encoding used by generator for makefile files */
-  virtual codecvt::Encoding GetMakefileEncoding() const
-  {
-    return codecvt::None;
-  }
+  virtual codecvt_Encoding GetMakefileEncoding() const;
 
 #if !defined(CMAKE_BOOTSTRAP)
   /** Get a JSON object describing the generator.  */
@@ -161,6 +158,8 @@
 
   virtual bool CheckCxxModuleSupport() { return false; }
 
+  virtual bool IsGNUMakeJobServerAware() const { return false; }
+
   bool Compute();
   virtual void AddExtraIDETargets() {}
 
@@ -640,6 +639,8 @@
   };
   StripCommandStyle GetStripCommandStyle(std::string const& strip);
 
+  virtual std::string& EncodeLiteral(std::string& lit) { return lit; }
+
 protected:
   // for a project collect all its targets by following depend
   // information, and also collect all the targets
@@ -661,6 +662,8 @@
 
   virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const;
 
+  bool DiscoverSyntheticTargets();
+
   bool AddHeaderSetVerification();
 
   bool AddAutomaticSources();
diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx
index 2453bfc..4ba53ec 100644
--- a/Source/cmGlobalGhsMultiGenerator.cxx
+++ b/Source/cmGlobalGhsMultiGenerator.cxx
@@ -670,7 +670,7 @@
     }
 
     // Add the cache file.
-    listFiles.push_back(cmStrCat(
+    listFiles.emplace_back(cmStrCat(
       this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeCache.txt"));
 
     // Print not implemented warning.
diff --git a/Source/cmGlobalJOMMakefileGenerator.cxx b/Source/cmGlobalJOMMakefileGenerator.cxx
index f1d4d09..44980d6 100644
--- a/Source/cmGlobalJOMMakefileGenerator.cxx
+++ b/Source/cmGlobalJOMMakefileGenerator.cxx
@@ -5,6 +5,7 @@
 #include <ostream>
 
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
@@ -45,7 +46,7 @@
                                                        std::string const& lang,
                                                        cmValue envVar) const
 {
-  if (lang == "CXX" || lang == "C") {
+  if (lang == "CXX"_s || lang == "C"_s) {
     /* clang-format off */
     os <<
       "To use the JOM generator with Visual C++, cmake must be run from a "
diff --git a/Source/cmGlobalJOMMakefileGenerator.h b/Source/cmGlobalJOMMakefileGenerator.h
index 5e74875..cd3ed33 100644
--- a/Source/cmGlobalJOMMakefileGenerator.h
+++ b/Source/cmGlobalJOMMakefileGenerator.h
@@ -47,6 +47,8 @@
   void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*,
                       bool optional) override;
 
+  bool IsGNUMakeJobServerAware() const override { return false; }
+
 protected:
   std::vector<GeneratedMakeCommand> GenerateBuildCommand(
     const std::string& makeProgram, const std::string& projectName,
diff --git a/Source/cmGlobalMSYSMakefileGenerator.cxx b/Source/cmGlobalMSYSMakefileGenerator.cxx
index e543aea..26b30fd 100644
--- a/Source/cmGlobalMSYSMakefileGenerator.cxx
+++ b/Source/cmGlobalMSYSMakefileGenerator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmGlobalMSYSMakefileGenerator.h"
 
+#include <cmext/string_view>
+
 #include "cmsys/FStream.hxx"
 
 #include "cmMakefile.h"
@@ -31,7 +33,7 @@
   while (fin) {
     fin >> path;
     fin >> mount;
-    if (mount == "/mingw") {
+    if (mount == "/mingw"_s) {
       mingwBin = cmStrCat(path, "/bin");
     }
   }
@@ -45,7 +47,7 @@
   this->cmGlobalUnixMakefileGenerator3::EnableLanguage(l, mf, optional);
 
   if (!mf->IsSet("CMAKE_AR") && !this->CMakeInstance->GetIsInTryCompile() &&
-      !(1 == l.size() && l[0] == "NONE")) {
+      !(1 == l.size() && l[0] == "NONE"_s)) {
     cmSystemTools::Error(
       "CMAKE_AR was not found, please set to archive program. " +
       mf->GetSafeDefinition("CMAKE_AR"));
diff --git a/Source/cmGlobalNMakeMakefileGenerator.cxx b/Source/cmGlobalNMakeMakefileGenerator.cxx
index cb53850..424c4bd 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.cxx
+++ b/Source/cmGlobalNMakeMakefileGenerator.cxx
@@ -5,6 +5,7 @@
 #include <ostream>
 
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmsys/RegularExpression.hxx"
 
@@ -88,7 +89,7 @@
 void cmGlobalNMakeMakefileGenerator::PrintCompilerAdvice(
   std::ostream& os, std::string const& lang, cmValue envVar) const
 {
-  if (lang == "CXX" || lang == "C") {
+  if (lang == "CXX"_s || lang == "C"_s) {
     /* clang-format off */
     os <<
       "To use the NMake generator with Visual C++, cmake must be run from a "
diff --git a/Source/cmGlobalNMakeMakefileGenerator.h b/Source/cmGlobalNMakeMakefileGenerator.h
index 436ebca..eafab83 100644
--- a/Source/cmGlobalNMakeMakefileGenerator.h
+++ b/Source/cmGlobalNMakeMakefileGenerator.h
@@ -7,7 +7,7 @@
 #include <string>
 #include <vector>
 
-#include "cm_codecvt.hxx"
+#include "cm_codecvt_Encoding.hxx"
 
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalUnixMakefileGenerator3.h"
@@ -38,9 +38,10 @@
   static std::string GetActualName() { return "NMake Makefiles"; }
 
   /** Get encoding used by generator for makefile files */
-  codecvt::Encoding GetMakefileEncoding() const override
+  codecvt_Encoding GetMakefileEncoding() const override
   {
-    return this->NMakeSupportsUTF8 ? codecvt::UTF8_WITH_BOM : codecvt::ANSI;
+    return this->NMakeSupportsUTF8 ? codecvt_Encoding::UTF8_WITH_BOM
+                                   : codecvt_Encoding::ANSI;
   }
 
   /** Get the documentation entry for this generator.  */
@@ -53,6 +54,8 @@
   void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*,
                       bool optional) override;
 
+  bool IsGNUMakeJobServerAware() const override { return false; }
+
 protected:
   std::vector<GeneratedMakeCommand> GenerateBuildCommand(
     const std::string& makeProgram, const std::string& projectName,
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 97c9b70..7368a6a 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -8,6 +8,7 @@
 #include <cstdio>
 #include <functional>
 #include <sstream>
+#include <type_traits>
 #include <utility>
 
 #include <cm/iterator>
@@ -181,14 +182,13 @@
   return encoded;
 }
 
-std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string& lit)
+std::string cmGlobalNinjaGenerator::GetEncodedLiteral(const std::string& lit)
 {
   std::string result = lit;
-  EncodeLiteralInplace(result);
-  return result;
+  return this->EncodeLiteral(result);
 }
 
-void cmGlobalNinjaGenerator::EncodeLiteralInplace(std::string& lit)
+std::string& cmGlobalNinjaGenerator::EncodeLiteral(std::string& lit)
 {
   cmSystemTools::ReplaceString(lit, "$", "$$");
   cmSystemTools::ReplaceString(lit, "\n", "$\n");
@@ -196,6 +196,7 @@
     cmSystemTools::ReplaceString(lit, cmStrCat('$', this->GetCMakeCFGIntDir()),
                                  this->GetCMakeCFGIntDir());
   }
+  return lit;
 }
 
 std::string cmGlobalNinjaGenerator::EncodePath(const std::string& path)
@@ -207,7 +208,7 @@
   else
     std::replace(result.begin(), result.end(), '/', '\\');
 #endif
-  this->EncodeLiteralInplace(result);
+  this->EncodeLiteral(result);
   cmSystemTools::ReplaceString(result, " ", "$ ");
   cmSystemTools::ReplaceString(result, ":", "$:");
   return result;
@@ -394,7 +395,7 @@
 #endif
       vars["COMMAND"] = std::move(cmd);
     }
-    vars["DESC"] = this->EncodeLiteral(description);
+    vars["DESC"] = this->GetEncodedLiteral(description);
     if (restat) {
       vars["restat"] = "1";
     }
@@ -553,6 +554,15 @@
 {
 #ifdef _WIN32
   cm->GetState()->SetWindowsShell(true);
+
+  // Attempt to use full path to COMSPEC, default "cmd.exe"
+  std::string comspec;
+  if (cmSystemTools::GetEnv("COMSPEC", comspec) &&
+      cmSystemTools::FileIsFullPath(comspec)) {
+    this->Comspec = comspec;
+  } else {
+    this->Comspec = "cmd.exe";
+  }
 #endif
   this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
 }
@@ -566,7 +576,7 @@
     cm::make_unique<cmLocalNinjaGenerator>(this, mf));
 }
 
-codecvt::Encoding cmGlobalNinjaGenerator::GetMakefileEncoding() const
+codecvt_Encoding cmGlobalNinjaGenerator::GetMakefileEncoding() const
 {
   return this->NinjaExpectedEncoding;
 }
@@ -799,7 +809,7 @@
   if (this->NinjaSupportsCodePage) {
     this->CheckNinjaCodePage();
   } else {
-    this->NinjaExpectedEncoding = codecvt::ANSI;
+    this->NinjaExpectedEncoding = codecvt_Encoding::ANSI;
   }
 #endif
 }
@@ -830,9 +840,9 @@
           lineView.substr(cmStrLen("Build file encoding: "));
         if (encoding == "UTF-8") {
           // Ninja expects UTF-8. We use that internally. No conversion needed.
-          this->NinjaExpectedEncoding = codecvt::None;
+          this->NinjaExpectedEncoding = codecvt_Encoding::None;
         } else {
-          this->NinjaExpectedEncoding = codecvt::ANSI;
+          this->NinjaExpectedEncoding = codecvt_Encoding::ANSI;
         }
         found = true;
         break;
@@ -842,10 +852,10 @@
       this->GetCMakeInstance()->IssueMessage(
         MessageType::WARNING,
         "Could not determine Ninja's code page, defaulting to UTF-8");
-      this->NinjaExpectedEncoding = codecvt::None;
+      this->NinjaExpectedEncoding = codecvt_Encoding::None;
     }
   } else {
-    this->NinjaExpectedEncoding = codecvt::ANSI;
+    this->NinjaExpectedEncoding = codecvt_Encoding::ANSI;
   }
 }
 
@@ -1268,6 +1278,13 @@
   return cmStrCat("cmake_object_order_depends_target_", target->GetName());
 }
 
+std::string cmGlobalNinjaGenerator::OrderDependsTargetForTargetPrivate(
+  cmGeneratorTarget const* target, const std::string& config) const
+{
+  return cmStrCat(this->OrderDependsTargetForTarget(target, config),
+                  "_private");
+}
+
 void cmGlobalNinjaGenerator::AppendTargetOutputs(
   cmGeneratorTarget const* target, cmNinjaDeps& outputs,
   const std::string& config, cmNinjaTargetDepends depends) const
@@ -2642,7 +2659,9 @@
   for (cmScanDepInfo const& object : objects) {
     for (auto const& p : object.Provides) {
       std::string mod;
-      if (!p.CompiledModulePath.empty()) {
+      if (cmDyndepCollation::IsBmiOnly(export_info, object.PrimaryOutput)) {
+        mod = object.PrimaryOutput;
+      } else if (!p.CompiledModulePath.empty()) {
         // The scanner provided the path to the module file.
         mod = p.CompiledModulePath;
         if (!cmSystemTools::FileIsFullPath(mod)) {
@@ -2713,8 +2732,12 @@
       build.Outputs[0] = this->ConvertToNinjaPath(object.PrimaryOutput);
       build.ImplicitOuts.clear();
       for (auto const& p : object.Provides) {
-        build.ImplicitOuts.push_back(
-          this->ConvertToNinjaPath(mod_files[p.LogicalName].BmiPath));
+        auto const implicitOut =
+          this->ConvertToNinjaPath(mod_files[p.LogicalName].BmiPath);
+        // Ignore the `provides` when the BMI is the output.
+        if (implicitOut != build.Outputs[0]) {
+          build.ImplicitOuts.emplace_back(implicitOut);
+        }
       }
       build.ImplicitDeps.clear();
       for (auto const& r : object.Requires) {
@@ -3160,3 +3183,10 @@
   return cmStrCat("cmake_object_order_depends_target_", target->GetName(), '_',
                   cmSystemTools::UpperCase(config));
 }
+
+std::string cmGlobalNinjaMultiGenerator::OrderDependsTargetForTargetPrivate(
+  cmGeneratorTarget const* target, const std::string& config) const
+{
+  return cmStrCat(this->OrderDependsTargetForTarget(target, config),
+                  "_private");
+}
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index bfbe57f..56a922c 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -16,7 +16,7 @@
 
 #include <cm/optional>
 
-#include "cm_codecvt.hxx"
+#include "cm_codecvt_Encoding.hxx"
 
 #include "cmBuildOptions.h"
 #include "cmGeneratedFileStream.h"
@@ -77,8 +77,8 @@
   static void WriteDivider(std::ostream& os);
 
   static std::string EncodeRuleName(std::string const& name);
-  std::string EncodeLiteral(const std::string& lit);
-  void EncodeLiteralInplace(std::string& lit);
+  std::string& EncodeLiteral(std::string& lit) override;
+  std::string GetEncodedLiteral(const std::string& lit);
   std::string EncodePath(const std::string& path);
 
   std::unique_ptr<cmLinkLineComputer> CreateLinkLineComputer(
@@ -192,7 +192,7 @@
   bool IsNinja() const override { return true; }
 
   /** Get encoding used by generator for ninja files */
-  codecvt::Encoding GetMakefileEncoding() const override;
+  codecvt_Encoding GetMakefileEncoding() const override;
 
   static cmDocumentationEntry GetDocumentation();
 
@@ -282,6 +282,10 @@
   };
   MapToNinjaPathImpl MapToNinjaPath() { return { this }; }
 
+#ifdef _WIN32
+  std::string const& GetComspec() const { return this->Comspec; }
+#endif
+
   // -- Additional clean files
   void AddAdditionalCleanFile(std::string fileName, const std::string& config);
   const char* GetAdditionalCleanTargetName() const
@@ -345,6 +349,9 @@
   virtual std::string OrderDependsTargetForTarget(
     cmGeneratorTarget const* target, const std::string& config) const;
 
+  virtual std::string OrderDependsTargetForTargetPrivate(
+    cmGeneratorTarget const* target, const std::string& config) const;
+
   void AppendTargetOutputs(cmGeneratorTarget const* target,
                            cmNinjaDeps& outputs, const std::string& config,
                            cmNinjaTargetDepends depends) const;
@@ -590,7 +597,12 @@
   bool NinjaSupportsMetadataOnRegeneration = false;
   bool NinjaSupportsCodePage = false;
 
-  codecvt::Encoding NinjaExpectedEncoding = codecvt::None;
+  codecvt_Encoding NinjaExpectedEncoding = codecvt_Encoding::None;
+
+#ifdef _WIN32
+  // Windows Command shell.
+  std::string Comspec;
+#endif
 
   bool DiagnosedCxxModuleNinjaSupport = false;
 
@@ -729,6 +741,9 @@
   std::string OrderDependsTargetForTarget(
     cmGeneratorTarget const* target, const std::string& config) const override;
 
+  std::string OrderDependsTargetForTargetPrivate(
+    cmGeneratorTarget const* target, const std::string& config) const override;
+
 protected:
   bool OpenBuildFileStreams() override;
   void CloseBuildFileStreams() override;
diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h
index 760679a..ee78351 100644
--- a/Source/cmGlobalUnixMakefileGenerator3.h
+++ b/Source/cmGlobalUnixMakefileGenerator3.h
@@ -120,6 +120,8 @@
 
   void Configure() override;
 
+  bool IsGNUMakeJobServerAware() const override { return true; }
+
   /**
    * Generate the all required files for building this project/tree. This
    * basically creates a series of LocalGenerators for each directory and
diff --git a/Source/cmGlobalVisualStudio10Generator.cxx b/Source/cmGlobalVisualStudio10Generator.cxx
index 35227db..541db63 100644
--- a/Source/cmGlobalVisualStudio10Generator.cxx
+++ b/Source/cmGlobalVisualStudio10Generator.cxx
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cmext/string_view>
 
 #include <cm3p/json/reader.h>
 #include <cm3p/json/value.h>
@@ -17,6 +18,7 @@
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
 
+#include "cmCryptoHash.h"
 #include "cmDocumentationEntry.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
@@ -83,10 +85,10 @@
 {
   if (this->SystemIsWindowsCE && ts.empty() &&
       this->DefaultPlatformToolset.empty()) {
-    std::ostringstream e;
-    e << this->GetName() << " Windows CE version '" << this->SystemVersion
-      << "' requires CMAKE_GENERATOR_TOOLSET to be set.";
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(this->GetName(), " Windows CE version '", this->SystemVersion,
+               "' requires CMAKE_GENERATOR_TOOLSET to be set."));
     return false;
   }
 
@@ -105,16 +107,17 @@
   if (!this->CustomFlagTableDir.empty() &&
       !(cmSystemTools::FileIsFullPath(this->CustomFlagTableDir) &&
         cmSystemTools::FileIsDirectory(this->CustomFlagTableDir))) {
-    std::ostringstream e;
-    /* clang-format off */
-    e <<
-      "Generator\n"
-      "  " << this->GetName() << "\n"
-      "given toolset\n"
-      "  customFlagTableDir=" << this->CustomFlagTableDir << "\n"
-      "that is not an absolute path to an existing directory.";
-    /* clang-format on */
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Generator\n"
+               "  ",
+               this->GetName(),
+               "\n"
+               "given toolset\n"
+               "  customFlagTableDir=",
+               this->CustomFlagTableDir,
+               "\n"
+               "that is not an absolute path to an existing directory."));
     cmSystemTools::SetFatalErrorOccurred();
     return false;
   }
@@ -125,7 +128,8 @@
     // differing from the "false" and "true" values used in older toolsets.
     // A VS 2015 update changed it back.  Parse the "link.xml" file to
     // discover which one we need.
-    std::string const link_xml = this->VCTargetsPath + "/1033/link.xml";
+    std::string const link_xml =
+      cmStrCat(this->VCTargetsPath, "/1033/link.xml");
     cmsys::ifstream fin(link_xml.c_str());
     std::string line;
     while (fin && cmSystemTools::GetLineFromStream(fin, line)) {
@@ -140,24 +144,24 @@
   this->SupportsUnityBuilds =
     this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS16 ||
     (this->Version == cmGlobalVisualStudioGenerator::VSVersion::VS15 &&
-     cmSystemTools::PathExists(this->VCTargetsPath +
-                               "/Microsoft.Cpp.Unity.targets"));
+     cmSystemTools::PathExists(
+       cmStrCat(this->VCTargetsPath, "/Microsoft.Cpp.Unity.targets")));
 
   if (this->GeneratorToolsetCuda.empty()) {
     // Find the highest available version of the CUDA tools.
     std::vector<std::string> cudaTools;
     std::string bcDir;
     if (this->GeneratorToolsetCudaCustomDir.empty()) {
-      bcDir = this->VCTargetsPath + "/BuildCustomizations";
+      bcDir = cmStrCat(this->VCTargetsPath, "/BuildCustomizations");
     } else {
-      bcDir = this->GetPlatformToolsetCudaCustomDirString() +
-        this->GetPlatformToolsetCudaVSIntegrationSubdirString() +
-        "extras\\visual_studio_integration\\MSBuildExtensions";
+      bcDir = cmStrCat(this->GetPlatformToolsetCudaCustomDirString(),
+                       this->GetPlatformToolsetCudaVSIntegrationSubdirString(),
+                       "extras\\visual_studio_integration\\MSBuildExtensions");
       cmSystemTools::ConvertToUnixSlashes(bcDir);
     }
     cmsys::Glob gl;
     gl.SetRelative(bcDir.c_str());
-    if (gl.FindFiles(bcDir + "/CUDA *.props")) {
+    if (gl.FindFiles(cmStrCat(bcDir, "/CUDA *.props"))) {
       cudaTools = gl.GetFiles();
     }
     if (!cudaTools.empty()) {
@@ -168,18 +172,19 @@
     } else if (!this->GeneratorToolsetCudaCustomDir.empty()) {
       // Generate an error if Visual Studio integration files are not found
       // inside of custom cuda toolset.
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given toolset\n"
-        "  cuda=" << this->GeneratorToolsetCudaCustomDir << "\n"
-        "cannot detect Visual Studio integration files in path\n"
-        "  " << bcDir;
-
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Generator\n"
+                 "  ",
+                 this->GetName(),
+                 "\n"
+                 "given toolset\n"
+                 "  cuda=",
+                 this->GeneratorToolsetCudaCustomDir,
+                 "\n"
+                 "cannot detect Visual Studio integration files in path\n"
+                 "  ",
+                 bcDir));
 
       // Clear the configured tool-set
       this->GeneratorToolsetCuda.clear();
@@ -187,35 +192,40 @@
   }
 
   if (!this->GeneratorToolsetVersion.empty() &&
-      this->GeneratorToolsetVersion != "Test Toolset Version") {
-    // If a specific minor version of the toolset was requested, verify that it
-    // is compatible to the major version and that is exists on disk.
-    // If not clear the value.
-    std::string versionToolset = this->GeneratorToolsetVersion;
-    cmsys::RegularExpression regex("[0-9][0-9]\\.[0-9][0-9]");
-    if (regex.find(versionToolset)) {
-      versionToolset = "v" + versionToolset.erase(2, 1);
-    } else {
-      // Version not recognized. Clear it.
-      versionToolset.clear();
-    }
+      this->GeneratorToolsetVersion != "Test Toolset Version"_s) {
+    // If a specific minor version of the MSVC toolset is requested, verify
+    // that it is compatible with the PlatformToolset version. The ability to
+    // choose a minor version of MSVC has been available since v141.
+    std::string const& platformToolset = this->GetPlatformToolsetString();
+    cmsys::RegularExpression vcPlatformToolsetRegex("^v[0-9][0-9][0-9]$");
+    if (vcPlatformToolsetRegex.find(platformToolset) ||
+        platformToolset == "Test Toolset"_s) {
+      std::string versionToolset = this->GeneratorToolsetVersion;
+      cmsys::RegularExpression versionToolsetRegex("^[0-9][0-9]\\.[0-9][0-9]");
+      if (versionToolsetRegex.find(versionToolset)) {
+        versionToolset = cmStrCat('v', versionToolset.erase(2, 1));
+      } else {
+        // Version not recognized. Clear it.
+        versionToolset.clear();
+      }
 
-    if (!cmHasPrefix(versionToolset, this->GetPlatformToolsetString())) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given toolset and version specification\n"
-        "  " << this->GetPlatformToolsetString() << ",version=" <<
-        this->GeneratorToolsetVersion << "\n"
-        "contains an invalid version specification."
-      ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      if (!cmHasPrefix(versionToolset, platformToolset)) {
+        mf->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("Generator\n"
+                   "  ",
+                   this->GetName(),
+                   "\n"
+                   "given toolset and version specification\n"
+                   "  ",
+                   this->GetPlatformToolsetString(),
+                   ",version=", this->GeneratorToolsetVersion,
+                   "\n"
+                   "contains an invalid version specification."));
 
-      // Clear the configured tool-set
-      this->GeneratorToolsetVersion.clear();
+        // Clear the configured tool-set
+        this->GeneratorToolsetVersion.clear();
+      }
     }
 
     std::string auxProps;
@@ -232,40 +242,40 @@
         this->GeneratorToolsetVersionProps = std::move(auxProps);
         break;
       case AuxToolset::PropsMissing: {
-        std::ostringstream e;
-        /* clang-format off */
-        e <<
-          "Generator\n"
-          "  " << this->GetName() << "\n"
-          "given toolset and version specification\n"
-          "  " << this->GetPlatformToolsetString() << ",version=" <<
-          this->GeneratorToolsetVersion << "\n"
-          "does not seem to be installed at\n" <<
-          "  " << auxProps;
-        ;
-        /* clang-format on */
-        mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        mf->IssueMessage(MessageType::FATAL_ERROR,
+                         cmStrCat("Generator\n"
+                                  "  ",
+                                  this->GetName(),
+                                  "\n"
+                                  "given toolset and version specification\n"
+                                  "  ",
+                                  this->GetPlatformToolsetString(),
+                                  ",version=", this->GeneratorToolsetVersion,
+                                  "\n"
+                                  "does not seem to be installed at\n"
+                                  "  ",
+                                  auxProps));
 
         // Clear the configured tool-set
         this->GeneratorToolsetVersion.clear();
         this->GeneratorToolsetVersionProps = {};
       } break;
       case AuxToolset::PropsIndeterminate: {
-        std::ostringstream e;
-        /* clang-format off */
-        e <<
-          "Generator\n"
-          "  " << this->GetName() << "\n"
-          "given toolset and version specification\n"
-          "  " << this->GetPlatformToolsetString() << ",version=" <<
-          this->GeneratorToolsetVersion << "\n"
-          "has multiple matches installed at\n" <<
-          "  " << auxProps << "\n" <<
-          "The toolset and version specification must resolve \n" <<
-           "to a single installed toolset";
-        ;
-        /* clang-format on */
-        mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+        mf->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("Generator\n"
+                   "  ",
+                   this->GetName(),
+                   "\n"
+                   "given toolset and version specification\n"
+                   "  ",
+                   this->GetPlatformToolsetString(),
+                   ",version=", this->GeneratorToolsetVersion,
+                   "\n"
+                   "has multiple matches installed at\n",
+                   "  ", auxProps, "\n",
+                   "The toolset and version specification must resolve \n"
+                   "to a single installed toolset"));
 
         // Clear the configured tool-set
         this->GeneratorToolsetVersion.clear();
@@ -319,47 +329,47 @@
   for (; fi != fields.end(); ++fi) {
     std::string::size_type pos = fi->find('=');
     if (pos == fi->npos) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given toolset specification\n"
-        "  " << ts << "\n"
-        "that contains a field after the first ',' with no '='."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Generator\n"
+                 "  ",
+                 this->GetName(),
+                 "\n"
+                 "given toolset specification\n"
+                 "  ",
+                 ts,
+                 "\n"
+                 "that contains a field after the first ',' with no '='."));
       return false;
     }
     std::string const key = fi->substr(0, pos);
     std::string const value = fi->substr(pos + 1);
     if (!handled.insert(key).second) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given toolset specification\n"
-        "  " << ts << "\n"
-        "that contains duplicate field key '" << key << "'."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("Generator\n"
+                                "  ",
+                                this->GetName(),
+                                "\n"
+                                "given toolset specification\n"
+                                "  ",
+                                ts,
+                                "\n"
+                                "that contains duplicate field key '",
+                                key, "'."));
       return false;
     }
     if (!this->ProcessGeneratorToolsetField(key, value)) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given toolset specification\n"
-        "  " << ts << "\n"
-        "that contains invalid field '" << *fi << "'."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("Generator\n"
+                                "  ",
+                                this->GetName(),
+                                "\n"
+                                "given toolset specification\n"
+                                "  ",
+                                ts,
+                                "\n"
+                                "that contains invalid field '",
+                                *fi, "'."));
       return false;
     }
   }
@@ -370,7 +380,7 @@
 bool cmGlobalVisualStudio10Generator::ProcessGeneratorToolsetField(
   std::string const& key, std::string const& value)
 {
-  if (key == "cuda") {
+  if (key == "cuda"_s) {
     /* test if cuda toolset is path to custom dir or cuda version */
     auto pos = value.find_first_not_of("0123456789.");
     if (pos != std::string::npos) {
@@ -395,16 +405,16 @@
     }
     return true;
   }
-  if (key == "customFlagTableDir") {
+  if (key == "customFlagTableDir"_s) {
     this->CustomFlagTableDir = value;
     cmSystemTools::ConvertToUnixSlashes(this->CustomFlagTableDir);
     return true;
   }
-  if (key == "version") {
+  if (key == "version"_s) {
     this->GeneratorToolsetVersion = value;
     return true;
   }
-  if (key == "VCTargetsPath") {
+  if (key == "VCTargetsPath"_s) {
     this->CustomVCTargetsPath = value;
     ConvertToWindowsSlashes(this->CustomVCTargetsPath);
     return true;
@@ -414,34 +424,35 @@
 
 bool cmGlobalVisualStudio10Generator::InitializeSystem(cmMakefile* mf)
 {
-  if (this->SystemName == "Windows") {
+  if (this->SystemName == "Windows"_s) {
     if (!this->InitializeWindows(mf)) {
       return false;
     }
-  } else if (this->SystemName == "WindowsCE") {
+  } else if (this->SystemName == "WindowsCE"_s) {
     this->SystemIsWindowsCE = true;
     if (!this->InitializeWindowsCE(mf)) {
       return false;
     }
-  } else if (this->SystemName == "WindowsPhone") {
+  } else if (this->SystemName == "WindowsPhone"_s) {
     this->SystemIsWindowsPhone = true;
     if (!this->InitializeWindowsPhone(mf)) {
       return false;
     }
-  } else if (this->SystemName == "WindowsStore") {
+  } else if (this->SystemName == "WindowsStore"_s) {
     this->SystemIsWindowsStore = true;
     if (!this->InitializeWindowsStore(mf)) {
       return false;
     }
-  } else if (this->SystemName == "Android") {
+  } else if (this->SystemName == "Android"_s) {
     if (this->PlatformInGeneratorName) {
-      std::ostringstream e;
-      e << "CMAKE_SYSTEM_NAME is 'Android' but CMAKE_GENERATOR "
-        << "specifies a platform too: '" << this->GetName() << "'";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("CMAKE_SYSTEM_NAME is 'Android' but CMAKE_GENERATOR ",
+                 "specifies a platform too: '", this->GetName(), '\''));
       return false;
     }
-    if (mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM") == "Tegra-Android") {
+    if (mf->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM") ==
+        "Tegra-Android"_s) {
       if (!this->InitializeTegraAndroid(mf)) {
         return false;
       }
@@ -464,10 +475,10 @@
 bool cmGlobalVisualStudio10Generator::InitializeWindowsCE(cmMakefile* mf)
 {
   if (this->PlatformInGeneratorName) {
-    std::ostringstream e;
-    e << "CMAKE_SYSTEM_NAME is 'WindowsCE' but CMAKE_GENERATOR "
-      << "specifies a platform too: '" << this->GetName() << "'";
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("CMAKE_SYSTEM_NAME is 'WindowsCE' but CMAKE_GENERATOR ",
+               "specifies a platform too: '", this->GetName(), '\''));
     return false;
   }
 
@@ -485,17 +496,17 @@
 
 bool cmGlobalVisualStudio10Generator::InitializeWindowsPhone(cmMakefile* mf)
 {
-  std::ostringstream e;
-  e << this->GetName() << " does not support Windows Phone.";
-  mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  mf->IssueMessage(
+    MessageType::FATAL_ERROR,
+    cmStrCat(this->GetName(), " does not support Windows Phone."));
   return false;
 }
 
 bool cmGlobalVisualStudio10Generator::InitializeWindowsStore(cmMakefile* mf)
 {
-  std::ostringstream e;
-  e << this->GetName() << " does not support Windows Store.";
-  mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  mf->IssueMessage(
+    MessageType::FATAL_ERROR,
+    cmStrCat(this->GetName(), " does not support Windows Store."));
   return false;
 }
 
@@ -519,15 +530,15 @@
 
 bool cmGlobalVisualStudio10Generator::InitializeAndroid(cmMakefile* mf)
 {
-  std::ostringstream e;
-  e << this->GetName() << " does not support Android.";
-  mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+  mf->IssueMessage(MessageType::FATAL_ERROR,
+                   cmStrCat(this->GetName(), " does not support Android."));
   return false;
 }
 
 bool cmGlobalVisualStudio10Generator::InitializePlatform(cmMakefile* mf)
 {
-  if (this->SystemName == "Windows" || this->SystemName == "WindowsStore") {
+  if (this->SystemName == "Windows"_s ||
+      this->SystemName == "WindowsStore"_s) {
     if (!this->InitializePlatformWindows(mf)) {
       return false;
     }
@@ -565,7 +576,7 @@
 
 std::string cmGlobalVisualStudio10Generator::SelectWindowsCEToolset() const
 {
-  if (this->SystemVersion == "8.0") {
+  if (this->SystemVersion == "8.0"_s) {
     return "CE800";
   }
   return "";
@@ -607,29 +618,30 @@
   }
   if (this->LongestSource.Length > 0) {
     cmLocalGenerator* lg = this->LongestSource.Target->GetLocalGenerator();
-    std::ostringstream e;
-    /* clang-format off */
-    e <<
-      "The binary and/or source directory paths may be too long to generate "
-      "Visual Studio 10 files for this project.  "
-      "Consider choosing shorter directory names to build this project with "
-      "Visual Studio 10.  "
-      "A more detailed explanation follows."
-      "\n"
-      "There is a bug in the VS 10 IDE that renders property dialog fields "
-      "blank for files referenced by full path in the project file.  "
-      "However, CMake must reference at least one file by full path:\n"
-      "  " << this->LongestSource.SourceFile->GetFullPath() << "\n"
-      "This is because some Visual Studio tools would append the relative "
-      "path to the end of the referencing directory path, as in:\n"
-      "  " << lg->GetCurrentBinaryDirectory() << "/"
-      << this->LongestSource.SourceRel << "\n"
-      "and then incorrectly complain that the file does not exist because "
-      "the path length is too long for some internal buffer or API.  "
-      "To avoid this problem CMake must use a full path for this file "
-      "which then triggers the VS 10 property dialog bug.";
-    /* clang-format on */
-    lg->IssueMessage(MessageType::WARNING, e.str());
+    lg->IssueMessage(
+      MessageType::WARNING,
+      cmStrCat(
+        "The binary and/or source directory paths may be too long to generate "
+        "Visual Studio 10 files for this project.  "
+        "Consider choosing shorter directory names to build this project with "
+        "Visual Studio 10.  "
+        "A more detailed explanation follows."
+        "\n"
+        "There is a bug in the VS 10 IDE that renders property dialog fields "
+        "blank for files referenced by full path in the project file.  "
+        "However, CMake must reference at least one file by full path:\n"
+        "  ",
+        this->LongestSource.SourceFile->GetFullPath(),
+        "\n"
+        "This is because some Visual Studio tools would append the relative "
+        "path to the end of the referencing directory path, as in:\n"
+        "  ",
+        lg->GetCurrentBinaryDirectory(), '/', this->LongestSource.SourceRel,
+        "\n"
+        "and then incorrectly complain that the file does not exist because "
+        "the path length is too long for some internal buffer or API.  "
+        "To avoid this problem CMake must use a full path for this file "
+        "which then triggers the VS 10 property dialog bug."));
   }
   if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
         "CMAKE_VS_NUGET_PACKAGE_RESTORE")) {
@@ -641,10 +653,10 @@
   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
 {
   for (std::string const& it : lang) {
-    if (it == "ASM_NASM") {
+    if (it == "ASM_NASM"_s) {
       this->NasmEnabled = true;
     }
-    if (it == "CUDA") {
+    if (it == "CUDA"_s) {
       this->CudaEnabled = true;
     }
   }
@@ -830,8 +842,8 @@
 bool cmGlobalVisualStudio10Generator::FindVCTargetsPath(cmMakefile* mf)
 {
   // Skip this in special cases within our own test suite.
-  if (this->GetPlatformName() == "Test Platform" ||
-      this->GetPlatformToolsetString() == "Test Toolset") {
+  if (this->GetPlatformName() == "Test Platform"_s ||
+      this->GetPlatformToolsetString() == "Test Toolset"_s) {
     return true;
   }
 
@@ -843,11 +855,11 @@
     wd = cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(),
                   "/CMakeFiles");
   }
-  wd += "/";
+  wd += '/';
   wd += cmVersion::GetCMakeVersion();
 
   // We record the result persistently in a file.
-  std::string const txt = wd + "/VCTargetsPath.txt";
+  std::string const txt = cmStrCat(wd, "/VCTargetsPath.txt");
 
   // If we have a recorded result, use it.
   {
@@ -861,8 +873,8 @@
 
   // Prepare the work directory.
   if (!cmSystemTools::MakeDirectory(wd)) {
-    std::string e = "Failed to make directory:\n  " + wd;
-    mf->IssueMessage(MessageType::FATAL_ERROR, e);
+    mf->IssueMessage(MessageType::FATAL_ERROR,
+                     cmStrCat("Failed to make directory:\n  ", wd));
     cmSystemTools::SetFatalErrorOccurred();
     return false;
   }
@@ -870,7 +882,7 @@
   // Generate a project file for MSBuild to tell us the VCTargetsPath value.
   std::string const vcxproj = "VCTargetsPath.vcxproj";
   {
-    std::string const vcxprojAbs = wd + "/" + vcxproj;
+    std::string const vcxprojAbs = cmStrCat(wd, '/', vcxproj);
     cmsys::ofstream fout(vcxprojAbs.c_str());
     cmXMLWriter xw(fout);
 
@@ -889,7 +901,7 @@
       cmXMLElement eig(eprj, "ItemGroup");
       eig.Attribute("Label", "ProjectConfigurations");
       cmXMLElement epc(eig, "ProjectConfiguration");
-      epc.Attribute("Include", "Debug|" + this->GetPlatformName());
+      epc.Attribute("Include", cmStrCat("Debug|", this->GetPlatformName()));
       cmXMLElement(epc, "Configuration").Content("Debug");
       cmXMLElement(epc, "Platform").Content(this->GetPlatformName());
     }
@@ -899,19 +911,19 @@
       cmXMLElement(epg, "ProjectGuid")
         .Content("{F3FC6D86-508D-3FB1-96D2-995F08B142EC}");
       cmXMLElement(epg, "Keyword")
-        .Content(mf->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"
+        .Content(mf->GetSafeDefinition("CMAKE_SYSTEM_NAME") == "Android"_s
                    ? "Android"
                    : "Win32Proj");
       cmXMLElement(epg, "Platform").Content(this->GetPlatformName());
-      if (this->GetSystemName() == "WindowsPhone") {
+      if (this->GetSystemName() == "WindowsPhone"_s) {
         cmXMLElement(epg, "ApplicationType").Content("Windows Phone");
         cmXMLElement(epg, "ApplicationTypeRevision")
           .Content(this->GetApplicationTypeRevision());
-      } else if (this->GetSystemName() == "WindowsStore") {
+      } else if (this->GetSystemName() == "WindowsStore"_s) {
         cmXMLElement(epg, "ApplicationType").Content("Windows Store");
         cmXMLElement(epg, "ApplicationTypeRevision")
           .Content(this->GetApplicationTypeRevision());
-      } else if (this->GetSystemName() == "Android") {
+      } else if (this->GetSystemName() == "Android"_s) {
         cmXMLElement(epg, "ApplicationType").Content("Android");
         cmXMLElement(epg, "ApplicationTypeRevision")
           .Content(this->GetApplicationTypeRevision());
@@ -920,10 +932,10 @@
         cmXMLElement(epg, "WindowsTargetPlatformVersion")
           .Content(this->WindowsTargetPlatformVersion);
       }
-      if (this->GetSystemName() != "Android") {
-        if (this->GetPlatformName() == "ARM64") {
+      if (this->GetSystemName() != "Android"_s) {
+        if (this->GetPlatformName() == "ARM64"_s) {
           cmXMLElement(epg, "WindowsSDKDesktopARM64Support").Content("true");
-        } else if (this->GetPlatformName() == "ARM") {
+        } else if (this->GetPlatformName() == "ARM"_s) {
           cmXMLElement(epg, "WindowsSDKDesktopARMSupport").Content("true");
         }
       }
@@ -970,10 +982,9 @@
   std::vector<std::string> cmd;
   cmd.push_back(this->GetMSBuildCommand());
   cmd.push_back(vcxproj);
-  cmd.push_back("/p:Configuration=Debug");
-  cmd.push_back(cmStrCat("/p:Platform=", this->GetPlatformName()));
-  cmd.push_back(std::string("/p:VisualStudioVersion=") +
-                this->GetIDEVersion());
+  cmd.emplace_back("/p:Configuration=Debug");
+  cmd.emplace_back(cmStrCat("/p:Platform=", this->GetPlatformName()));
+  cmd.emplace_back(cmStrCat("/p:VisualStudioVersion=", this->GetIDEVersion()));
   std::string out;
   int ret = 0;
   cmsys::RegularExpression regex("\n *VCTargetsPath=([^%\r\n]+)[\r\n]");
@@ -1049,7 +1060,7 @@
           break;
         }
         std::string proj = project.GetRelativePath();
-        if (proj.size() > 7 && proj.substr(proj.size() - 7) == ".vfproj") {
+        if (proj.size() > 7 && proj.substr(proj.size() - 7) == ".vfproj"_s) {
           useDevEnv = true;
         }
       }
@@ -1080,7 +1091,7 @@
     makeCommand.Add(makeProgramSelected);
     cm::optional<cmSlnProjectEntry> proj = cm::nullopt;
 
-    if (tname == "clean") {
+    if (tname == "clean"_s) {
       makeCommand.Add(cmStrCat(projectName, ".sln"));
       makeCommand.Add("/t:Clean");
     } else {
@@ -1104,7 +1115,7 @@
         requiresRestore = false;
       } else if (cmValue cached =
                    this->CMakeInstance->GetState()->GetCacheEntryValue(
-                     tname + "_REQUIRES_VS_PACKAGE_RESTORE")) {
+                     cmStrCat(tname, "_REQUIRES_VS_PACKAGE_RESTORE"))) {
         requiresRestore = cached.IsOn();
       } else {
         // There are no package references defined.
@@ -1164,7 +1175,7 @@
       std::string extension =
         cmSystemTools::GetFilenameLastExtension(proj->GetRelativePath());
       extension = cmSystemTools::LowerCase(extension);
-      if (extension == ".csproj") {
+      if (extension == ".csproj"_s) {
         // Use correct platform name
         platform =
           slnData.GetConfigurationTarget(tname, plainConfig, platform);
@@ -1198,9 +1209,10 @@
 {
   // The VS 10 generator needs to create the .rule files on disk.
   // Hide them away under the CMakeFiles directory.
+  cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
   std::string ruleDir = cmStrCat(
     this->GetCMakeInstance()->GetHomeOutputDirectory(), "/CMakeFiles/",
-    cmSystemTools::ComputeStringMD5(cmSystemTools::GetFilenamePath(output)));
+    hasher.HashString(cmSystemTools::GetFilenamePath(output)));
   std::string ruleFile =
     cmStrCat(ruleDir, '/', cmSystemTools::GetFilenameName(output), ".rule");
   return ruleFile;
@@ -1230,7 +1242,6 @@
 {
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
       return "4.0";
 
       // in Visual Studio 2013 they detached the MSBuild tools version
@@ -1272,7 +1283,7 @@
 
 std::string cmGlobalVisualStudio10Generator::GetApplicationTypeRevision() const
 {
-  if (this->GetSystemName() == "Android") {
+  if (this->GetSystemName() == "Android"_s) {
     return this->GetAndroidApplicationTypeRevision();
   }
 
@@ -1303,23 +1314,23 @@
     if (specials.isArray()) {
       for (auto const& special : specials) {
         std::string s = special.asString();
-        if (s == "UserValue") {
+        if (s == "UserValue"_s) {
           value |= cmIDEFlagTable::UserValue;
-        } else if (s == "UserIgnored") {
+        } else if (s == "UserIgnored"_s) {
           value |= cmIDEFlagTable::UserIgnored;
-        } else if (s == "UserRequired") {
+        } else if (s == "UserRequired"_s) {
           value |= cmIDEFlagTable::UserRequired;
-        } else if (s == "Continue") {
+        } else if (s == "Continue"_s) {
           value |= cmIDEFlagTable::Continue;
-        } else if (s == "SemicolonAppendable") {
+        } else if (s == "SemicolonAppendable"_s) {
           value |= cmIDEFlagTable::SemicolonAppendable;
-        } else if (s == "UserFollowing") {
+        } else if (s == "UserFollowing"_s) {
           value |= cmIDEFlagTable::UserFollowing;
-        } else if (s == "CaseInsensitive") {
+        } else if (s == "CaseInsensitive"_s) {
           value |= cmIDEFlagTable::CaseInsensitive;
-        } else if (s == "SpaceAppendable") {
+        } else if (s == "SpaceAppendable"_s) {
           value |= cmIDEFlagTable::SpaceAppendable;
-        } else if (s == "CommaAppendable") {
+        } else if (s == "CommaAppendable"_s) {
           value |= cmIDEFlagTable::CommaAppendable;
         }
       }
@@ -1538,22 +1549,22 @@
   std::string const& toolset = this->GetPlatformToolsetString();
   std::string const useToolset = this->CanonicalToolsetName(toolset);
 
-  if (toolset == "v142") {
+  if (toolset == "v142"_s) {
     return "v142";
   }
-  if (toolset == "v141") {
+  if (toolset == "v141"_s) {
     return "v141";
   }
-  if (useToolset == "v140") {
+  if (useToolset == "v140"_s) {
     return "v140";
   }
-  if (useToolset == "v120") {
+  if (useToolset == "v120"_s) {
     return "v12";
   }
-  if (useToolset == "v110") {
+  if (useToolset == "v110"_s) {
     return "v11";
   }
-  if (useToolset == "v100") {
+  if (useToolset == "v100"_s) {
     return "v10";
   }
   return "";
@@ -1564,22 +1575,22 @@
   std::string const& toolset = this->GetPlatformToolsetString();
   std::string const useToolset = this->CanonicalToolsetName(toolset);
 
-  if (useToolset == "v142") {
+  if (useToolset == "v142"_s) {
     return "v142";
   }
-  if (useToolset == "v141") {
+  if (useToolset == "v141"_s) {
     return "v141";
   }
-  if (useToolset == "v140") {
+  if (useToolset == "v140"_s) {
     return "v140";
   }
-  if (useToolset == "v120") {
+  if (useToolset == "v120"_s) {
     return "v12";
   }
-  if (useToolset == "v110") {
+  if (useToolset == "v110"_s) {
     return "v11";
   }
-  if (useToolset == "v100") {
+  if (useToolset == "v100"_s) {
     return "v10";
   }
   return "";
@@ -1590,17 +1601,17 @@
   std::string const& toolset = this->GetPlatformToolsetString();
   std::string const useToolset = this->CanonicalToolsetName(toolset);
 
-  if ((useToolset == "v140") || (useToolset == "v141") ||
-      (useToolset == "v142")) {
+  if ((useToolset == "v140"_s) || (useToolset == "v141"_s) ||
+      (useToolset == "v142"_s)) {
     return "v14";
   }
-  if (useToolset == "v120") {
+  if (useToolset == "v120"_s) {
     return "v12";
   }
-  if (useToolset == "v110") {
+  if (useToolset == "v110"_s) {
     return "v11";
   }
-  if (useToolset == "v100") {
+  if (useToolset == "v100"_s) {
     return "v10";
   }
   return "";
@@ -1611,17 +1622,17 @@
   std::string const& toolset = this->GetPlatformToolsetString();
   std::string const useToolset = this->CanonicalToolsetName(toolset);
 
-  if ((useToolset == "v140") || (useToolset == "v141") ||
-      (useToolset == "v142")) {
+  if ((useToolset == "v140"_s) || (useToolset == "v141"_s) ||
+      (useToolset == "v142"_s)) {
     return "v14";
   }
-  if (useToolset == "v120") {
+  if (useToolset == "v120"_s) {
     return "v12";
   }
-  if (useToolset == "v110") {
+  if (useToolset == "v110"_s) {
     return "v11";
   }
-  if (useToolset == "v100") {
+  if (useToolset == "v100"_s) {
     return "v10";
   }
   return "";
@@ -1632,22 +1643,22 @@
   std::string const& toolset = this->GetPlatformToolsetString();
   std::string const useToolset = this->CanonicalToolsetName(toolset);
 
-  if (useToolset == "v142") {
+  if (useToolset == "v142"_s) {
     return "v142";
   }
-  if (useToolset == "v141") {
+  if (useToolset == "v141"_s) {
     return "v141";
   }
-  if (useToolset == "v140") {
+  if (useToolset == "v140"_s) {
     return "v140";
   }
-  if (useToolset == "v120") {
+  if (useToolset == "v120"_s) {
     return "v12";
   }
-  if (useToolset == "v110") {
+  if (useToolset == "v110"_s) {
     return "v11";
   }
-  if (useToolset == "v100") {
+  if (useToolset == "v100"_s) {
     return "v10";
   }
   return "";
@@ -1658,17 +1669,17 @@
   std::string const& toolset = this->GetPlatformToolsetString();
   std::string const useToolset = this->CanonicalToolsetName(toolset);
 
-  if ((useToolset == "v140") || (useToolset == "v141") ||
-      (useToolset == "v142")) {
+  if ((useToolset == "v140"_s) || (useToolset == "v141"_s) ||
+      (useToolset == "v142"_s)) {
     return "v14";
   }
-  if (useToolset == "v120") {
+  if (useToolset == "v120"_s) {
     return "v12";
   }
-  if (useToolset == "v110") {
+  if (useToolset == "v110"_s) {
     return "v11";
   }
-  if (useToolset == "v100") {
+  if (useToolset == "v100"_s) {
     return "v10";
   }
   return "";
diff --git a/Source/cmGlobalVisualStudio11Generator.cxx b/Source/cmGlobalVisualStudio11Generator.cxx
index 3ad10eb..0ec5e8b 100644
--- a/Source/cmGlobalVisualStudio11Generator.cxx
+++ b/Source/cmGlobalVisualStudio11Generator.cxx
@@ -3,168 +3,30 @@
 #include "cmGlobalVisualStudio11Generator.h"
 
 #include <cstring>
-#include <sstream>
 #include <utility>
 #include <vector>
 
+#include <cmext/string_view>
+
 #include "cmGlobalGenerator.h"
-#include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-static const char vs11generatorName[] = "Visual Studio 11 2012";
-
-// Map generator name without year to name with year.
-static const char* cmVS11GenName(const std::string& name, std::string& genName)
-{
-  if (strncmp(name.c_str(), vs11generatorName,
-              sizeof(vs11generatorName) - 6) != 0) {
-    return nullptr;
-  }
-  const char* p = name.c_str() + sizeof(vs11generatorName) - 6;
-  if (cmHasLiteralPrefix(p, " 2012")) {
-    p += 5;
-  }
-  genName = std::string(vs11generatorName) + p;
-  return p;
-}
-
-class cmGlobalVisualStudio11Generator::Factory
-  : public cmGlobalGeneratorFactory
-{
-public:
-  std::unique_ptr<cmGlobalGenerator> CreateGlobalGenerator(
-    const std::string& name, bool allowArch, cmake* cm) const override
-  {
-    std::string genName;
-    const char* p = cmVS11GenName(name, genName);
-    if (!p) {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-    if (!*p) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio11Generator(cm, genName, ""));
-    }
-    if (!allowArch || *p++ != ' ') {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-    if (strcmp(p, "Win64") == 0) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio11Generator(cm, genName, "x64"));
-    }
-    if (strcmp(p, "ARM") == 0) {
-      return std::unique_ptr<cmGlobalGenerator>(
-        new cmGlobalVisualStudio11Generator(cm, genName, "ARM"));
-    }
-
-    std::set<std::string> installedSDKs =
-      cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs();
-
-    if (installedSDKs.find(p) == installedSDKs.end()) {
-      return std::unique_ptr<cmGlobalGenerator>();
-    }
-
-    auto ret = std::unique_ptr<cmGlobalVisualStudio11Generator>(
-      new cmGlobalVisualStudio11Generator(cm, name, p));
-    ret->WindowsCEVersion = "8.00";
-    return std::unique_ptr<cmGlobalGenerator>(std::move(ret));
-  }
-
-  cmDocumentationEntry GetDocumentation() const override
-  {
-    return { std::string(vs11generatorName) + " [arch]",
-             "Deprecated.  Generates Visual Studio 2012 project files.  "
-             "Optional [arch] can be \"Win64\" or \"ARM\"." };
-  }
-
-  std::vector<std::string> GetGeneratorNames() const override
-  {
-    std::vector<std::string> names;
-    names.push_back(vs11generatorName);
-    return names;
-  }
-
-  std::vector<std::string> GetGeneratorNamesWithPlatform() const override
-  {
-    std::vector<std::string> names;
-    names.push_back(vs11generatorName + std::string(" ARM"));
-    names.push_back(vs11generatorName + std::string(" Win64"));
-
-    std::set<std::string> installedSDKs =
-      cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs();
-    for (std::string const& i : installedSDKs) {
-      names.push_back(std::string(vs11generatorName) + " " + i);
-    }
-
-    return names;
-  }
-
-  bool SupportsToolset() const override { return true; }
-  bool SupportsPlatform() const override { return true; }
-
-  std::vector<std::string> GetKnownPlatforms() const override
-  {
-    std::vector<std::string> platforms;
-    platforms.emplace_back("x64");
-    platforms.emplace_back("Win32");
-    platforms.emplace_back("ARM");
-
-    std::set<std::string> installedSDKs =
-      cmGlobalVisualStudio11Generator::GetInstalledWindowsCESDKs();
-    for (std::string const& i : installedSDKs) {
-      platforms.emplace_back(i);
-    }
-
-    return platforms;
-  }
-
-  std::string GetDefaultPlatformName() const override { return "Win32"; }
-};
-
-std::unique_ptr<cmGlobalGeneratorFactory>
-cmGlobalVisualStudio11Generator::NewFactory()
-{
-  return std::unique_ptr<cmGlobalGeneratorFactory>(new Factory);
-}
-
 cmGlobalVisualStudio11Generator::cmGlobalVisualStudio11Generator(
   cmake* cm, const std::string& name,
   std::string const& platformInGeneratorName)
   : cmGlobalVisualStudio10Generator(cm, name, platformInGeneratorName)
 {
-  std::string vc11Express;
-  this->ExpressEdition = cmSystemTools::ReadRegistryValue(
-    "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\11.0\\Setup\\VC;"
-    "ProductDir",
-    vc11Express, cmSystemTools::KeyWOW64_32);
-  this->DefaultPlatformToolset = "v110";
-  this->DefaultCLFlagTableName = "v11";
-  this->DefaultCSharpFlagTableName = "v11";
-  this->DefaultLibFlagTableName = "v11";
-  this->DefaultLinkFlagTableName = "v11";
-  this->DefaultMasmFlagTableName = "v11";
-  this->DefaultRCFlagTableName = "v11";
-  this->Version = VSVersion::VS11;
-}
-
-bool cmGlobalVisualStudio11Generator::MatchesGeneratorName(
-  const std::string& name) const
-{
-  std::string genName;
-  if (cmVS11GenName(name, genName)) {
-    return genName == this->GetName();
-  }
-  return false;
 }
 
 void cmGlobalVisualStudio11Generator::EnableLanguage(
   std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
 {
   for (std::string const& it : lang) {
-    if (it == "ASM_MARMASM") {
+    if (it == "ASM_MARMASM"_s) {
       this->MarmasmEnabled = true;
     }
   }
@@ -175,16 +37,18 @@
 bool cmGlobalVisualStudio11Generator::InitializeWindowsPhone(cmMakefile* mf)
 {
   if (!this->SelectWindowsPhoneToolset(this->DefaultPlatformToolset)) {
-    std::ostringstream e;
+    std::string e;
     if (this->DefaultPlatformToolset.empty()) {
-      e << this->GetName() << " supports Windows Phone '8.0', but not '"
-        << this->SystemVersion << "'.  Check CMAKE_SYSTEM_VERSION.";
+      e = cmStrCat(this->GetName(), " supports Windows Phone '8.0', but not '",
+                   this->SystemVersion, "'.  Check CMAKE_SYSTEM_VERSION.");
     } else {
-      e << "A Windows Phone component with CMake requires both the Windows "
-        << "Desktop SDK as well as the Windows Phone '" << this->SystemVersion
-        << "' SDK. Please make sure that you have both installed";
+      e = cmStrCat(
+        "A Windows Phone component with CMake requires both the Windows "
+        "Desktop SDK as well as the Windows Phone '",
+        this->SystemVersion,
+        "' SDK. Please make sure that you have both installed");
     }
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
   return true;
@@ -193,16 +57,18 @@
 bool cmGlobalVisualStudio11Generator::InitializeWindowsStore(cmMakefile* mf)
 {
   if (!this->SelectWindowsStoreToolset(this->DefaultPlatformToolset)) {
-    std::ostringstream e;
+    std::string e;
     if (this->DefaultPlatformToolset.empty()) {
-      e << this->GetName() << " supports Windows Store '8.0', but not '"
-        << this->SystemVersion << "'.  Check CMAKE_SYSTEM_VERSION.";
+      e = cmStrCat(this->GetName(), " supports Windows Store '8.0', but not '",
+                   this->SystemVersion, "'.  Check CMAKE_SYSTEM_VERSION.");
     } else {
-      e << "A Windows Store component with CMake requires both the Windows "
-        << "Desktop SDK as well as the Windows Store '" << this->SystemVersion
-        << "' SDK. Please make sure that you have both installed";
+      e = cmStrCat(
+        "A Windows Store component with CMake requires both the Windows "
+        "Desktop SDK as well as the Windows Store '",
+        this->SystemVersion,
+        "' SDK. Please make sure that you have both installed");
     }
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
   return true;
@@ -211,7 +77,7 @@
 bool cmGlobalVisualStudio11Generator::SelectWindowsPhoneToolset(
   std::string& toolset) const
 {
-  if (this->SystemVersion == "8.0") {
+  if (this->SystemVersion == "8.0"_s) {
     if (this->IsWindowsPhoneToolsetInstalled() &&
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v110_wp80";
@@ -226,7 +92,7 @@
 bool cmGlobalVisualStudio11Generator::SelectWindowsStoreToolset(
   std::string& toolset) const
 {
-  if (this->SystemVersion == "8.0") {
+  if (this->SystemVersion == "8.0"_s) {
     if (this->IsWindowsStoreToolsetInstalled() &&
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v110";
diff --git a/Source/cmGlobalVisualStudio11Generator.h b/Source/cmGlobalVisualStudio11Generator.h
index fd25984..ad12c1f 100644
--- a/Source/cmGlobalVisualStudio11Generator.h
+++ b/Source/cmGlobalVisualStudio11Generator.h
@@ -13,7 +13,6 @@
 #include "cmGlobalVisualStudio10Generator.h"
 #include "cmTransformDepfile.h"
 
-class cmGlobalGeneratorFactory;
 class cmMakefile;
 class cmake;
 
@@ -21,10 +20,6 @@
 class cmGlobalVisualStudio11Generator : public cmGlobalVisualStudio10Generator
 {
 public:
-  static std::unique_ptr<cmGlobalGeneratorFactory> NewFactory();
-
-  bool MatchesGeneratorName(const std::string& name) const override;
-
   void EnableLanguage(std::vector<std::string> const& languages, cmMakefile*,
                       bool optional) override;
 
@@ -58,8 +53,4 @@
 
   /** Return true if target system supports debugging deployment. */
   bool TargetSystemSupportsDeployment() const override;
-
-private:
-  class Factory;
-  friend class Factory;
 };
diff --git a/Source/cmGlobalVisualStudio12Generator.cxx b/Source/cmGlobalVisualStudio12Generator.cxx
index d417f9e..1f1a2c3 100644
--- a/Source/cmGlobalVisualStudio12Generator.cxx
+++ b/Source/cmGlobalVisualStudio12Generator.cxx
@@ -6,6 +6,8 @@
 #include <sstream>
 #include <vector>
 
+#include <cmext/string_view>
+
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
@@ -63,8 +65,8 @@
 
   cmDocumentationEntry GetDocumentation() const override
   {
-    return { std::string(vs12generatorName) + " [arch]",
-             "Generates Visual Studio 2013 project files.  "
+    return { cmStrCat(vs12generatorName, " [arch]"),
+             "Deprecated.  Generates Visual Studio 2013 project files.  "
              "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
 
@@ -78,8 +80,8 @@
   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
   {
     std::vector<std::string> names;
-    names.push_back(vs12generatorName + std::string(" ARM"));
-    names.push_back(vs12generatorName + std::string(" Win64"));
+    names.emplace_back(cmStrCat(vs12generatorName, " ARM"));
+    names.emplace_back(cmStrCat(vs12generatorName, " Win64"));
     return names;
   }
 
@@ -137,8 +139,8 @@
 bool cmGlobalVisualStudio12Generator::ProcessGeneratorToolsetField(
   std::string const& key, std::string const& value)
 {
-  if (key == "host" &&
-      (value == "x64" || value == "x86" || value == "ARM64")) {
+  if (key == "host"_s &&
+      (value == "x64"_s || value == "x86"_s || value == "ARM64"_s)) {
     this->GeneratorToolsetHostArchitecture = value;
     return true;
   }
@@ -149,18 +151,20 @@
 bool cmGlobalVisualStudio12Generator::InitializeWindowsPhone(cmMakefile* mf)
 {
   if (!this->SelectWindowsPhoneToolset(this->DefaultPlatformToolset)) {
-    std::ostringstream e;
+    std::string e;
     if (this->DefaultPlatformToolset.empty()) {
-      e << this->GetName()
-        << " supports Windows Phone '8.0' and '8.1', but "
-           "not '"
-        << this->SystemVersion << "'.  Check CMAKE_SYSTEM_VERSION.";
+      e = cmStrCat(this->GetName(),
+                   " supports Windows Phone '8.0' and '8.1', but "
+                   "not '",
+                   this->SystemVersion, "'.  Check CMAKE_SYSTEM_VERSION.");
     } else {
-      e << "A Windows Phone component with CMake requires both the Windows "
-        << "Desktop SDK as well as the Windows Phone '" << this->SystemVersion
-        << "' SDK. Please make sure that you have both installed";
+      e = cmStrCat(
+        "A Windows Phone component with CMake requires both the Windows "
+        "Desktop SDK as well as the Windows Phone '",
+        this->SystemVersion,
+        "' SDK. Please make sure that you have both installed");
     }
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
   return true;
@@ -169,18 +173,20 @@
 bool cmGlobalVisualStudio12Generator::InitializeWindowsStore(cmMakefile* mf)
 {
   if (!this->SelectWindowsStoreToolset(this->DefaultPlatformToolset)) {
-    std::ostringstream e;
+    std::string e;
     if (this->DefaultPlatformToolset.empty()) {
-      e << this->GetName()
-        << " supports Windows Store '8.0' and '8.1', but "
-           "not '"
-        << this->SystemVersion << "'.  Check CMAKE_SYSTEM_VERSION.";
+      e = cmStrCat(this->GetName(),
+                   " supports Windows Store '8.0' and '8.1', but "
+                   "not '",
+                   this->SystemVersion, "'.  Check CMAKE_SYSTEM_VERSION.");
     } else {
-      e << "A Windows Store component with CMake requires both the Windows "
-        << "Desktop SDK as well as the Windows Store '" << this->SystemVersion
-        << "' SDK. Please make sure that you have both installed";
+      e = cmStrCat(
+        "A Windows Store component with CMake requires both the Windows "
+        "Desktop SDK as well as the Windows Store '",
+        this->SystemVersion,
+        "' SDK. Please make sure that you have both installed");
     }
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
   return true;
@@ -189,7 +195,7 @@
 bool cmGlobalVisualStudio12Generator::SelectWindowsPhoneToolset(
   std::string& toolset) const
 {
-  if (this->SystemVersion == "8.1") {
+  if (this->SystemVersion == "8.1"_s) {
     if (this->IsWindowsPhoneToolsetInstalled() &&
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v120_wp81";
@@ -204,7 +210,7 @@
 bool cmGlobalVisualStudio12Generator::SelectWindowsStoreToolset(
   std::string& toolset) const
 {
-  if (this->SystemVersion == "8.1") {
+  if (this->SystemVersion == "8.1"_s) {
     if (this->IsWindowsStoreToolsetInstalled() &&
         this->IsWindowsDesktopToolsetInstalled()) {
       toolset = "v120";
diff --git a/Source/cmGlobalVisualStudio14Generator.cxx b/Source/cmGlobalVisualStudio14Generator.cxx
index 01294dd..68b4d1a 100644
--- a/Source/cmGlobalVisualStudio14Generator.cxx
+++ b/Source/cmGlobalVisualStudio14Generator.cxx
@@ -67,7 +67,7 @@
 
   cmDocumentationEntry GetDocumentation() const override
   {
-    return { std::string(vs14generatorName) + " [arch]",
+    return { cmStrCat(vs14generatorName, " [arch]"),
              "Generates Visual Studio 2015 project files.  "
              "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
@@ -82,8 +82,8 @@
   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
   {
     std::vector<std::string> names;
-    names.push_back(vs14generatorName + std::string(" ARM"));
-    names.push_back(vs14generatorName + std::string(" Win64"));
+    names.emplace_back(cmStrCat(vs14generatorName, " ARM"));
+    names.emplace_back(cmStrCat(vs14generatorName, " Win64"));
     return names;
   }
 
@@ -250,7 +250,7 @@
     "given platform specification containing a\n"
     "  version=" << *this->GeneratorPlatformVersion << "\n"
     "field.  The version field is not supported when targeting\n"
-    "  " << this->SystemName << " " << this->SystemVersion << "\n"
+    "  " << this->SystemName << ' ' << this->SystemVersion << '\n'
     ;
   /* clang-format on */
   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
@@ -259,19 +259,21 @@
 
 bool cmGlobalVisualStudio14Generator::InitializeWindowsStore(cmMakefile* mf)
 {
-  std::ostringstream e;
   if (!this->SelectWindowsStoreToolset(this->DefaultPlatformToolset)) {
+    std::string e;
     if (this->DefaultPlatformToolset.empty()) {
-      e << this->GetName()
-        << " supports Windows Store '8.0', '8.1' and "
-           "'10.0', but not '"
-        << this->SystemVersion << "'.  Check CMAKE_SYSTEM_VERSION.";
+      e = cmStrCat(this->GetName(),
+                   " supports Windows Store '8.0', '8.1' and "
+                   "'10.0', but not '",
+                   this->SystemVersion, "'.  Check CMAKE_SYSTEM_VERSION.");
     } else {
-      e << "A Windows Store component with CMake requires both the Windows "
-        << "Desktop SDK as well as the Windows Store '" << this->SystemVersion
-        << "' SDK. Please make sure that you have both installed";
+      e = cmStrCat(
+        "A Windows Store component with CMake requires both the Windows "
+        "Desktop SDK as well as the Windows Store '",
+        this->SystemVersion,
+        "' SDK. Please make sure that you have both installed");
     }
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(MessageType::FATAL_ERROR, e);
     return false;
   }
   return true;
@@ -285,7 +287,7 @@
 bool cmGlobalVisualStudio14Generator::ProcessGeneratorPlatformField(
   std::string const& key, std::string const& value)
 {
-  if (key == "version") {
+  if (key == "version"_s) {
     this->GeneratorPlatformVersion = value;
     return true;
   }
@@ -308,7 +310,7 @@
       return false;
     }
 
-    if (this->SystemName == "WindowsStore") {
+    if (this->SystemName == "WindowsStore"_s) {
       mf->IssueMessage(
         MessageType::FATAL_ERROR,
         "Could not find an appropriate version of the Windows 10 SDK"
@@ -328,10 +330,11 @@
   if (!this->WindowsTargetPlatformVersion.empty() &&
       !cmSystemTools::VersionCompareEqual(this->WindowsTargetPlatformVersion,
                                           this->SystemVersion)) {
-    std::ostringstream e;
-    e << "Selecting Windows SDK version " << this->WindowsTargetPlatformVersion
-      << " to target Windows " << this->SystemVersion << ".";
-    mf->DisplayStatus(e.str(), -1);
+    mf->DisplayStatus(cmStrCat("Selecting Windows SDK version ",
+                               this->WindowsTargetPlatformVersion,
+                               " to target Windows ", this->SystemVersion,
+                               '.'),
+                      -1);
   }
   mf->AddDefinition("CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION",
                     this->WindowsTargetPlatformVersion);
@@ -416,7 +419,7 @@
 {
   bool operator()(std::string const& p)
   {
-    return !cmSystemTools::FileExists(p + "/um/windows.h", true);
+    return !cmSystemTools::FileExists(cmStrCat(p, "/um/windows.h"), true);
   }
 };
 class WindowsSDKTooRecent
@@ -473,7 +476,7 @@
   std::vector<std::string> sdks;
   // Grab the paths of the different SDKs that are installed
   for (std::string const& i : win10Roots) {
-    std::string path = i + "/Include/*";
+    std::string path = cmStrCat(i, "/Include/*");
     cmSystemTools::GlobDirs(path, sdks);
   }
 
diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx
index bcb26cc..8375b72 100644
--- a/Source/cmGlobalVisualStudio71Generator.cxx
+++ b/Source/cmGlobalVisualStudio71Generator.cxx
@@ -86,7 +86,7 @@
 {
   fout << "\tGlobalSection(SolutionConfiguration) = preSolution\n";
   for (std::string const& i : configs) {
-    fout << "\t\t" << i << " = " << i << "\n";
+    fout << "\t\t" << i << " = " << i << '\n';
   }
   fout << "\tEndGlobalSection\n";
 }
@@ -136,9 +136,9 @@
          << uname << ".vcproj" << "\", \"{"
          << this->GetGUID(uname) << "}\"\n"
          << "\tProjectSection(ProjectDependencies) = postProject\n"
-         << "\t\t{" << guid << "} = {" << guid << "}\n"
-         << "\tEndProjectSection\n"
-         << "EndProject\n";
+            "\t\t{" << guid << "} = {" << guid << "}\n"
+            "\tEndProjectSection\n"
+            "EndProject\n";
     /* clang-format on */
   }
 }
@@ -209,19 +209,19 @@
     cmList mapConfig;
     const char* dstConfig = i.c_str();
     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
-      if (cmValue m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
-                                         cmSystemTools::UpperCase(i))) {
+      if (cmValue m = target.GetProperty(
+            cmStrCat("MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(i)))) {
         mapConfig.assign(*m);
         if (!mapConfig.empty()) {
           dstConfig = mapConfig[0].c_str();
         }
       }
     }
-    fout << "\t\t{" << guid << "}." << i << ".ActiveCfg = " << dstConfig << "|"
+    fout << "\t\t{" << guid << "}." << i << ".ActiveCfg = " << dstConfig << '|'
          << platformName << std::endl;
     auto ci = configsPartOfDefaultBuild.find(i);
     if (!(ci == configsPartOfDefaultBuild.end())) {
-      fout << "\t\t{" << guid << "}." << i << ".Build.0 = " << dstConfig << "|"
+      fout << "\t\t{" << guid << "}." << i << ".Build.0 = " << dstConfig << '|'
            << platformName << std::endl;
     }
   }
diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx
index b254777..1abdd0b 100644
--- a/Source/cmGlobalVisualStudio7Generator.cxx
+++ b/Source/cmGlobalVisualStudio7Generator.cxx
@@ -158,7 +158,7 @@
   std::string vskey;
 
   // Search in standard location.
-  vskey = this->GetRegistryBase() + ";InstallDir";
+  vskey = cmStrCat(this->GetRegistryBase(), ";InstallDir");
   if (cmSystemTools::ReadRegistryValue(vskey, vscmd,
                                        cmSystemTools::KeyWOW64_32)) {
     cmSystemTools::ConvertToUnixSlashes(vscmd);
@@ -189,25 +189,25 @@
   const std::string& location)
 {
   std::string extension = cmSystemTools::GetFilenameLastExtension(location);
-  if (extension == ".vbproj") {
+  if (extension == ".vbproj"_s) {
     return "F184B08F-C81C-45F6-A57F-5ABD9991F28F";
   }
-  if (extension == ".csproj") {
+  if (extension == ".csproj"_s) {
     return "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC";
   }
-  if (extension == ".fsproj") {
+  if (extension == ".fsproj"_s) {
     return "F2A71F9B-5D33-465A-A702-920D77279786";
   }
-  if (extension == ".vdproj") {
+  if (extension == ".vdproj"_s) {
     return "54435603-DBB4-11D2-8724-00A0C9A8B90C";
   }
-  if (extension == ".dbproj") {
+  if (extension == ".dbproj"_s) {
     return "C8D11400-126E-41CD-887F-60BD40844F9E";
   }
-  if (extension == ".wixproj") {
+  if (extension == ".wixproj"_s) {
     return "930C7802-8A8C-48F9-8165-68863BCCD9DD";
   }
-  if (extension == ".pyproj") {
+  if (extension == ".pyproj"_s) {
     return "888888A0-9F3D-457C-B088-3A5042F75D52";
   }
   return "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942";
@@ -252,14 +252,14 @@
       continue;
     }
     bool clean = false;
-    if (realTarget == "clean") {
+    if (realTarget == "clean"_s) {
       clean = true;
       realTarget = "ALL_BUILD";
     }
     GeneratedMakeCommand makeCommand;
     makeCommand.RequiresOutputForward = requiresOutputForward;
     makeCommand.Add(makeProgramSelected);
-    makeCommand.Add(projectName + ".sln");
+    makeCommand.Add(cmStrCat(projectName, ".sln"));
     makeCommand.Add((clean ? "/clean" : "/build"));
     makeCommand.Add((config.empty() ? "Debug" : config));
     makeCommand.Add("/project");
@@ -330,23 +330,23 @@
     }
   }
 
-  if (this->Version == VSVersion::VS11 &&
+  if (this->Version == VSVersion::VS12 &&
       !this->CMakeInstance->GetIsInTryCompile()) {
-    std::string cmakeWarnVS11;
+    std::string cmakeWarnVS12;
     if (cmValue cached = this->CMakeInstance->GetState()->GetCacheEntryValue(
-          "CMAKE_WARN_VS11")) {
-      this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS11");
-      cmakeWarnVS11 = *cached;
+          "CMAKE_WARN_VS12")) {
+      this->CMakeInstance->MarkCliAsUsed("CMAKE_WARN_VS12");
+      cmakeWarnVS12 = *cached;
     } else {
-      cmSystemTools::GetEnv("CMAKE_WARN_VS11", cmakeWarnVS11);
+      cmSystemTools::GetEnv("CMAKE_WARN_VS12", cmakeWarnVS12);
     }
-    if (cmakeWarnVS11.empty() || !cmIsOff(cmakeWarnVS11)) {
+    if (cmakeWarnVS12.empty() || !cmIsOff(cmakeWarnVS12)) {
       this->CMakeInstance->IssueMessage(
         MessageType::WARNING,
-        "The \"Visual Studio 11 2012\" generator is deprecated "
+        "The \"Visual Studio 12 2013\" generator is deprecated "
         "and will be removed in a future version of CMake."
         "\n"
-        "Add CMAKE_WARN_VS11=OFF to the cache to disable this warning.");
+        "Add CMAKE_WARN_VS12=OFF to the cache to disable this warning.");
     }
   }
 }
@@ -435,14 +435,6 @@
       target->CheckCxxModuleStatus(c);
     }
 
-    if (target->HaveCxx20ModuleSources() && !this->SupportsCxxModuleDyndep()) {
-      root->GetMakefile()->IssueMessage(
-        MessageType::FATAL_ERROR,
-        cmStrCat("The \"", target->GetName(),
-                 "\" target contains C++ module sources which are not "
-                 "supported by the generator"));
-    }
-
     // handle external vc project files
     cmValue expath = target->GetProperty("EXTERNAL_MSPROJECT");
     if (expath) {
@@ -459,7 +451,7 @@
         cmLocalGenerator* lg = target->GetLocalGenerator();
         std::string dir = lg->GetCurrentBinaryDirectory();
         dir = root->MaybeRelativeToCurBinDir(dir);
-        if (dir == ".") {
+        if (dir == "."_s) {
           dir.clear(); // msbuild cannot handle ".\" prefix
         }
         this->WriteProject(fout, *vcprojName, dir, target);
@@ -483,12 +475,12 @@
           }
 
           if (cumulativePath.empty()) {
-            cumulativePath = "CMAKE_FOLDER_GUID_" + iter;
+            cumulativePath = cmStrCat("CMAKE_FOLDER_GUID_", iter);
           } else {
-            VisualStudioFolders[cumulativePath].insert(cumulativePath + "/" +
-                                                       iter);
+            VisualStudioFolders[cumulativePath].insert(
+              cmStrCat(cumulativePath, '/', iter));
 
-            cumulativePath = cumulativePath + "/" + iter;
+            cumulativePath = cmStrCat(cumulativePath, '/', iter);
           }
         }
 
@@ -552,7 +544,8 @@
 void cmGlobalVisualStudio7Generator::WriteSLNGlobalSections(
   std::ostream& fout, cmLocalGenerator* root)
 {
-  std::string const guid = this->GetGUID(root->GetProjectName() + ".sln");
+  std::string const guid =
+    this->GetGUID(cmStrCat(root->GetProjectName(), ".sln"));
   bool extensibilityGlobalsOverridden = false;
   bool extensibilityAddInsOverridden = false;
   const std::vector<std::string> propKeys =
@@ -572,14 +565,15 @@
       }
       if (!name.empty()) {
         bool addGuid = false;
-        if (name == "ExtensibilityGlobals" && sectionType == "postSolution") {
+        if (name == "ExtensibilityGlobals"_s &&
+            sectionType == "postSolution"_s) {
           addGuid = true;
           extensibilityGlobalsOverridden = true;
-        } else if (name == "ExtensibilityAddIns" &&
-                   sectionType == "postSolution") {
+        } else if (name == "ExtensibilityAddIns"_s &&
+                   sectionType == "postSolution"_s) {
           extensibilityAddInsOverridden = true;
         }
-        fout << "\tGlobalSection(" << name << ") = " << sectionType << "\n";
+        fout << "\tGlobalSection(" << name << ") = " << sectionType << '\n';
         cmValue p = root->GetMakefile()->GetProperty(it);
         cmList keyValuePairs{ *p };
         for (std::string const& itPair : keyValuePairs) {
@@ -589,8 +583,8 @@
               cmTrimWhitespace(itPair.substr(0, posEqual));
             const std::string value =
               cmTrimWhitespace(itPair.substr(posEqual + 1));
-            fout << "\t\t" << key << " = " << value << "\n";
-            if (key == "SolutionGuid") {
+            fout << "\t\t" << key << " = " << value << '\n';
+            if (key == "SolutionGuid"_s) {
               addGuid = false;
             }
           }
@@ -679,7 +673,7 @@
 
 std::string cmGlobalVisualStudio7Generator::GetGUID(std::string const& name)
 {
-  std::string const& guidStoreName = name + "_GUID_CMAKE";
+  std::string const& guidStoreName = cmStrCat(name, "_GUID_CMAKE");
   if (cmValue storedGUID =
         this->CMakeInstance->GetCacheDefinition(guidStoreName)) {
     return *storedGUID;
@@ -704,9 +698,7 @@
   const std::string& suffix, std::string& dir)
 {
   if (!config.empty()) {
-    dir += prefix;
-    dir += config;
-    dir += suffix;
+    dir += cmStrCat(prefix, config, suffix);
   }
 }
 
@@ -727,7 +719,7 @@
       // check if target <t> is part of default build
       if (target->GetName() == t) {
         const std::string propertyName =
-          "CMAKE_VS_INCLUDE_" + t + "_TO_DEFAULT_BUILD";
+          cmStrCat("CMAKE_VS_INCLUDE_", t, "_TO_DEFAULT_BUILD");
         // inspect CMAKE_VS_INCLUDE_<t>_TO_DEFAULT_BUILD properties
         for (std::string const& i : configs) {
           cmValue propertyValue =
diff --git a/Source/cmGlobalVisualStudio8Generator.cxx b/Source/cmGlobalVisualStudio8Generator.cxx
index 3125eb5..0d357ad 100644
--- a/Source/cmGlobalVisualStudio8Generator.cxx
+++ b/Source/cmGlobalVisualStudio8Generator.cxx
@@ -288,7 +288,7 @@
     for (const auto& gi : generators) {
       stampFile = cmStrCat(gi->GetMakefile()->GetCurrentBinaryDirectory(),
                            "/CMakeFiles/generate.stamp");
-      fout << stampFile << "\n";
+      fout << stampFile << '\n';
       stamps.push_back(stampFile);
     }
   }
@@ -325,7 +325,7 @@
     std::string argS = cmStrCat("-S", lg.GetSourceDirectory());
     std::string argB = cmStrCat("-B", lg.GetBinaryDirectory());
     std::string const sln =
-      lg.GetBinaryDirectory() + "/" + lg.GetProjectName() + ".sln";
+      cmStrCat(lg.GetBinaryDirectory(), '/', lg.GetProjectName(), ".sln");
     cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
       { cmSystemTools::GetCMakeCommand(), argS, argB, "--check-stamp-list",
         stampList, "--vs-solution-file", sln });
@@ -348,7 +348,7 @@
           lg.AddCustomCommandToOutput(std::move(cc), true)) {
       gt->AddSource(file->ResolveFullPath());
     } else {
-      cmSystemTools::Error("Error adding rule for " + stamps[0]);
+      cmSystemTools::Error(cmStrCat("Error adding rule for ", stamps[0]));
     }
   }
 
@@ -376,8 +376,8 @@
 {
   fout << "\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n";
   for (std::string const& i : configs) {
-    fout << "\t\t" << i << "|" << this->GetPlatformName() << " = " << i << "|"
-         << this->GetPlatformName() << "\n";
+    fout << "\t\t" << i << '|' << this->GetPlatformName() << " = " << i << '|'
+         << this->GetPlatformName() << '\n';
   }
   fout << "\tEndGlobalSection\n";
 }
@@ -393,33 +393,33 @@
     cmList mapConfig;
     const char* dstConfig = i.c_str();
     if (target.GetProperty("EXTERNAL_MSPROJECT")) {
-      if (cmValue m = target.GetProperty("MAP_IMPORTED_CONFIG_" +
-                                         cmSystemTools::UpperCase(i))) {
+      if (cmValue m = target.GetProperty(
+            cmStrCat("MAP_IMPORTED_CONFIG_", cmSystemTools::UpperCase(i)))) {
         mapConfig.assign(*m);
         if (!mapConfig.empty()) {
           dstConfig = mapConfig[0].c_str();
         }
       }
     }
-    fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
-         << ".ActiveCfg = " << dstConfig << "|"
+    fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName()
+         << ".ActiveCfg = " << dstConfig << '|'
          << (!platformMapping.empty() ? platformMapping
                                       : this->GetPlatformName())
-         << "\n";
+         << '\n';
     auto ci = configsPartOfDefaultBuild.find(i);
     if (!(ci == configsPartOfDefaultBuild.end())) {
-      fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
-           << ".Build.0 = " << dstConfig << "|"
+      fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName()
+           << ".Build.0 = " << dstConfig << '|'
            << (!platformMapping.empty() ? platformMapping
                                         : this->GetPlatformName())
-           << "\n";
+           << '\n';
     }
     if (this->NeedsDeploy(target, dstConfig)) {
-      fout << "\t\t{" << guid << "}." << i << "|" << this->GetPlatformName()
-           << ".Deploy.0 = " << dstConfig << "|"
+      fout << "\t\t{" << guid << "}." << i << '|' << this->GetPlatformName()
+           << ".Deploy.0 = " << dstConfig << '|'
            << (!platformMapping.empty() ? platformMapping
                                         : this->GetPlatformName())
-           << "\n";
+           << '\n';
     }
   }
 }
diff --git a/Source/cmGlobalVisualStudio9Generator.cxx b/Source/cmGlobalVisualStudio9Generator.cxx
index e396405..de2153d 100644
--- a/Source/cmGlobalVisualStudio9Generator.cxx
+++ b/Source/cmGlobalVisualStudio9Generator.cxx
@@ -9,6 +9,7 @@
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGeneratorFactory.h"
 #include "cmGlobalVisualStudioGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmVisualStudioWCEPlatformParser.h"
 
@@ -63,7 +64,7 @@
 
   cmDocumentationEntry GetDocumentation() const override
   {
-    return { std::string(vs9generatorName) + " [arch]",
+    return { cmStrCat(vs9generatorName, " [arch]"),
              "Deprecated.  Generates Visual Studio 2008 project files.  "
              "Optional [arch] can be \"Win64\" or \"IA64\"." };
   }
@@ -71,21 +72,21 @@
   std::vector<std::string> GetGeneratorNames() const override
   {
     std::vector<std::string> names;
-    names.push_back(vs9generatorName);
+    names.emplace_back(vs9generatorName);
     return names;
   }
 
   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
   {
     std::vector<std::string> names;
-    names.push_back(vs9generatorName + std::string(" Win64"));
-    names.push_back(vs9generatorName + std::string(" IA64"));
+    names.emplace_back(cmStrCat(vs9generatorName, " Win64"));
+    names.emplace_back(cmStrCat(vs9generatorName, " IA64"));
     cmVisualStudioWCEPlatformParser parser;
     parser.ParseVersion("9.0");
     const std::vector<std::string>& availablePlatforms =
       parser.GetAvailablePlatforms();
     for (std::string const& i : availablePlatforms) {
-      names.push_back("Visual Studio 9 2008 " + i);
+      names.emplace_back(cmStrCat("Visual Studio 9 2008 ", i));
     }
     return names;
   }
@@ -144,7 +145,7 @@
     cmSystemTools::ConvertToUnixSlashes(base);
 
     // 9.0 macros folder:
-    path = base + "/VSMacros80";
+    path = cmStrCat(base, "/VSMacros80");
     // *NOT* a typo; right now in Visual Studio 2008 beta the macros
     // folder is VSMacros80... They may change it to 90 before final
     // release of 2008 or they may not... we'll have to keep our eyes
diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx
index 6b024db..5305fec 100644
--- a/Source/cmGlobalVisualStudioGenerator.cxx
+++ b/Source/cmGlobalVisualStudioGenerator.cxx
@@ -12,6 +12,7 @@
 
 #include <cm/iterator>
 #include <cm/memory>
+#include <cmext/string_view>
 
 #include <windows.h>
 
@@ -78,9 +79,9 @@
   if (!this->InitializePlatform(mf)) {
     return false;
   }
-  if (this->GetPlatformName() == "x64") {
+  if (this->GetPlatformName() == "x64"_s) {
     mf->AddDefinition("CMAKE_FORCE_WIN64", "TRUE");
-  } else if (this->GetPlatformName() == "Itanium") {
+  } else if (this->GetPlatformName() == "Itanium"_s) {
     mf->AddDefinition("CMAKE_FORCE_IA64", "TRUE");
   }
   mf->AddDefinition("CMAKE_VS_PLATFORM_NAME", this->GetPlatformName());
@@ -105,8 +106,6 @@
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return "9.0";
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
-      return "11.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "12.0";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -132,14 +131,6 @@
       fout << "Microsoft Visual Studio Solution File, Format Version 10.00\n";
       fout << "# Visual Studio 2008\n";
       break;
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
-      fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
-      if (this->ExpressEdition) {
-        fout << "# Visual Studio Express 2012 for Windows Desktop\n";
-      } else {
-        fout << "# Visual Studio 2012\n";
-      }
-      break;
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       fout << "Microsoft Visual Studio Solution File, Format Version 12.00\n";
       if (this->ExpressEdition) {
@@ -194,8 +185,8 @@
 
 std::string cmGlobalVisualStudioGenerator::GetRegistryBase(const char* version)
 {
-  std::string key = R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\)";
-  return key + version;
+  return cmStrCat(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\)",
+                  version);
 }
 
 void cmGlobalVisualStudioGenerator::AddExtraIDETargets()
@@ -252,12 +243,12 @@
   std::string tgtDir = gt->LocalGenerator->GetTargetDirectory(gt);
   if (!tgtDir.empty()) {
     dir += tgtDir;
-    dir += "/";
+    dir += '/';
   }
   const char* cd = this->GetCMakeCFGIntDir();
   if (cd && *cd) {
     dir += cd;
-    dir += "/";
+    dir += '/';
   }
   gt->ObjectDirectory = dir;
 }
@@ -284,7 +275,7 @@
     std::string src = cmStrCat(cmSystemTools::GetCMakeRoot(),
                                "/Templates/" CMAKE_VSMACROS_FILENAME);
 
-    std::string dst = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
+    std::string dst = cmStrCat(dir, "/CMakeMacros/" CMAKE_VSMACROS_FILENAME);
 
     // Copy the macros file to the user directory only if the
     // destination does not exist or the source location is newer.
@@ -295,8 +286,8 @@
     if (!cmSystemTools::FileTimeCompare(src, dst, &res) || res > 0) {
       if (!cmSystemTools::CopyFileAlways(src, dst)) {
         std::ostringstream oss;
-        oss << "Could not copy from: " << src << std::endl;
-        oss << "                 to: " << dst << std::endl;
+        oss << "Could not copy from: " << src << std::endl
+            << "                 to: " << dst << std::endl;
         cmSystemTools::Message(oss.str(), "Warning");
       }
     }
@@ -319,7 +310,8 @@
   //  - there were .sln/.vcproj files changed during generation
   //
   if (!dir.empty()) {
-    std::string macrosFile = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME;
+    std::string macrosFile =
+      cmStrCat(dir, "/CMakeMacros/" CMAKE_VSMACROS_FILENAME);
     std::string nextSubkeyName;
     if (cmSystemTools::FileExists(macrosFile) &&
         IsVisualStudioMacrosFileRegistered(
@@ -528,9 +520,9 @@
     }
     root->GetMakefile()->IssueMessage(
       MessageType::AUTHOR_WARNING,
-      "Directory property VS_STARTUP_PROJECT specifies target "
-      "'" +
-        startup + "' that does not exist.  Ignoring.");
+      cmStrCat("Directory property VS_STARTUP_PROJECT specifies target "
+               "'",
+               startup, "' that does not exist.  Ignoring."));
   }
 
   // default, if not specified
@@ -556,7 +548,7 @@
   LONG result = ERROR_SUCCESS;
   DWORD index = 0;
 
-  keyname = regKeyBase + "\\OtherProjects7";
+  keyname = cmStrCat(regKeyBase, "\\OtherProjects7");
   hkey = nullptr;
   result =
     RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
@@ -648,7 +640,7 @@
   // as the name of the next subkey.
   nextAvailableSubKeyName = std::to_string(index);
 
-  keyname = regKeyBase + "\\RecordingProject7";
+  keyname = cmStrCat(regKeyBase, "\\RecordingProject7");
   hkey = nullptr;
   result =
     RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
@@ -694,7 +686,7 @@
                                     const std::string& macrosFile,
                                     const std::string& regKeyBase)
 {
-  std::string keyname = regKeyBase + "\\OtherProjects7";
+  std::string keyname = cmStrCat(regKeyBase, "\\OtherProjects7");
   HKEY hkey = nullptr;
   LONG result =
     RegOpenKeyExW(HKEY_CURRENT_USER, cmsys::Encoding::ToWide(keyname).c_str(),
@@ -829,7 +821,7 @@
   // Intel Fortran .vfproj files do support the resource compiler.
   languages.erase("RC");
 
-  return languages.size() == 1 && *languages.begin() == "Fortran";
+  return languages.size() == 1 && *languages.begin() == "Fortran"_s;
 }
 
 bool cmGlobalVisualStudioGenerator::IsInSolution(
@@ -915,10 +907,10 @@
   cmSystemTools::ReplaceString(obj_dir_expanded, this->GetCMakeCFGIntDir(),
                                configName.c_str());
   cmSystemTools::MakeDirectory(obj_dir_expanded);
-  std::string const objs_file = obj_dir_expanded + "/objects.txt";
+  std::string const objs_file = cmStrCat(obj_dir_expanded, "/objects.txt");
   cmGeneratedFileStream fout(objs_file.c_str());
   if (!fout) {
-    cmSystemTools::Error("could not open " + objs_file);
+    cmSystemTools::Error(cmStrCat("could not open ", objs_file));
     return;
   }
 
@@ -929,7 +921,7 @@
       // It must exist because we populated the mapping just above.
       const auto& v = mapping[it];
       assert(!v.empty());
-      std::string objFile = obj_dir + v;
+      std::string objFile = cmStrCat(obj_dir, v);
       objs.push_back(objFile);
     }
     std::vector<cmSourceFile const*> externalObjectSources;
@@ -985,7 +977,7 @@
                                          const std::string& projectName,
                                          bool dryRun)
 {
-  std::string sln = bindir + "/" + projectName + ".sln";
+  std::string sln = cmStrCat(bindir, '/', projectName, ".sln");
 
   if (dryRun) {
     return cmSystemTools::FileExists(sln, true);
diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h
index 52db98d..76713fa 100644
--- a/Source/cmGlobalVisualStudioGenerator.h
+++ b/Source/cmGlobalVisualStudioGenerator.h
@@ -10,7 +10,7 @@
 #include <string>
 #include <vector>
 
-#include "cm_codecvt.hxx"
+#include "cm_codecvt_Encoding.hxx"
 
 #include "cmGlobalGenerator.h"
 #include "cmTargetDepend.h"
@@ -35,7 +35,6 @@
   enum class VSVersion : uint16_t
   {
     VS9 = 90,
-    VS11 = 110,
     VS12 = 120,
     /* VS13 = 130 was skipped */
     VS14 = 140,
@@ -120,9 +119,9 @@
 
   /** Get encoding used by generator for generated source files
    */
-  codecvt::Encoding GetMakefileEncoding() const override
+  codecvt_Encoding GetMakefileEncoding() const override
   {
-    return codecvt::ANSI;
+    return codecvt_Encoding::ANSI;
   }
 
   class TargetSet : public std::set<cmGeneratorTarget const*>
diff --git a/Source/cmGlobalVisualStudioVersionedGenerator.cxx b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
index 14c7d0f..d816d7b 100644
--- a/Source/cmGlobalVisualStudioVersionedGenerator.cxx
+++ b/Source/cmGlobalVisualStudioVersionedGenerator.cxx
@@ -127,8 +127,6 @@
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return 9;
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
-      return 11;
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return 12;
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -149,8 +147,6 @@
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return "v90";
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
-      return "v110";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "v120";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -171,8 +167,6 @@
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
       return "9";
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
-      return "11";
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "12";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -192,7 +186,6 @@
 {
   switch (v) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -218,7 +211,7 @@
   if (cmHasLiteralPrefix(p, " 2017")) {
     p += 5;
   }
-  genName = std::string(vs15generatorName) + p;
+  genName = cmStrCat(vs15generatorName, p);
   return p;
 }
 
@@ -257,7 +250,7 @@
 
   cmDocumentationEntry GetDocumentation() const override
   {
-    return { std::string(vs15generatorName) + " [arch]",
+    return { cmStrCat(vs15generatorName, " [arch]"),
              "Generates Visual Studio 2017 project files.  "
              "Optional [arch] can be \"Win64\" or \"ARM\"." };
   }
@@ -272,8 +265,8 @@
   std::vector<std::string> GetGeneratorNamesWithPlatform() const override
   {
     std::vector<std::string> names;
-    names.push_back(vs15generatorName + std::string(" ARM"));
-    names.push_back(vs15generatorName + std::string(" Win64"));
+    names.emplace_back(cmStrCat(vs15generatorName, " ARM"));
+    names.emplace_back(cmStrCat(vs15generatorName, " Win64"));
     return names;
   }
 
@@ -313,7 +306,7 @@
   if (cmHasLiteralPrefix(p, " 2019")) {
     p += 5;
   }
-  genName = std::string(vs16generatorName) + p;
+  genName = cmStrCat(vs16generatorName, p);
   return p;
 }
 
@@ -327,7 +320,7 @@
   if (cmHasLiteralPrefix(p, " 2022")) {
     p += 5;
   }
-  genName = std::string(vs17generatorName) + p;
+  genName = cmStrCat(vs17generatorName, p);
   return p;
 }
 
@@ -493,7 +486,6 @@
   std::string genName;
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
       break;
@@ -532,20 +524,21 @@
   if (!this->GeneratorInstanceVersion.empty()) {
     std::string const majorStr = VSVersionToMajorString(this->Version);
     cmsys::RegularExpression versionRegex(
-      cmStrCat("^", majorStr, R"(\.[0-9]+\.[0-9]+\.[0-9]+$)"));
+      cmStrCat('^', majorStr, R"(\.[0-9]+\.[0-9]+\.[0-9]+$)"));
     if (!versionRegex.find(this->GeneratorInstanceVersion)) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given instance specification\n"
-        "  " << i << "\n"
-        "but the version field is not 4 integer components"
-        " starting in " << majorStr << "."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Generator\n"
+                 "  ",
+                 this->GetName(),
+                 "\n"
+                 "given instance specification\n"
+                 "  ",
+                 i,
+                 "\n"
+                 "but the version field is not 4 integer components"
+                 " starting in ",
+                 majorStr, '.'));
       return false;
     }
   }
@@ -574,14 +567,13 @@
       return false;
     }
   } else if (!this->vsSetupAPIHelper.GetVSInstanceInfo(vsInstance)) {
-    std::ostringstream e;
-    /* clang-format off */
-    e <<
-      "Generator\n"
-      "  " << this->GetName() << "\n"
-      "could not find any instance of Visual Studio.\n";
-    /* clang-format on */
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Generator\n"
+               "  ",
+               this->GetName(),
+               "\n"
+               "could not find any instance of Visual Studio.\n"));
     return false;
   }
 
@@ -627,47 +619,47 @@
   for (; fi != fields.end(); ++fi) {
     std::string::size_type pos = fi->find('=');
     if (pos == fi->npos) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given instance specification\n"
-        "  " << is << "\n"
-        "that contains a field after the first ',' with no '='."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Generator\n"
+                 "  ",
+                 this->GetName(),
+                 "\n"
+                 "given instance specification\n"
+                 "  ",
+                 is,
+                 "\n"
+                 "that contains a field after the first ',' with no '='."));
       return false;
     }
     std::string const key = fi->substr(0, pos);
     std::string const value = fi->substr(pos + 1);
     if (!handled.insert(key).second) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given instance specification\n"
-        "  " << is << "\n"
-        "that contains duplicate field key '" << key << "'."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("Generator\n"
+                                "  ",
+                                this->GetName(),
+                                "\n"
+                                "given instance specification\n"
+                                "  ",
+                                is,
+                                "\n"
+                                "that contains duplicate field key '",
+                                key, "'."));
       return false;
     }
     if (!this->ProcessGeneratorInstanceField(key, value)) {
-      std::ostringstream e;
-      /* clang-format off */
-      e <<
-        "Generator\n"
-        "  " << this->GetName() << "\n"
-        "given instance specification\n"
-        "  " << is << "\n"
-        "that contains invalid field '" << *fi << "'."
-        ;
-      /* clang-format on */
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(MessageType::FATAL_ERROR,
+                       cmStrCat("Generator\n"
+                                "  ",
+                                this->GetName(),
+                                "\n"
+                                "given instance specification\n"
+                                "  ",
+                                is,
+                                "\n"
+                                "that contains invalid field '",
+                                *fi, "'."));
       return false;
     }
   }
@@ -685,7 +677,7 @@
 bool cmGlobalVisualStudioVersionedGenerator::ProcessGeneratorInstanceField(
   std::string const& key, std::string const& value)
 {
-  if (key == "version") {
+  if (key == "version"_s) {
     this->GeneratorInstanceVersion = value;
     return true;
   }
@@ -761,7 +753,6 @@
 {
   switch (this->Version) {
     case cmGlobalVisualStudioGenerator::VSVersion::VS9:
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
       return "";
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
@@ -877,13 +868,13 @@
 
     // Accept known SxS props file names using four version components
     // in VS versions later than the current.
-    if (version == "14.28.16.9" && vcToolsetVersion == "14.28.29910") {
+    if (version == "14.28.16.9"_s && vcToolsetVersion == "14.28.29910"_s) {
       return AuxToolset::Default;
     }
-    if (version == "14.29.16.10" && vcToolsetVersion == "14.29.30037") {
+    if (version == "14.29.16.10"_s && vcToolsetVersion == "14.29.30037"_s) {
       return AuxToolset::Default;
     }
-    if (version == "14.29.16.11" && vcToolsetVersion == "14.29.30133") {
+    if (version == "14.29.16.11"_s && vcToolsetVersion == "14.29.30133"_s) {
       return AuxToolset::Default;
     }
 
@@ -945,8 +936,8 @@
         "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\"
         "Windows Kits\\Installed Roots;KitsRoot81",
         win81Root, cmSystemTools::KeyWOW64_32)) {
-    return cmSystemTools::FileExists(win81Root + "/include/um/windows.h",
-                                     true);
+    return cmSystemTools::FileExists(
+      cmStrCat(win81Root, "/include/um/windows.h"), true);
   }
   return false;
 }
@@ -979,29 +970,29 @@
     if (this->Version >= cmGlobalVisualStudioGenerator::VSVersion::VS17) {
       if (VSIsArm64Host()) {
         if (VSHasDotNETFrameworkArm64()) {
-          msbuild = vs + "/MSBuild/Current/Bin/arm64/MSBuild.exe";
+          msbuild = cmStrCat(vs, "/MSBuild/Current/Bin/arm64/MSBuild.exe");
           if (cmSystemTools::FileExists(msbuild)) {
             return msbuild;
           }
         }
         if (VSIsWindows11OrGreater()) {
-          msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
+          msbuild = cmStrCat(vs, "/MSBuild/Current/Bin/amd64/MSBuild.exe");
           if (cmSystemTools::FileExists(msbuild)) {
             return msbuild;
           }
         }
       } else {
-        msbuild = vs + "/MSBuild/Current/Bin/amd64/MSBuild.exe";
+        msbuild = cmStrCat(vs, "/MSBuild/Current/Bin/amd64/MSBuild.exe");
         if (cmSystemTools::FileExists(msbuild)) {
           return msbuild;
         }
       }
     }
-    msbuild = vs + "/MSBuild/Current/Bin/MSBuild.exe";
+    msbuild = cmStrCat(vs, "/MSBuild/Current/Bin/MSBuild.exe");
     if (cmSystemTools::FileExists(msbuild)) {
       return msbuild;
     }
-    msbuild = vs + "/MSBuild/15.0/Bin/MSBuild.exe";
+    msbuild = cmStrCat(vs, "/MSBuild/15.0/Bin/MSBuild.exe");
     if (cmSystemTools::FileExists(msbuild)) {
       return msbuild;
     }
@@ -1018,7 +1009,7 @@
   // Ask Visual Studio Installer tool.
   std::string vs;
   if (vsSetupAPIHelper.GetVSInstanceInfo(vs)) {
-    devenv = vs + "/Common7/IDE/devenv.com";
+    devenv = cmStrCat(vs, "/Common7/IDE/devenv.com");
     if (cmSystemTools::FileExists(devenv)) {
       return devenv;
     }
diff --git a/Source/cmGlobalWatcomWMakeGenerator.cxx b/Source/cmGlobalWatcomWMakeGenerator.cxx
index ed44e6b..ca7a1b9 100644
--- a/Source/cmGlobalWatcomWMakeGenerator.cxx
+++ b/Source/cmGlobalWatcomWMakeGenerator.cxx
@@ -4,6 +4,9 @@
 
 #include <ostream>
 
+#include <cm/string_view>
+#include <cmext/string_view>
+
 #include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -46,7 +49,7 @@
 bool cmGlobalWatcomWMakeGenerator::SetSystemName(std::string const& s,
                                                  cmMakefile* mf)
 {
-  if (mf->GetSafeDefinition("CMAKE_SYSTEM_PROCESSOR") == "I86") {
+  if (mf->GetSafeDefinition("CMAKE_SYSTEM_PROCESSOR") == "I86"_s) {
     mf->AddDefinition("CMAKE_GENERATOR_CC", "wcl");
     mf->AddDefinition("CMAKE_GENERATOR_CXX", "wcl");
   }
diff --git a/Source/cmGlobalWatcomWMakeGenerator.h b/Source/cmGlobalWatcomWMakeGenerator.h
index 5579120..8b24679 100644
--- a/Source/cmGlobalWatcomWMakeGenerator.h
+++ b/Source/cmGlobalWatcomWMakeGenerator.h
@@ -53,6 +53,8 @@
   bool AllowNotParallel() const override { return false; }
   bool AllowDeleteOnError() const override { return false; }
 
+  bool IsGNUMakeJobServerAware() const override { return false; }
+
 protected:
   std::vector<GeneratedMakeCommand> GenerateBuildCommand(
     const std::string& makeProgram, const std::string& projectName,
diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx
index fd58f75..5076e6c 100644
--- a/Source/cmGlobalXCodeGenerator.cxx
+++ b/Source/cmGlobalXCodeGenerator.cxx
@@ -53,6 +53,7 @@
 #include "cmXCodeObject.h"
 #include "cmXCodeScheme.h"
 #include "cmXMLWriter.h"
+#include "cmXcFramework.h"
 #include "cmake.h"
 
 #if !defined(CMAKE_BOOTSTRAP) && defined(__APPLE__)
@@ -81,10 +82,10 @@
   }
   void EndElement(const std::string& name) override
   {
-    if (name == "key") {
+    if (name == "key"_s) {
       this->Key = this->Data;
-    } else if (name == "string") {
-      if (this->Key == "CFBundleShortVersionString") {
+    } else if (name == "string"_s) {
+      if (this->Key == "CFBundleShortVersionString"_s) {
         this->Version = this->Data;
       }
     }
@@ -104,15 +105,13 @@
 class cmGlobalXCodeGenerator::BuildObjectListOrString
 {
   cmGlobalXCodeGenerator* Generator;
-  cmXCodeObject* Group;
-  bool Empty;
+  cmXCodeObject* Group = nullptr;
+  bool Empty = true;
   std::string String;
 
 public:
   BuildObjectListOrString(cmGlobalXCodeGenerator* gen, bool buildObjectList)
     : Generator(gen)
-    , Group(nullptr)
-    , Empty(true)
   {
     if (buildObjectList) {
       this->Group = this->Generator->CreateObject(cmXCodeObject::OBJECT_LIST);
@@ -227,7 +226,8 @@
     if (commandResult) {
       std::string::size_type pos = out.find(".app/");
       if (pos != std::string::npos) {
-        versionFile = out.substr(0, pos + 5) + "Contents/version.plist";
+        versionFile =
+          cmStrCat(out.substr(0, pos + 5), "Contents/version.plist");
       }
     }
   }
@@ -249,7 +249,7 @@
 
   if (version_number < 50) {
     cm->IssueMessage(MessageType::FATAL_ERROR,
-                     "Xcode " + version_string + " not supported.");
+                     cmStrCat("Xcode ", version_string, " not supported."));
     return std::unique_ptr<cmGlobalGenerator>();
   }
 
@@ -389,7 +389,7 @@
 bool cmGlobalXCodeGenerator::ProcessGeneratorToolsetField(
   std::string const& key, std::string const& value, cmMakefile* mf)
 {
-  if (key == "buildsystem") {
+  if (key == "buildsystem"_s) {
     if (value == "1"_s) {
       this->XcodeBuildSystem = BuildSystem::One;
     } else if (value == "12"_s) {
@@ -407,8 +407,10 @@
       mf->IssueMessage(MessageType::FATAL_ERROR, e);
       return false;
     }
-    if (this->XcodeBuildSystem == BuildSystem::Twelve &&
-        this->XcodeVersion < 120) {
+    if ((this->XcodeBuildSystem == BuildSystem::Twelve &&
+         this->XcodeVersion < 120) ||
+        (this->XcodeBuildSystem == BuildSystem::One &&
+         this->XcodeVersion >= 140)) {
       /* clang-format off */
       std::string const& e = cmStrCat(
         "Generator\n"
@@ -451,7 +453,7 @@
   bool ret = false;
 
 #ifdef HAVE_APPLICATION_SERVICES
-  std::string url = bindir + "/" + projectName + ".xcodeproj";
+  std::string url = cmStrCat(bindir, '/', projectName, ".xcodeproj");
 
   if (dryRun) {
     return cmSystemTools::FileExists(url, false);
@@ -702,7 +704,7 @@
   cmLocalGenerator* root, std::vector<cmLocalGenerator*> const& gens)
 {
   std::vector<std::string> lfiles;
-  for (auto gen : gens) {
+  for (auto* gen : gens) {
     cm::append(lfiles, gen->GetMakefile()->GetListFiles());
   }
 
@@ -721,29 +723,30 @@
   this->CurrentReRunCMakeMakefile += "/ReRunCMake.make";
   cmGeneratedFileStream makefileStream(this->CurrentReRunCMakeMakefile);
   makefileStream.SetCopyIfDifferent(true);
-  makefileStream << "# Generated by CMake, DO NOT EDIT\n\n";
+  makefileStream << "# Generated by CMake, DO NOT EDIT\n\n"
 
-  makefileStream << "TARGETS:= \n";
-  makefileStream << "empty:= \n";
-  makefileStream << "space:= $(empty) $(empty)\n";
-  makefileStream << "spaceplus:= $(empty)\\ $(empty)\n\n";
+                    "TARGETS:= \n"
+                    "empty:= \n"
+                    "space:= $(empty) $(empty)\n"
+                    "spaceplus:= $(empty)\\ $(empty)\n\n";
 
   for (const auto& lfile : lfiles) {
     makefileStream << "TARGETS += $(subst $(space),$(spaceplus),$(wildcard "
                    << this->ConvertToRelativeForMake(lfile) << "))\n";
   }
-  makefileStream << "\n";
+  makefileStream << '\n';
 
   std::string checkCache =
     cmStrCat(root->GetBinaryDirectory(), "/CMakeFiles/cmake.check_cache");
 
   if (cm->DoWriteGlobVerifyTarget()) {
-    makefileStream << ".NOTPARALLEL:\n\n";
-    makefileStream << ".PHONY: all VERIFY_GLOBS\n\n";
-    makefileStream << "all: VERIFY_GLOBS "
-                   << this->ConvertToRelativeForMake(checkCache) << "\n\n";
-    makefileStream << "VERIFY_GLOBS:\n";
-    makefileStream << "\t"
+    makefileStream << ".NOTPARALLEL:\n\n"
+                      ".PHONY: all VERIFY_GLOBS\n\n"
+                      "all: VERIFY_GLOBS "
+                   << this->ConvertToRelativeForMake(checkCache)
+                   << "\n\n"
+                      "VERIFY_GLOBS:\n"
+                      "\t"
                    << this->ConvertToRelativeForMake(
                         cmSystemTools::GetCMakeCommand())
                    << " -P "
@@ -754,11 +757,11 @@
   makefileStream << this->ConvertToRelativeForMake(checkCache)
                  << ": $(TARGETS)\n";
   makefileStream
-    << "\t" << this->ConvertToRelativeForMake(cmSystemTools::GetCMakeCommand())
+    << '\t' << this->ConvertToRelativeForMake(cmSystemTools::GetCMakeCommand())
     << " -S" << this->ConvertToRelativeForMake(root->GetSourceDirectory())
     << " -B" << this->ConvertToRelativeForMake(root->GetBinaryDirectory())
     << (cm->GetIgnoreWarningAsError() ? " --compile-no-warning-as-error" : "")
-    << "\n";
+    << '\n';
 }
 
 static bool objectIdLessThan(const std::unique_ptr<cmXCodeObject>& l,
@@ -813,7 +816,7 @@
 {
   auto obj = cm::make_unique<cmXCode21Object>(ptype, cmXCodeObject::OBJECT,
                                               this->GetObjectId(ptype, key));
-  auto ptr = obj.get();
+  auto* ptr = obj.get();
   this->addObject(std::move(obj));
   return ptr;
 }
@@ -823,7 +826,7 @@
   auto obj = cm::make_unique<cmXCodeObject>(
     cmXCodeObject::None, type,
     "Temporary cmake object, should not be referred to in Xcode file");
-  auto ptr = obj.get();
+  auto* ptr = obj.get();
   this->addObject(std::move(obj));
   return ptr;
 }
@@ -853,10 +856,7 @@
 static std::string GetGroupMapKeyFromPath(cmGeneratorTarget* target,
                                           const std::string& fullpath)
 {
-  std::string key(target->GetName());
-  key += "-";
-  key += fullpath;
-  return key;
+  return cmStrCat(target->GetName(), '-', fullpath);
 }
 
 cmXCodeObject* cmGlobalXCodeGenerator::CreateXCodeBuildFileFromPath(
@@ -920,7 +920,7 @@
           "Xcode does not support per-config per-source " << property << ":\n"
           "  " << expression << "\n"
           "specified for source:\n"
-          "  " << this->SourceFile->ResolveFullPath() << "\n";
+          "  " << this->SourceFile->ResolveFullPath() << '\n';
       /* clang-format on */
       this->LocalGenerator->IssueMessage(MessageType::FATAL_ERROR, e.str());
     }
@@ -944,10 +944,10 @@
   std::string const& srcfmt = sf->GetSafeProperty("Fortran_FORMAT");
   switch (cmOutputConverter::GetFortranFormat(srcfmt)) {
     case cmOutputConverter::FortranFormatFixed:
-      flags = "-fixed " + flags;
+      flags = cmStrCat("-fixed ", flags);
       break;
     case cmOutputConverter::FortranFormatFree:
-      flags = "-free " + flags;
+      flags = cmStrCat("-free ", flags);
       break;
     default:
       break;
@@ -1061,25 +1061,30 @@
 {
   // Empty file extension is a special case for paths to framework's
   // internal binary which could be MyFw.framework/Versions/*/MyFw
-  return (fileExt == ".framework" || fileExt == ".a" || fileExt == ".o" ||
-          fileExt == ".dylib" || fileExt == ".tbd" || fileExt.empty());
+  return (fileExt == ".framework"_s || fileExt == ".xcframework"_s ||
+          fileExt == ".a"_s || fileExt == ".o"_s || fileExt == ".dylib"_s ||
+          fileExt == ".tbd"_s || fileExt.empty());
 }
 bool IsLibraryType(const std::string& fileType)
 {
-  return (fileType == "wrapper.framework" || fileType == "archive.ar" ||
-          fileType == "compiled.mach-o.objfile" ||
-          fileType == "compiled.mach-o.dylib" ||
-          fileType == "compiled.mach-o.executable" ||
-          fileType == "sourcecode.text-based-dylib-definition");
+  return (fileType == "wrapper.framework"_s ||
+          fileType == "wrapper.xcframework"_s || fileType == "archive.ar"_s ||
+          fileType == "compiled.mach-o.objfile"_s ||
+          fileType == "compiled.mach-o.dylib"_s ||
+          fileType == "compiled.mach-o.executable"_s ||
+          fileType == "sourcecode.text-based-dylib-definition"_s);
 }
 
 std::string GetDirectoryValueFromFileExtension(const std::string& dirExt)
 {
   std::string ext = cmSystemTools::LowerCase(dirExt);
-  if (ext == "framework") {
+  if (ext == "framework"_s) {
     return "wrapper.framework";
   }
-  if (ext == "xcassets") {
+  if (ext == "xcframework"_s) {
+    return "wrapper.xcframework";
+  }
+  if (ext == "xcassets"_s) {
     return "folder.assetcatalog";
   }
   return "folder";
@@ -1092,66 +1097,68 @@
   std::string ext = cmSystemTools::LowerCase(_ext);
   std::string sourcecode = "sourcecode";
 
-  if (ext == "o") {
+  if (ext == "o"_s) {
     keepLastKnownFileType = true;
     sourcecode = "compiled.mach-o.objfile";
-  } else if (ext == "xctest") {
+  } else if (ext == "xctest"_s) {
     sourcecode = "wrapper.cfbundle";
-  } else if (ext == "xib") {
+  } else if (ext == "xib"_s) {
     keepLastKnownFileType = true;
     sourcecode = "file.xib";
-  } else if (ext == "storyboard") {
+  } else if (ext == "storyboard"_s) {
     keepLastKnownFileType = true;
     sourcecode = "file.storyboard";
-  } else if (ext == "mm" && !cm::contains(enabled_langs, "OBJCXX")) {
+    // NOLINTNEXTLINE(bugprone-branch-clone)
+  } else if (ext == "mm"_s && !cm::contains(enabled_langs, "OBJCXX")) {
     sourcecode += ".cpp.objcpp";
-  } else if (ext == "m" && !cm::contains(enabled_langs, "OBJC")) {
+    // NOLINTNEXTLINE(bugprone-branch-clone)
+  } else if (ext == "m"_s && !cm::contains(enabled_langs, "OBJC")) {
     sourcecode += ".c.objc";
-  } else if (ext == "swift") {
+  } else if (ext == "swift"_s) {
     sourcecode += ".swift";
-  } else if (ext == "plist") {
+  } else if (ext == "plist"_s) {
     sourcecode += ".text.plist";
-  } else if (ext == "h") {
+  } else if (ext == "h"_s) {
     sourcecode += ".c.h";
-  } else if (ext == "hxx" || ext == "hpp" || ext == "txx" || ext == "pch" ||
-             ext == "hh" || ext == "inl") {
+  } else if (ext == "hxx"_s || ext == "hpp"_s || ext == "txx"_s ||
+             ext == "pch"_s || ext == "hh"_s || ext == "inl"_s) {
     sourcecode += ".cpp.h";
-  } else if (ext == "png" || ext == "gif" || ext == "jpg") {
+  } else if (ext == "png"_s || ext == "gif"_s || ext == "jpg"_s) {
     keepLastKnownFileType = true;
     sourcecode = "image";
-  } else if (ext == "txt") {
+  } else if (ext == "txt"_s) {
     sourcecode += ".text";
-  } else if (lang == "CXX") {
+  } else if (lang == "CXX"_s) {
     sourcecode += ".cpp.cpp";
-  } else if (lang == "C") {
+  } else if (lang == "C"_s) {
     sourcecode += ".c.c";
-  } else if (lang == "OBJCXX") {
+  } else if (lang == "OBJCXX"_s) {
     sourcecode += ".cpp.objcpp";
-  } else if (lang == "OBJC") {
+  } else if (lang == "OBJC"_s) {
     sourcecode += ".c.objc";
-  } else if (lang == "Fortran") {
+  } else if (lang == "Fortran"_s) {
     sourcecode += ".fortran.f90";
-  } else if (lang == "ASM") {
+  } else if (lang == "ASM"_s) {
     sourcecode += ".asm";
-  } else if (ext == "metal") {
+  } else if (ext == "metal"_s) {
     sourcecode += ".metal";
-  } else if (ext == "mig") {
+  } else if (ext == "mig"_s) {
     sourcecode += ".mig";
-  } else if (ext == "tbd") {
+  } else if (ext == "tbd"_s) {
     sourcecode += ".text-based-dylib-definition";
-  } else if (ext == "a") {
+  } else if (ext == "a"_s) {
     keepLastKnownFileType = true;
     sourcecode = "archive.ar";
-  } else if (ext == "dylib") {
+  } else if (ext == "dylib"_s) {
     keepLastKnownFileType = true;
     sourcecode = "compiled.mach-o.dylib";
-  } else if (ext == "framework") {
+  } else if (ext == "framework"_s) {
     keepLastKnownFileType = true;
     sourcecode = "wrapper.framework";
-  } else if (ext == "xcassets") {
+  } else if (ext == "xcassets"_s) {
     keepLastKnownFileType = true;
     sourcecode = "folder.assetcatalog";
-  } else if (ext == "xcconfig") {
+  } else if (ext == "xcconfig"_s) {
     keepLastKnownFileType = true;
     sourcecode = "text.xcconfig";
   }
@@ -1173,11 +1180,11 @@
   cmList archs{ target.GetSafeProperty("OSX_ARCHITECTURES") };
   if (archs.size() > 1) {
     return "$(CURRENT_ARCH)";
-  } else if (archs.size() == 1) {
-    return archs.front();
-  } else {
-    return defaultVal;
   }
+  if (archs.size() == 1) {
+    return archs.front();
+  }
+  return defaultVal;
 }
 
 } // anonymous
@@ -1267,12 +1274,18 @@
   fileRef->AddAttribute("sourceTree", this->CreateString(sourceTree));
 
   cmXCodeObject* group = this->GroupMap[key];
-  if (!group && IsLibraryType(fileType)) {
-    group = this->FrameworkGroup;
-    this->GroupMap[key] = group;
-  }
   if (!group) {
-    cmSystemTools::Error("Could not find a PBX group for " + key);
+    if (IsLibraryType(fileType)) {
+      group = this->FrameworkGroup;
+    } else if (fileType == "folder") {
+      group = this->ResourcesGroup;
+    }
+    if (group)
+      this->GroupMap[key] = group;
+  }
+
+  if (!group) {
+    cmSystemTools::Error(cmStrCat("Could not find a PBX group for ", key));
     return nullptr;
   }
   cmXCodeObject* children = group->GetAttribute("children");
@@ -1293,8 +1306,8 @@
 
 bool cmGlobalXCodeGenerator::SpecialTargetEmitted(std::string const& tname)
 {
-  if (tname == "ALL_BUILD" || tname == "install" || tname == "package" ||
-      tname == "RUN_TESTS" || tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
+  if (tname == "ALL_BUILD"_s || tname == "install"_s || tname == "package"_s ||
+      tname == "RUN_TESTS"_s || tname == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
     if (this->TargetDoneSet.find(tname) != this->TargetDoneSet.end()) {
       return true;
     }
@@ -1328,10 +1341,10 @@
   {
     std::string const& a = l->GetTarget()->GetName();
     std::string const& b = r->GetTarget()->GetName();
-    if (a == "ALL_BUILD") {
+    if (a == "ALL_BUILD"_s) {
       return true;
     }
-    if (b == "ALL_BUILD") {
+    if (b == "ALL_BUILD"_s) {
       return false;
     }
     return a < b;
@@ -1344,7 +1357,7 @@
   this->SetCurrentLocalGenerator(gen);
   std::vector<cmGeneratorTarget*> gts =
     this->GetLocalGeneratorTargetsInOrder(gen);
-  for (auto gtgt : gts) {
+  for (auto* gtgt : gts) {
     if (!this->CreateXCodeTarget(gtgt, targets)) {
       return false;
     }
@@ -1371,17 +1384,9 @@
     gtgt->CheckCxxModuleStatus(configName);
   }
 
-  if (gtgt->HaveCxx20ModuleSources()) {
-    gtgt->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      cmStrCat("The \"", gtgt->GetName(),
-               "\" target contains C++ module sources which are not "
-               "supported by the generator"));
-  }
-
   auto& gtgt_visited = this->CommandsVisited[gtgt];
-  auto& deps = this->GetTargetDirectDepends(gtgt);
-  for (auto& d : deps) {
+  auto const& deps = this->GetTargetDirectDepends(gtgt);
+  for (auto const& d : deps) {
     // Take the union of visited source files of custom commands so far.
     // ComputeTargetOrder ensures our dependencies already visited their
     // custom commands and updated CommandsVisited.
@@ -1426,7 +1431,7 @@
   std::vector<cmXCodeObject*> headerFiles;
   std::vector<cmXCodeObject*> resourceFiles;
   std::vector<cmXCodeObject*> sourceFiles;
-  for (auto sourceFile : commonSourceFiles) {
+  for (auto* sourceFile : commonSourceFiles) {
     cmXCodeObject* xsf = this->CreateXCodeSourceFile(
       this->CurrentLocalGenerator, sourceFile, gtgt);
     cmXCodeObject* fr = xsf->GetAttribute("fileRef");
@@ -1439,7 +1444,7 @@
     cmGeneratorTarget::SourceFileFlags tsFlags =
       gtgt->GetTargetSourceFileFlags(sourceFile);
 
-    if (filetype && filetype->GetString() == "compiled.mach-o.objfile") {
+    if (filetype && filetype->GetString() == "compiled.mach-o.objfile"_s) {
       if (sourceFile->GetObjectLibrary().empty()) {
         externalObjFiles.push_back(xsf);
       }
@@ -1529,7 +1534,7 @@
     using mapOfVectorOfSourceFiles =
       std::map<std::string, std::vector<cmSourceFile*>>;
     mapOfVectorOfSourceFiles bundleFiles;
-    for (auto sourceFile : commonSourceFiles) {
+    for (auto* sourceFile : commonSourceFiles) {
       cmGeneratorTarget::SourceFileFlags tsFlags =
         gtgt->GetTargetSourceFileFlags(sourceFile);
       if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeMacContent) {
@@ -1548,7 +1553,7 @@
       if (gtgt->IsFrameworkOnApple()) {
         // dstPath in frameworks is relative to Versions/<version>
         ostr << keySources.first;
-      } else if (keySources.first != "MacOS") {
+      } else if (keySources.first != "MacOS"_s) {
         if (gtgt->Target->GetMakefile()->PlatformIsAppleEmbedded()) {
           ostr << keySources.first;
         } else {
@@ -1562,7 +1567,7 @@
                                         this->CreateString("0"));
       buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
       copyFilesBuildPhase->AddAttribute("files", buildFiles);
-      for (auto sourceFile : keySources.second) {
+      for (auto* sourceFile : keySources.second) {
         cmXCodeObject* xsf = this->CreateXCodeSourceFile(
           this->CurrentLocalGenerator, sourceFile, gtgt);
         buildFiles->AddObject(xsf);
@@ -1577,7 +1582,7 @@
     using mapOfVectorOfSourceFiles =
       std::map<std::string, std::vector<cmSourceFile*>>;
     mapOfVectorOfSourceFiles bundleFiles;
-    for (auto sourceFile : commonSourceFiles) {
+    for (auto* sourceFile : commonSourceFiles) {
       cmGeneratorTarget::SourceFileFlags tsFlags =
         gtgt->GetTargetSourceFileFlags(sourceFile);
       if (tsFlags.Type == cmGeneratorTarget::SourceFileTypeDeepResource) {
@@ -1598,7 +1603,7 @@
                                         this->CreateString("0"));
       buildFiles = this->CreateObject(cmXCodeObject::OBJECT_LIST);
       copyFilesBuildPhase->AddAttribute("files", buildFiles);
-      for (auto sourceFile : keySources.second) {
+      for (auto* sourceFile : keySources.second) {
         cmXCodeObject* xsf = this->CreateXCodeSourceFile(
           this->CurrentLocalGenerator, sourceFile, gtgt);
         buildFiles->AddObject(xsf);
@@ -1674,8 +1679,9 @@
   if (const char* productType = GetTargetProductType(gtgt)) {
     if (strcmp(productType,
                "com.apple.product-type.app-extension.messages-sticker-pack") ==
-        0)
+        0) {
       return;
+    }
   }
 
   // Add an empty source file to the target that compiles with the
@@ -1687,7 +1693,7 @@
     gtgt->GetName(), "-CMakeForceLinker.", cmSystemTools::LowerCase(llang));
   {
     cmGeneratedFileStream fout(fname);
-    fout << "\n";
+    fout << '\n';
   }
   if (cmSourceFile* sf = mf->GetOrCreateSource(fname)) {
     sf->SetProperty("LANGUAGE", llang);
@@ -1797,7 +1803,7 @@
     // add all the sources
     std::vector<cmCustomCommand> commands;
     auto& visited = this->CommandsVisited[gtgt];
-    for (auto sourceFile : classes) {
+    for (auto* sourceFile : classes) {
       if (sourceFile->GetCustomCommand() &&
           visited.insert(sourceFile).second) {
         commands.push_back(*sourceFile->GetCustomCommand());
@@ -1836,7 +1842,7 @@
   if (resourceBuildPhase) {
     buildPhases->AddObject(resourceBuildPhase);
   }
-  for (auto obj : contentBuildPhases) {
+  for (auto* obj : contentBuildPhases) {
     buildPhases->AddObject(obj);
   }
   if (sourceBuildPhase) {
@@ -1865,7 +1871,7 @@
     return;
   }
   auto& visited = this->CommandsVisited[gt];
-  for (auto sf : sources) {
+  for (auto* sf : sources) {
     this->CreateRunScriptBuildPhases(buildPhases, sf, gt, visited);
   }
 }
@@ -1900,7 +1906,7 @@
 
   auto depfilesDirectory = cmStrCat(
     gt->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/CMakeFiles/d/");
-  auto depfilesPrefix = cmStrCat(depfilesDirectory, buildPhase->GetId(), ".");
+  auto depfilesPrefix = cmStrCat(depfilesDirectory, buildPhase->GetId(), '.');
 
   std::string shellScript = "set -e\n";
   for (std::string const& configName : this->CurrentConfigurationTypes) {
@@ -2070,7 +2076,7 @@
   }
   wd = lg->ConvertToOutputFormat(wd, cmOutputConverter::SHELL);
   ReplaceScriptVars(wd);
-  script = cmStrCat(script, "  cd ", wd, "\n");
+  script = cmStrCat(script, "  cd ", wd, '\n');
   for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) {
     std::string cmd = ccg.GetCommand(c);
     if (cmd.empty()) {
@@ -2195,10 +2201,10 @@
 
   std::string cdir = this->CurrentLocalGenerator->GetCurrentBinaryDirectory();
   cdir = this->ConvertToRelativeForMake(cdir);
-  std::string makecmd =
-    cmStrCat("make -C ", cdir, " -f ",
-             this->ConvertToRelativeForMake((makefile + "$CONFIGURATION")),
-             " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
+  std::string makecmd = cmStrCat(
+    "make -C ", cdir, " -f ",
+    this->ConvertToRelativeForMake(cmStrCat(makefile, "$CONFIGURATION")),
+    " OBJDIR=$(basename \"$OBJECT_FILE_DIR_normal\") all");
   buildphase->AddAttribute("shellScript", this->CreateString(makecmd));
   buildphase->AddAttribute("showEnvVarsInLog", this->CreateString("0"));
 }
@@ -2213,12 +2219,13 @@
     return;
   }
   makefileStream.SetCopyIfDifferent(true);
-  makefileStream << "# Generated by CMake, DO NOT EDIT\n";
-  makefileStream << "# Custom rules for " << target->GetName() << "\n";
+  makefileStream << "# Generated by CMake, DO NOT EDIT\n"
+                    "# Custom rules for "
+                 << target->GetName() << '\n';
 
   // disable the implicit rules
   makefileStream << ".SUFFIXES: "
-                 << "\n";
+                    "\n";
 
   // have all depend on all outputs
   makefileStream << "all: ";
@@ -2236,7 +2243,7 @@
       } else {
         std::ostringstream str;
         str << "_buildpart_" << count++;
-        tname[&ccg.GetCC()] = target->GetName() + str.str();
+        tname[&ccg.GetCC()] = cmStrCat(target->GetName(), str.str());
         makefileStream << "\\\n\t" << tname[&ccg.GetCC()];
       }
     }
@@ -2255,7 +2262,7 @@
         return cmStrCat(
           depfilesDirectory,
           this->GetObjectId(cmXCodeObject::PBXShellScriptBuildPhase, file),
-          ".", config, ".d");
+          '.', config, ".d");
       });
 
     auto depfile = ccg.GetInternalDepfile();
@@ -2279,7 +2286,7 @@
     }
 
     if (ccg.GetNumberOfCommands() > 0) {
-      makefileStream << "\n";
+      makefileStream << '\n';
       const std::vector<std::string>& outputs = ccg.GetOutputs();
       if (!outputs.empty()) {
         // There is at least one output, start the rule for it
@@ -2296,14 +2303,14 @@
       for (auto const& dep : realDepends) {
         makefileStream << "\\\n" << this->ConvertToRelativeForMake(dep);
       }
-      makefileStream << "\n";
+      makefileStream << '\n';
 
       if (cm::optional<std::string> comment = ccg.GetComment()) {
         std::string echo_cmd =
           cmStrCat("echo ",
                    (this->CurrentLocalGenerator->EscapeForShell(
                      *comment, ccg.GetCC().GetEscapeAllowMakeVars())));
-        makefileStream << "\t" << echo_cmd << "\n";
+        makefileStream << '\t' << echo_cmd << '\n';
       }
 
       // Add each command line to the set of commands.
@@ -2321,7 +2328,7 @@
         }
         cmd += cmd2;
         ccg.AppendArguments(c, cmd);
-        makefileStream << "\t" << cmd << "\n";
+        makefileStream << '\t' << cmd << '\n';
       }
 
       // Symbolic inputs are not expected to exist, so add dummy rules.
@@ -2400,8 +2407,8 @@
   std::string llang = gtgt->GetLinkerLanguage(configName);
   if (binary && llang.empty()) {
     cmSystemTools::Error(
-      "CMake can not determine linker language for target: " +
-      gtgt->GetName());
+      cmStrCat("CMake can not determine linker language for target: ",
+               gtgt->GetName()));
     return;
   }
 
@@ -2465,7 +2472,7 @@
       std::set<std::string> defines(targetSwiftDefines.begin(),
                                     targetSwiftDefines.end());
       this->CurrentLocalGenerator->JoinDefines(defines, defineString, "Swift");
-      cflags["Swift"] += " " + defineString;
+      cflags["Swift"] += cmStrCat(' ', defineString);
     } else {
       BuildObjectListOrString swiftDefs(this, true);
       this->AppendDefines(swiftDefs, targetSwiftDefines);
@@ -2552,9 +2559,9 @@
   std::string realName = components.base;
   std::string soName = components.base;
   if (version && soversion) {
-    realName += ".";
+    realName += '.';
     realName += *version;
-    soName += ".";
+    soName += '.';
     soName += *soversion;
   }
 
@@ -2639,7 +2646,7 @@
         std::string createFlags = this->LookupFlags(
           "CMAKE_SHARED_MODULE_CREATE_", llang, "_FLAGS", "-bundle");
         if (!createFlags.empty()) {
-          extraLinkOptions += " ";
+          extraLinkOptions += ' ';
           extraLinkOptions += createFlags;
         }
         cmValue ext = gtgt->GetProperty("BUNDLE_EXTENSION");
@@ -2665,7 +2672,7 @@
         std::string createFlags =
           this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
         if (!createFlags.empty()) {
-          extraLinkOptions += " ";
+          extraLinkOptions += ' ';
           extraLinkOptions += createFlags;
         }
       }
@@ -2695,7 +2702,7 @@
         std::string createFlags = this->LookupFlags(
           "CMAKE_SHARED_LIBRARY_CREATE_", llang, "_FLAGS", "-dynamiclib");
         if (!createFlags.empty()) {
-          extraLinkOptions += " ";
+          extraLinkOptions += ' ';
           extraLinkOptions += createFlags;
         }
       }
@@ -2715,7 +2722,7 @@
       std::string createFlags =
         this->LookupFlags("CMAKE_", llang, "_LINK_FLAGS", "");
       if (!createFlags.empty()) {
-        extraLinkOptions += " ";
+        extraLinkOptions += ' ';
         extraLinkOptions += createFlags;
       }
 
@@ -2824,7 +2831,7 @@
         includes, gtgt, language, configName);
 
       if (!includeFlags.empty()) {
-        cflags[language] += " " + includeFlags;
+        cflags[language] += cmStrCat(' ', includeFlags);
       }
     }
   }
@@ -2848,8 +2855,8 @@
     gflag = this->ExtractFlag("-g", flags);
     // put back gdwarf-2 if used since there is no way
     // to represent it in the gui, but we still want debug yes
-    if (gflag == "-gdwarf-2") {
-      flags += " ";
+    if (gflag == "-gdwarf-2"_s) {
+      flags += ' ';
       flags += gflag;
     }
     if (last_gflag && *last_gflag != gflag) {
@@ -2863,17 +2870,17 @@
     // We can't set the Xcode flag differently depending on the language,
     // so put them back in this case.
     for (auto const& language : languages) {
-      cflags[language] += " ";
+      cflags[language] += ' ';
       cflags[language] += gflags[language];
     }
     debugStr = "NO";
-  } else if (last_gflag && (last_gflag->empty() || *last_gflag == "-g0")) {
+  } else if (last_gflag && (last_gflag->empty() || *last_gflag == "-g0"_s)) {
     debugStr = "NO";
   }
 
   // extract C++ stdlib
   for (auto const& language : languages) {
-    if (language != "CXX" && language != "OBJCXX") {
+    if (language != "CXX"_s && language != "OBJCXX"_s) {
       continue;
     }
     std::string& flags = cflags[language];
@@ -2882,7 +2889,7 @@
       this->ExtractFlagRegex("(^| )(-stdlib=[^ ]+)( |$)", 2, flags);
     if (stdlib.size() > 8) {
       const auto cxxLibrary = stdlib.substr(8);
-      if (language == "CXX" ||
+      if (language == "CXX"_s ||
           !buildSettings->GetAttribute("CLANG_CXX_LIBRARY")) {
         buildSettings->AddAttribute("CLANG_CXX_LIBRARY",
                                     this->CreateString(cxxLibrary));
@@ -2902,21 +2909,21 @@
                               this->CreateString("NO"));
 
   for (auto const& language : languages) {
-    std::string flags = cflags[language] + " " + defFlags;
-    if (language == "CXX" || language == "OBJCXX") {
-      if (language == "CXX" ||
+    std::string flags = cmStrCat(cflags[language], ' ', defFlags);
+    if (language == "CXX"_s || language == "OBJCXX"_s) {
+      if (language == "CXX"_s ||
           !buildSettings->GetAttribute("OTHER_CPLUSPLUSFLAGS")) {
         buildSettings->AddAttribute("OTHER_CPLUSPLUSFLAGS",
                                     this->CreateString(flags));
       }
-    } else if (language == "Fortran") {
+    } else if (language == "Fortran"_s) {
       buildSettings->AddAttribute("IFORT_OTHER_FLAGS",
                                   this->CreateString(flags));
-    } else if (language == "C" || language == "OBJC") {
-      if (language == "C" || !buildSettings->GetAttribute("OTHER_CFLAGS")) {
+    } else if (language == "C"_s || language == "OBJC"_s) {
+      if (language == "C"_s || !buildSettings->GetAttribute("OTHER_CFLAGS")) {
         buildSettings->AddAttribute("OTHER_CFLAGS", this->CreateString(flags));
       }
-    } else if (language == "Swift") {
+    } else if (language == "Swift"_s) {
       buildSettings->AddAttribute("OTHER_SWIFT_FLAGS",
                                   this->CreateString(flags));
     }
@@ -2955,7 +2962,7 @@
       // Convert to a path for the native build tool.
       cmSystemTools::ConvertToUnixSlashes(install_name_dir);
       install_name += install_name_dir;
-      install_name += "/";
+      install_name += '/';
     }
     install_name += gtgt->GetSOName(configName);
 
@@ -2983,7 +2990,7 @@
       if (unique_dirs.find(runpath) == unique_dirs.end()) {
         unique_dirs.insert(runpath);
         if (!search_paths.empty()) {
-          search_paths += " ";
+          search_paths += ' ';
         }
         search_paths += this->XCodeEscapePath(runpath);
       }
@@ -3018,7 +3025,7 @@
 
     // Xcode always wants at least 1.0.0 or nothing
     if (!(major == 0 && minor == 0 && patch == 0)) {
-      v << major << "." << minor << "." << patch;
+      v << major << '.' << minor << '.' << patch;
     }
     buildSettings->AddAttribute("DYLIB_CURRENT_VERSION",
                                 this->CreateString(v.str()));
@@ -3030,7 +3037,7 @@
 
     // Xcode always wants at least 1.0.0 or nothing
     if (!(major == 0 && minor == 0 && patch == 0)) {
-      vso << major << "." << minor << "." << patch;
+      vso << major << '.' << minor << '.' << patch;
     }
     buildSettings->AddAttribute("DYLIB_COMPATIBILITY_VERSION",
                                 this->CreateString(vso.str()));
@@ -3111,7 +3118,7 @@
     // Add CMakeLists.txt file for user convenience.
     this->AddXCodeProjBuildRule(gtgt, sources);
 
-    for (auto sourceFile : sources) {
+    for (auto* sourceFile : sources) {
       if (!sourceFile->GetIsGenerated()) {
         this->CreateXCodeFileReference(sourceFile, gtgt);
       }
@@ -3172,11 +3179,11 @@
     return;
   }
 
-  auto sf = this->CurrentMakefile->GetSource(xcconfig);
+  auto* sf = this->CurrentMakefile->GetSource(xcconfig);
   if (!sf) {
     cmSystemTools::Error(
       cmStrCat("sources for ALL_BUILD do not contain xcconfig file: '",
-               xcconfig, "' (configuration: ", configName, ")"));
+               xcconfig, "' (configuration: ", configName, ')'));
     return;
   }
 
@@ -3204,12 +3211,12 @@
     return;
   }
 
-  auto sf = target->Makefile->GetSource(xcconfig);
+  auto* sf = target->Makefile->GetSource(xcconfig);
   if (!sf) {
     cmSystemTools::Error(cmStrCat("target sources for target ",
                                   target->Target->GetName(),
                                   " do not contain xcconfig file: '", xcconfig,
-                                  "' (configuration: ", configName, ")"));
+                                  "' (configuration: ", configName, ')'));
     return;
   }
 
@@ -3452,8 +3459,8 @@
 {
   if (value->GetType() != cmXCodeObject::OBJECT_LIST &&
       value->GetType() != cmXCodeObject::STRING) {
-    cmSystemTools::Error("Unsupported value type for appending: " +
-                         std::string(attribute));
+    cmSystemTools::Error(
+      cmStrCat("Unsupported value type for appending: ", attribute));
     return;
   }
   if (attr->GetType() == cmXCodeObject::OBJECT_LIST) {
@@ -3476,8 +3483,8 @@
       attr->SetString(newValue);
     }
   } else {
-    cmSystemTools::Error("Unsupported attribute type for appending: " +
-                         std::string(attribute));
+    cmSystemTools::Error(
+      cmStrCat("Unsupported attribute type for appending: ", attribute));
   }
 }
 
@@ -3491,7 +3498,7 @@
     target->GetAttribute("buildConfigurationList")->GetObject();
   cmXCodeObject* buildConfigs =
     configurationList->GetAttribute("buildConfigurations");
-  for (auto obj : buildConfigs->GetObjectList()) {
+  for (auto* obj : buildConfigs->GetObjectList()) {
     if (configName.empty() ||
         obj->GetAttribute("name")->GetString() == configName) {
       cmXCodeObject* settings = obj->GetAttribute("buildSettings");
@@ -3507,7 +3514,7 @@
     target->GetAttribute("buildConfigurationList")->GetObject();
   cmXCodeObject* buildConfigs =
     configurationList->GetAttribute("buildConfigurations");
-  for (auto obj : buildConfigs->GetObjectList()) {
+  for (auto* obj : buildConfigs->GetObjectList()) {
     cmXCodeObject* settings = obj->GetAttribute("buildSettings");
     if (cmXCodeObject* attr = settings->GetAttribute(attribute)) {
       BuildObjectListOrString inherited(this, true);
@@ -3568,14 +3575,14 @@
   cmValue prop =
     target->GetTarget()->GetProperty("XCODE_LINK_BUILD_PHASE_MODE");
   if (prop) {
-    if (*prop == "BUILT_ONLY") {
+    if (*prop == "BUILT_ONLY"_s) {
       useLinkPhase = true;
-    } else if (*prop == "KNOWN_LOCATION") {
+    } else if (*prop == "KNOWN_LOCATION"_s) {
       useLinkPhase = true;
       forceLinkPhase = true;
-    } else if (*prop != "NONE") {
-      cmSystemTools::Error("Invalid value for XCODE_LINK_BUILD_PHASE_MODE: " +
-                           *prop);
+    } else if (*prop != "NONE"_s) {
+      cmSystemTools::Error(
+        cmStrCat("Invalid value for XCODE_LINK_BUILD_PHASE_MODE: ", *prop));
       return;
     }
   }
@@ -3585,6 +3592,13 @@
       continue;
     }
     for (auto const& libItem : cli->GetItems()) {
+      // Explicitly ignore OBJECT libraries as Xcode emulates them as static
+      // libraries without an artifact. Avoid exposing this to the rest of
+      // CMake's compilation model.
+      if (libItem.Target &&
+          libItem.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
+        continue;
+      }
       // We want to put only static libraries, dynamic libraries, frameworks
       // and bundles that are built from targets that are not imported in "Link
       // Binary With Libraries" build phase. Except if the target property
@@ -3607,6 +3621,8 @@
         bool canUseLinkPhase = !libItem.HasFeature() ||
           libItem.GetFeatureName() == "__CMAKE_LINK_FRAMEWORK"_s ||
           libItem.GetFeatureName() == "FRAMEWORK"_s ||
+          libItem.GetFeatureName() == "__CMAKE_LINK_XCFRAMEWORK"_s ||
+          libItem.GetFeatureName() == "XCFRAMEWORK"_s ||
           libItem.GetFeatureName() == "WEAK_FRAMEWORK"_s ||
           libItem.GetFeatureName() == "WEAK_LIBRARY"_s;
         if (canUseLinkPhase) {
@@ -3827,7 +3843,7 @@
       BuildObjectListOrString libSearchPaths(this, true);
       std::vector<cmSourceFile const*> objs;
       gt->GetExternalObjects(objs, configName);
-      for (auto sourceFile : objs) {
+      for (auto const* sourceFile : objs) {
         if (sourceFile->GetObjectLibrary().empty()) {
           continue;
         }
@@ -3861,12 +3877,12 @@
 
       std::string linkDirs;
       for (auto const& libDir : cli->GetDirectories()) {
-        if (!libDir.empty() && libDir != "/usr/lib") {
+        if (!libDir.empty() && libDir != "/usr/lib"_s) {
           cmPolicies::PolicyStatus cmp0142 =
             target->GetTarget()->GetPolicyStatusCMP0142();
           if (cmp0142 == cmPolicies::OLD || cmp0142 == cmPolicies::WARN) {
-            libSearchPaths.Add(this->XCodeEscapePath(
-              libDir + "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"));
+            libSearchPaths.Add(this->XCodeEscapePath(cmStrCat(
+              libDir, "/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)")));
           }
           libSearchPaths.Add(this->XCodeEscapePath(libDir));
         }
@@ -3917,12 +3933,14 @@
           if (cmSystemTools::FileIsFullPath(cleanPath)) {
             cleanPath = cmSystemTools::CollapseFullPath(cleanPath);
           }
-          bool isFramework =
+          bool isXcFramework =
+            cmHasSuffix(libName.GetFeatureName(), "XCFRAMEWORK"_s);
+          bool isFramework = !isXcFramework &&
             cmHasSuffix(libName.GetFeatureName(), "FRAMEWORK"_s);
           if (isFramework) {
             const auto fwDescriptor = this->SplitFrameworkPath(
               cleanPath, cmGlobalGenerator::FrameworkFormat::Extended);
-            if (!fwDescriptor->Directory.empty() &&
+            if (isFramework && !fwDescriptor->Directory.empty() &&
                 emitted.insert(fwDescriptor->Directory).second) {
               // This is a search path we had not added before and it isn't
               // an implicit search path, so we need it
@@ -3940,13 +3958,54 @@
                                fwDescriptor->GetLinkName()))
                              .Value);
             }
+          } else if (isXcFramework) {
+            auto plist = cmParseXcFrameworkPlist(
+              cleanPath, *this->Makefiles.front(), libName.Value.Backtrace);
+            if (!plist) {
+              return;
+            }
+            if (auto const* library = plist->SelectSuitableLibrary(
+                  *this->Makefiles.front(), libName.Value.Backtrace)) {
+              auto libraryPath =
+                cmStrCat(cleanPath, '/', library->LibraryIdentifier, '/',
+                         library->LibraryPath);
+              if (auto const fwDescriptor = this->SplitFrameworkPath(
+                    libraryPath,
+                    cmGlobalGenerator::FrameworkFormat::Relaxed)) {
+                if (!fwDescriptor->Directory.empty() &&
+                    emitted.insert(fwDescriptor->Directory).second) {
+                  // This is a search path we had not added before and it
+                  // isn't an implicit search path, so we need it
+                  fwSearchPaths.Add(
+                    this->XCodeEscapePath(fwDescriptor->Directory));
+                }
+                libPaths.Add(cmStrCat(
+                  "-framework ",
+                  this->XCodeEscapePath(fwDescriptor->GetLinkName())));
+              } else {
+                libPaths.Add(
+                  libName.GetFormattedItem(this->XCodeEscapePath(libraryPath))
+                    .Value);
+                if (!library->HeadersPath.empty()) {
+                  this->AppendBuildSettingAttribute(
+                    target, "HEADER_SEARCH_PATHS",
+                    this->CreateString(this->XCodeEscapePath(
+                      cmStrCat(cleanPath, '/', library->LibraryIdentifier, '/',
+                               library->HeadersPath))),
+                    configName);
+                }
+              }
+            } else {
+              return;
+            }
           } else {
             libPaths.Add(
               libName.GetFormattedItem(this->XCodeEscapePath(cleanPath))
                 .Value);
           }
           if ((!libName.Target || libName.Target->IsImported()) &&
-              (isFramework || IsLinkPhaseLibraryExtension(cleanPath))) {
+              (isFramework || isXcFramework ||
+               IsLinkPhaseLibraryExtension(cleanPath))) {
             // Create file reference for embedding
             auto it = this->ExternalLibRefs.find(cleanPath);
             if (it == this->ExternalLibRefs.end()) {
@@ -4035,16 +4094,16 @@
       // This is a target - get it's product path reference
       auto* xcTarget = this->FindXCodeTarget(genTarget);
       if (!xcTarget) {
-        cmSystemTools::Error("Can not find a target for " +
-                             genTarget->GetName());
+        cmSystemTools::Error(
+          cmStrCat("Can not find a target for ", genTarget->GetName()));
         continue;
       }
       // Add the target output file as a build reference for other targets
       // to link against
       auto* fileRefObject = xcTarget->GetAttribute("productReference");
       if (!fileRefObject) {
-        cmSystemTools::Error("Target " + genTarget->GetName() +
-                             " is missing product reference");
+        cmSystemTools::Error(cmStrCat("Target ", genTarget->GetName(),
+                                      " is missing product reference"));
         continue;
       }
       auto it = this->FileRefToEmbedBuildFileMap.find(fileRefObject);
@@ -4056,7 +4115,8 @@
         buildFile = it->second;
       }
     } else if (cmSystemTools::IsPathToFramework(relFile) ||
-               cmSystemTools::IsPathToMacOSSharedLibrary(relFile)) {
+               cmSystemTools::IsPathToMacOSSharedLibrary(relFile) ||
+               cmSystemTools::FileIsDirectory(filePath)) {
       // This is a regular string path - create file reference
       auto it = this->EmbeddedLibRefs.find(relFile);
       if (it == this->EmbeddedLibRefs.end()) {
@@ -4069,7 +4129,8 @@
                                   this->CreateObjectReference(fileRef));
         }
         if (!buildFile) {
-          cmSystemTools::Error("Can't create build file for " + relFile);
+          cmSystemTools::Error(
+            cmStrCat("Can't create build file for ", relFile));
           continue;
         }
         this->EmbeddedLibRefs.emplace(filePath, buildFile);
@@ -4078,7 +4139,7 @@
       }
     }
     if (!buildFile) {
-      cmSystemTools::Error("Can't find a build file for " + relFile);
+      cmSystemTools::Error(cmStrCat("Can't find a build file for ", relFile));
       continue;
     }
     // Set build file configuration
@@ -4120,7 +4181,7 @@
 
 void cmGlobalXCodeGenerator::AddEmbeddedFrameworks(cmXCodeObject* target)
 {
-  static const auto dstSubfolderSpec = "10";
+  static auto const* const dstSubfolderSpec = "10";
 
   // Despite the name, by default Xcode uses "Embed Frameworks" build phase
   // for both frameworks and dynamic libraries
@@ -4131,7 +4192,7 @@
 
 void cmGlobalXCodeGenerator::AddEmbeddedPlugIns(cmXCodeObject* target)
 {
-  static const auto dstSubfolderSpec = "13";
+  static auto const* const dstSubfolderSpec = "13";
 
   this->AddEmbeddedObjects(target, "Embed PlugIns", "XCODE_EMBED_PLUGINS",
                            dstSubfolderSpec, NoActionOnCopyByDefault);
@@ -4139,7 +4200,7 @@
 
 void cmGlobalXCodeGenerator::AddEmbeddedAppExtensions(cmXCodeObject* target)
 {
-  static const auto dstSubfolderSpec = "13";
+  static auto const* const dstSubfolderSpec = "13";
 
   this->AddEmbeddedObjects(target, "Embed App Extensions",
                            "XCODE_EMBED_APP_EXTENSIONS", dstSubfolderSpec,
@@ -4149,7 +4210,7 @@
 void cmGlobalXCodeGenerator::AddEmbeddedExtensionKitExtensions(
   cmXCodeObject* target)
 {
-  static const auto dstSubfolderSpec = "16";
+  static auto const* const dstSubfolderSpec = "16";
 
   this->AddEmbeddedObjects(target, "Embed App Extensions",
                            "XCODE_EMBED_EXTENSIONKIT_EXTENSIONS",
@@ -4157,6 +4218,15 @@
                            "$(EXTENSIONS_FOLDER_PATH)");
 }
 
+void cmGlobalXCodeGenerator::AddEmbeddedResources(cmXCodeObject* target)
+{
+  static const auto dstSubfolderSpec = "7";
+
+  this->AddEmbeddedObjects(target, "Embed Resources",
+                           "XCODE_EMBED_RESOURCES_PATH", dstSubfolderSpec,
+                           NoActionOnCopyByDefault);
+}
+
 bool cmGlobalXCodeGenerator::CreateGroups(
   std::vector<cmLocalGenerator*>& generators)
 {
@@ -4260,7 +4330,7 @@
     for (std::vector<std::string>::size_type i = 0; i < tgt_folders.size();
          i++) {
       if (i != 0) {
-        curr_tgt_folder += "/";
+        curr_tgt_folder += '/';
       }
       curr_tgt_folder += tgt_folders[i];
       it = this->TargetGroup.find(curr_tgt_folder);
@@ -4299,7 +4369,7 @@
       } else {
         tgroup = i_folder->second;
       }
-      curr_folder += "\\";
+      curr_folder += '\\';
     }
     return tgroup;
   }
@@ -4358,6 +4428,15 @@
   this->FrameworkGroup->AddAttribute("children", frameworkGroupChildren);
   this->MainGroupChildren->AddObject(this->FrameworkGroup);
 
+  this->ResourcesGroup = this->CreateObject(cmXCodeObject::PBXGroup);
+  this->ResourcesGroup->AddAttribute("name", this->CreateString("Resources"));
+  this->ResourcesGroup->AddAttribute("sourceTree",
+                                     this->CreateString("<group>"));
+  cmXCodeObject* ResourcesGroupChildren =
+    this->CreateObject(cmXCodeObject::OBJECT_LIST);
+  this->ResourcesGroup->AddAttribute("children", ResourcesGroupChildren);
+  this->MainGroupChildren->AddObject(this->ResourcesGroup);
+
   this->RootObject = this->CreateObject(cmXCodeObject::PBXProject);
   this->RootObject->SetComment("Project object");
 
@@ -4543,12 +4622,13 @@
     this->CustomCommandRoots.clear();
   }
   // loop over all targets and add link and depend info
-  for (auto t : targets) {
+  for (auto* t : targets) {
     this->AddDependAndLinkInformation(t);
     this->AddEmbeddedFrameworks(t);
     this->AddEmbeddedPlugIns(t);
     this->AddEmbeddedAppExtensions(t);
     this->AddEmbeddedExtensionKitExtensions(t);
+    this->AddEmbeddedResources(t);
     // Inherit project-wide values for any target-specific search paths.
     this->InheritBuildSettingAttribute(t, "HEADER_SEARCH_PATHS");
     this->InheritBuildSettingAttribute(t, "SYSTEM_HEADER_SEARCH_PATHS");
@@ -4569,7 +4649,7 @@
   }
   // now add all targets to the root object
   cmXCodeObject* allTargets = this->CreateObject(cmXCodeObject::OBJECT_LIST);
-  for (auto t : targets) {
+  for (auto* t : targets) {
     allTargets->AddObject(t);
     cmXCodeObject* productRef = t->GetAttribute("productReference");
     if (productRef) {
@@ -4637,16 +4717,17 @@
 {
   cmGeneratedFileStream makefileStream(this->CurrentXCodeHackMakefile);
   if (!makefileStream) {
-    cmSystemTools::Error("Could not create " + this->CurrentXCodeHackMakefile);
+    cmSystemTools::Error(
+      cmStrCat("Could not create ", this->CurrentXCodeHackMakefile));
     return;
   }
   makefileStream.SetCopyIfDifferent(true);
   // one more pass for external depend information not handled
   // correctly by xcode
   /* clang-format off */
-  makefileStream << "# DO NOT EDIT\n";
-  makefileStream << "# This makefile makes sure all linkable targets are\n";
-  makefileStream << "# up-to-date with anything they link to\n"
+  makefileStream << "# DO NOT EDIT\n"
+                    "# This makefile makes sure all linkable targets are\n"
+                    "# up-to-date with anything they link to\n"
     "default:\n"
     "\techo \"Do not invoke directly\"\n"
     "\n";
@@ -4662,7 +4743,7 @@
     "# does not seem to check these dependencies itself.\n";
   /* clang-format on */
   for (const auto& configName : this->CurrentConfigurationTypes) {
-    for (auto target : targets) {
+    for (auto* target : targets) {
       cmGeneratorTarget* gt = target->GetTarget();
 
       if (gt->GetType() == cmStateEnums::EXECUTABLE ||
@@ -4687,20 +4768,20 @@
         if (y != target->GetDependTargets().end()) {
           for (auto const& deptgt : y->second) {
             makefileStream << this->PostBuildMakeTarget(deptgt, configName)
-                           << ": " << trel << "\n";
+                           << ": " << trel << '\n';
           }
         }
 
         std::vector<cmGeneratorTarget*> objlibs;
         gt->GetObjectLibrariesCMP0026(objlibs);
-        for (auto objLib : objlibs) {
+        for (auto* objLib : objlibs) {
           makefileStream << this->PostBuildMakeTarget(objLib->GetName(),
                                                       configName)
-                         << ": " << trel << "\n";
+                         << ": " << trel << '\n';
         }
 
         // Create a rule for this target.
-        makefileStream << trel << ":";
+        makefileStream << trel << ':';
 
         // List dependencies if any exist.
         auto const x = target->GetDependLibraries().find(configName);
@@ -4712,7 +4793,7 @@
           }
         }
 
-        for (auto objLib : objlibs) {
+        for (auto* objLib : objlibs) {
 
           const std::string objLibName = objLib->GetName();
           std::string d = cmStrCat(this->GetTargetTempDir(gt, configName),
@@ -4724,9 +4805,9 @@
         }
 
         // Write the action to remove the target if it is out of date.
-        makefileStream << "\n";
-        makefileStream << "\t/bin/rm -f "
-                       << this->ConvertToRelativeForMake(tfull) << "\n";
+        makefileStream << "\n"
+                          "\t/bin/rm -f "
+                       << this->ConvertToRelativeForMake(tfull) << '\n';
         // if building for more than one architecture
         // then remove those executables as well
         if (this->Architectures.size() > 1) {
@@ -4737,7 +4818,7 @@
                                                  gt->GetFullName(configName));
             makefileStream << "\t/bin/rm -f "
                            << this->ConvertToRelativeForMake(universalFile)
-                           << "\n";
+                           << '\n';
           }
         }
         makefileStream << "\n\n";
@@ -4746,8 +4827,8 @@
   }
 
   makefileStream << "\n\n"
-                 << "# For each target create a dummy rule"
-                 << "so the target does not have to exist\n";
+                    "# For each target create a dummy rule"
+                    "so the target does not have to exist\n";
   for (auto const& dummyRule : dummyRules) {
     makefileStream << dummyRule << ":\n";
   }
@@ -4765,7 +4846,7 @@
   std::string xcodeDir = cmStrCat(root->GetCurrentBinaryDirectory(), '/',
                                   root->GetProjectName(), ".xcodeproj");
   cmSystemTools::MakeDirectory(xcodeDir);
-  std::string xcodeProjFile = xcodeDir + "/project.pbxproj";
+  std::string xcodeProjFile = cmStrCat(xcodeDir, "/project.pbxproj");
   cmGeneratedFileStream fout(xcodeProjFile);
   fout.SetCopyIfDifferent(true);
   if (!fout) {
@@ -4890,8 +4971,8 @@
 {
   SortXCodeObjects();
 
-  fout << "// !$*UTF8*$!\n";
-  fout << "{\n";
+  fout << "// !$*UTF8*$!\n"
+          "{\n";
   cmXCodeObject::Indent(1, fout);
   fout << "archiveVersion = 1;\n";
   cmXCodeObject::Indent(1, fout);
@@ -4903,8 +4984,8 @@
   cmXCode21Object::PrintList(this->XCodeObjects, fout);
   cmXCodeObject::Indent(1, fout);
   fout << "rootObject = " << this->RootObject->GetId()
-       << " /* Project object */;\n";
-  fout << "}\n";
+       << " /* Project object */;\n"
+          "}\n";
 }
 
 const char* cmGlobalXCodeGenerator::GetCMakeCFGIntDir() const
@@ -5043,7 +5124,7 @@
 
   // Separate from previous flags.
   if (!flags.empty()) {
-    flags += " ";
+    flags += ' ';
   }
 
   // Check if the flag needs quoting.
@@ -5062,7 +5143,7 @@
 
   if (quoteFlag) {
     // Open single quote.
-    flags += "'";
+    flags += '\'';
   }
 
   // Flag value with escaped quotes and backslashes.
@@ -5078,7 +5159,7 @@
 
   if (quoteFlag) {
     // Close single quote.
-    flags += "'";
+    flags += '\'';
   }
 }
 
@@ -5155,6 +5236,10 @@
     case cmMakefile::AppleSDK::WatchSimulator:
       return "WATCHOS_DEPLOYMENT_TARGET";
 
+    case cmMakefile::AppleSDK::XROS:
+    case cmMakefile::AppleSDK::XRSimulator:
+      return "XROS_DEPLOYMENT_TARGET";
+
     case cmMakefile::AppleSDK::MacOS:
     default:
       return "MACOSX_DEPLOYMENT_TARGET";
diff --git a/Source/cmGlobalXCodeGenerator.h b/Source/cmGlobalXCodeGenerator.h
index 1fdd189..da0a4ea 100644
--- a/Source/cmGlobalXCodeGenerator.h
+++ b/Source/cmGlobalXCodeGenerator.h
@@ -120,7 +120,7 @@
    * Used to determine if this generator supports DEPFILE option.
    */
   bool SupportsCustomCommandDepfile() const override { return true; }
-  virtual cm::optional<cmDepfileFormat> DepfileFormat() const override
+  cm::optional<cmDepfileFormat> DepfileFormat() const override
   {
     return this->XcodeBuildSystem == BuildSystem::One
       ? cmDepfileFormat::MakeDepfile
@@ -224,6 +224,7 @@
   void AddEmbeddedPlugIns(cmXCodeObject* target);
   void AddEmbeddedAppExtensions(cmXCodeObject* target);
   void AddEmbeddedExtensionKitExtensions(cmXCodeObject* target);
+  void AddEmbeddedResources(cmXCodeObject* target);
   void AddPositionIndependentLinkAttribute(cmGeneratorTarget* target,
                                            cmXCodeObject* buildSettings,
                                            const std::string& configName);
@@ -355,6 +356,7 @@
                                   std::string const& configName);
   cmXCodeObject* MainGroupChildren;
   cmXCodeObject* FrameworkGroup;
+  cmXCodeObject* ResourcesGroup;
   cmMakefile* CurrentMakefile;
   cmLocalGenerator* CurrentLocalGenerator;
   cmLocalGenerator* CurrentRootGenerator = nullptr;
diff --git a/Source/cmImportedCxxModuleInfo.cxx b/Source/cmImportedCxxModuleInfo.cxx
new file mode 100644
index 0000000..9e3ac9a
--- /dev/null
+++ b/Source/cmImportedCxxModuleInfo.cxx
@@ -0,0 +1,76 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmImportedCxxModuleInfo.h"
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "cmCryptoHash.h"
+#include "cmList.h"
+#include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
+
+bool ImportedCxxModuleLookup::Initialized() const
+{
+  return this->DoneInit;
+}
+
+void ImportedCxxModuleLookup::Initialize(std::string const& importedModules)
+{
+  for (auto const& entry : cmList{ importedModules }) {
+    auto nameSep = entry.find('=');
+    if (nameSep == std::string::npos) {
+      // Invalid entry; ignore.
+      continue;
+    }
+
+    auto name = entry.substr(0, nameSep);
+
+    auto sourceSep = entry.find(',', nameSep);
+    std::string source;
+    if (sourceSep == std::string::npos) {
+      source = entry.substr(nameSep + 1);
+    } else {
+      source = entry.substr(nameSep + 1, sourceSep - nameSep - 1);
+    }
+
+    std::vector<std::string> bmis;
+    if (sourceSep != std::string::npos) {
+      auto bmiPaths = entry.substr(sourceSep + 1);
+      bmis = cmSystemTools::SplitString(bmiPaths, ',');
+    }
+
+    this->ImportedInfo.emplace(source,
+                               ImportedCxxModuleInfo{ name, std::move(bmis) });
+  }
+
+  this->DoneInit = true;
+}
+
+std::string ImportedCxxModuleLookup::BmiNameForSource(std::string const& path)
+{
+  auto genit = this->GeneratorInfo.find(path);
+  if (genit != this->GeneratorInfo.end()) {
+    return genit->second.BmiName;
+  }
+
+  auto importit = this->ImportedInfo.find(path);
+  std::string bmiName;
+  auto hasher = cmCryptoHash::New("SHA3_512");
+  constexpr size_t HASH_TRUNCATION = 12;
+  if (importit != this->ImportedInfo.end()) {
+    auto safename = hasher->HashString(importit->second.Name);
+    bmiName = cmStrCat(safename.substr(0, HASH_TRUNCATION), ".bmi");
+  } else {
+    auto dirhash = hasher->HashString(path);
+    bmiName = cmStrCat(dirhash.substr(0, HASH_TRUNCATION), ".bmi");
+  }
+
+  this->GeneratorInfo.emplace(
+    path, ImportedCxxModuleGeneratorInfo{ &importit->second, bmiName });
+  return bmiName;
+}
diff --git a/Source/cmImportedCxxModuleInfo.h b/Source/cmImportedCxxModuleInfo.h
new file mode 100644
index 0000000..e052283
--- /dev/null
+++ b/Source/cmImportedCxxModuleInfo.h
@@ -0,0 +1,37 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <map>
+#include <string>
+#include <vector>
+
+struct ImportedCxxModuleInfo
+{
+  std::string const Name;
+  std::vector<std::string> const AvailableBmis;
+};
+
+struct ImportedCxxModuleGeneratorInfo
+{
+  ImportedCxxModuleInfo const* ImportedInfo;
+  std::string const BmiName;
+};
+
+struct ImportedCxxModuleLookup
+{
+  ImportedCxxModuleLookup() = default;
+  ~ImportedCxxModuleLookup() = default;
+
+  bool Initialized() const;
+  void Initialize(std::string const& importedModules);
+
+  std::string BmiNameForSource(std::string const& path);
+
+private:
+  bool DoneInit = false;
+  std::map<std::string, ImportedCxxModuleInfo> ImportedInfo;
+  std::map<std::string, ImportedCxxModuleGeneratorInfo> GeneratorInfo;
+};
diff --git a/Source/cmIncludeGuardCommand.cxx b/Source/cmIncludeGuardCommand.cxx
index e0a6958..2b0e03c 100644
--- a/Source/cmIncludeGuardCommand.cxx
+++ b/Source/cmIncludeGuardCommand.cxx
@@ -2,11 +2,12 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmIncludeGuardCommand.h"
 
+#include "cmCryptoHash.h"
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
 #include "cmStateDirectory.h"
 #include "cmStateSnapshot.h"
-#include "cmSystemTools.h"
+#include "cmStringAlgorithms.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -21,14 +22,8 @@
 
 std::string GetIncludeGuardVariableName(std::string const& filePath)
 {
-  std::string result = "__INCGUARD_";
-#ifndef CMAKE_BOOTSTRAP
-  result += cmSystemTools::ComputeStringMD5(filePath);
-#else
-  result += cmSystemTools::MakeCidentifier(filePath);
-#endif
-  result += "__";
-  return result;
+  cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+  return cmStrCat("__INCGUARD_", hasher.HashString(filePath), "__");
 }
 
 bool CheckIncludeGuardIsSet(cmMakefile* mf, std::string const& includeGuardVar)
diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx
index 7f5f15b..0fc4011 100644
--- a/Source/cmInstallCommand.cxx
+++ b/Source/cmInstallCommand.cxx
@@ -21,7 +21,6 @@
 #include "cmArgumentParser.h"
 #include "cmArgumentParserTypes.h"
 #include "cmExecutionStatus.h"
-#include "cmExperimental.h"
 #include "cmExportSet.h"
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
@@ -491,6 +490,7 @@
   publicHeaderArgs.Parse(argVectors.PublicHeader, &unknownArgs);
   resourceArgs.Parse(argVectors.Resource, &unknownArgs);
   includesArgs.Parse(&argVectors.Includes, &unknownArgs);
+  cxxModuleBmiArgs.Parse(argVectors.CxxModulesBmi, &unknownArgs);
   for (std::size_t i = 0; i < argVectors.FileSets.size(); i++) {
     // We have to create a separate object for the parsing because
     // cmArgumentParser<void>::Bind() binds to a specific address, but the
@@ -501,15 +501,6 @@
     fileSetArgs[i] = std::move(fileSetArg);
   }
 
-  bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
-    *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
-  if (!supportCxx20FileSetTypes) {
-    std::copy(argVectors.CxxModulesBmi.begin(), argVectors.CxxModulesBmi.end(),
-              std::back_inserter(unknownArgs));
-  } else {
-    cxxModuleBmiArgs.Parse(argVectors.CxxModulesBmi, &unknownArgs);
-  }
-
   if (!unknownArgs.empty()) {
     // Unknown argument.
     status.SetError(
@@ -541,12 +532,10 @@
   success = success && privateHeaderArgs.Finalize();
   success = success && publicHeaderArgs.Finalize();
   success = success && resourceArgs.Finalize();
+  success = success && cxxModuleBmiArgs.Finalize();
   for (auto& fileSetArg : fileSetArgs) {
     success = success && fileSetArg.Finalize();
   }
-  if (supportCxx20FileSetTypes) {
-    success = success && cxxModuleBmiArgs.Finalize();
-  }
 
   if (!success) {
     return false;
@@ -1173,8 +1162,7 @@
       }
     }
 
-    if (supportCxx20FileSetTypes &&
-        !cxxModuleBmiArgs.GetDestination().empty()) {
+    if (!cxxModuleBmiArgs.GetDestination().empty()) {
       cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>(
         target.GetName(),
         helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs),
@@ -1837,8 +1825,7 @@
       }
 
       // Make sure the name is a directory.
-      if (cmSystemTools::FileExists(dir) &&
-          !cmSystemTools::FileIsDirectory(dir)) {
+      if (cmSystemTools::FileExists(dir, true)) {
         status.SetError(cmStrCat(args[0], " given non-directory \"", args[i],
                                  "\" to install."));
         return false;
@@ -2072,12 +2059,7 @@
   ica.Bind("NAMESPACE"_s, name_space);
   ica.Bind("EXPORT_LINK_INTERFACE_LIBRARIES"_s, exportOld);
   ica.Bind("FILE"_s, filename);
-
-  bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
-    *helper.Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
-  if (supportCxx20FileSetTypes) {
-    ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory);
-  }
+  ica.Bind("CXX_MODULES_DIRECTORY"_s, cxx_modules_directory);
 
   std::vector<std::string> unknownArgs;
   ica.Parse(args, &unknownArgs);
diff --git a/Source/cmInstallCxxModuleBmiGenerator.cxx b/Source/cmInstallCxxModuleBmiGenerator.cxx
index 1ef1eaa..111691e 100644
--- a/Source/cmInstallCxxModuleBmiGenerator.cxx
+++ b/Source/cmInstallCxxModuleBmiGenerator.cxx
@@ -11,6 +11,7 @@
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmOutputConverter.h"
+#include "cmScriptGenerator.h"
 #include "cmStringAlgorithms.h"
 
 cmInstallCxxModuleBmiGenerator::cmInstallCxxModuleBmiGenerator(
diff --git a/Source/cmInstallCxxModuleBmiGenerator.h b/Source/cmInstallCxxModuleBmiGenerator.h
index 21edb2e..23ed02e 100644
--- a/Source/cmInstallCxxModuleBmiGenerator.h
+++ b/Source/cmInstallCxxModuleBmiGenerator.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmGeneratorTarget;
 class cmListFileBacktrace;
diff --git a/Source/cmInstallDirectoryGenerator.h b/Source/cmInstallDirectoryGenerator.h
index 7deb9ba..96a924f 100644
--- a/Source/cmInstallDirectoryGenerator.h
+++ b/Source/cmInstallDirectoryGenerator.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmListFileBacktrace;
 class cmLocalGenerator;
diff --git a/Source/cmInstallExportGenerator.cxx b/Source/cmInstallExportGenerator.cxx
index 1d81b0b..71d5471 100644
--- a/Source/cmInstallExportGenerator.cxx
+++ b/Source/cmInstallExportGenerator.cxx
@@ -8,6 +8,7 @@
 
 #include <cm/memory>
 
+#include "cmCryptoHash.h"
 #ifndef CMAKE_BOOTSTRAP
 #  include "cmExportInstallAndroidMKGenerator.h"
 #endif
@@ -16,6 +17,7 @@
 #include "cmInstallType.h"
 #include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
+#include "cmScriptGenerator.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
@@ -63,11 +65,10 @@
     return path;
   }
 
-#ifndef CMAKE_BOOTSTRAP
+  cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
   path += '/';
   // Replace the destination path with a hash to keep it short.
-  path += cmSystemTools::ComputeStringMD5(this->Destination);
-#endif
+  path += hasher.HashString(this->Destination);
 
   return path;
 }
diff --git a/Source/cmInstallExportGenerator.h b/Source/cmInstallExportGenerator.h
index 346ca67..f2d4a05 100644
--- a/Source/cmInstallExportGenerator.h
+++ b/Source/cmInstallExportGenerator.h
@@ -10,7 +10,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmExportInstallFileGenerator;
 class cmExportSet;
diff --git a/Source/cmInstallFileSetGenerator.h b/Source/cmInstallFileSetGenerator.h
index 56341d4..708cee6 100644
--- a/Source/cmInstallFileSetGenerator.h
+++ b/Source/cmInstallFileSetGenerator.h
@@ -8,7 +8,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmGeneratorTarget;
 class cmFileSet;
diff --git a/Source/cmInstallFilesGenerator.h b/Source/cmInstallFilesGenerator.h
index 53076b3..874bde1 100644
--- a/Source/cmInstallFilesGenerator.h
+++ b/Source/cmInstallFilesGenerator.h
@@ -9,7 +9,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmListFileBacktrace;
 class cmLocalGenerator;
diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
index 3e493bc..3f8a103 100644
--- a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
+++ b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx
@@ -19,6 +19,7 @@
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmOutputConverter.h"
+#include "cmScriptGenerator.h"
 #include "cmStringAlgorithms.h"
 
 namespace {
diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.h b/Source/cmInstallGetRuntimeDependenciesGenerator.h
index a2d6593..8e4e8cb 100644
--- a/Source/cmInstallGetRuntimeDependenciesGenerator.h
+++ b/Source/cmInstallGetRuntimeDependenciesGenerator.h
@@ -7,7 +7,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmListFileBacktrace;
 class cmLocalGenerator;
diff --git a/Source/cmInstallImportedRuntimeArtifactsGenerator.h b/Source/cmInstallImportedRuntimeArtifactsGenerator.h
index 9e045ee..84d3fee 100644
--- a/Source/cmInstallImportedRuntimeArtifactsGenerator.h
+++ b/Source/cmInstallImportedRuntimeArtifactsGenerator.h
@@ -8,7 +8,6 @@
 
 #include "cmInstallGenerator.h"
 #include "cmListFileCache.h"
-#include "cmScriptGenerator.h"
 
 class cmGeneratorTarget;
 class cmLocalGenerator;
diff --git a/Source/cmInstallRuntimeDependencySetGenerator.h b/Source/cmInstallRuntimeDependencySetGenerator.h
index 680361b..1a3c1e4 100644
--- a/Source/cmInstallRuntimeDependencySetGenerator.h
+++ b/Source/cmInstallRuntimeDependencySetGenerator.h
@@ -7,7 +7,6 @@
 #include <vector>
 
 #include "cmInstallGenerator.h"
-#include "cmScriptGenerator.h"
 
 class cmInstallRuntimeDependencySet;
 class cmListFileBacktrace;
diff --git a/Source/cmInstallScriptGenerator.h b/Source/cmInstallScriptGenerator.h
index c3a7058..012a4a3 100644
--- a/Source/cmInstallScriptGenerator.h
+++ b/Source/cmInstallScriptGenerator.h
@@ -9,7 +9,6 @@
 
 #include "cmInstallGenerator.h"
 #include "cmListFileCache.h"
-#include "cmScriptGenerator.h"
 
 class cmLocalGenerator;
 
diff --git a/Source/cmInstallTargetGenerator.cxx b/Source/cmInstallTargetGenerator.cxx
index 3ac100d..e7998ea 100644
--- a/Source/cmInstallTargetGenerator.cxx
+++ b/Source/cmInstallTargetGenerator.cxx
@@ -23,6 +23,7 @@
 #include "cmMessageType.h"
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
+#include "cmScriptGenerator.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
diff --git a/Source/cmInstallTargetGenerator.h b/Source/cmInstallTargetGenerator.h
index 2f41163..11a3264 100644
--- a/Source/cmInstallTargetGenerator.h
+++ b/Source/cmInstallTargetGenerator.h
@@ -11,7 +11,6 @@
 #include "cmInstallGenerator.h"
 #include "cmInstallType.h"
 #include "cmListFileCache.h"
-#include "cmScriptGenerator.h"
 
 class cmGeneratorTarget;
 class cmLocalGenerator;
diff --git a/Source/cmInstalledFile.h b/Source/cmInstalledFile.h
index 373c349..e0d7647 100644
--- a/Source/cmInstalledFile.h
+++ b/Source/cmInstalledFile.h
@@ -9,7 +9,8 @@
 #include <string>
 #include <vector>
 
-class cmCompiledGeneratorExpression;
+#include "cmGeneratorExpression.h"
+
 class cmMakefile;
 
 /** \class cmInstalledFile
diff --git a/Source/cmJSONHelpers.cxx b/Source/cmJSONHelpers.cxx
new file mode 100644
index 0000000..c36b56d
--- /dev/null
+++ b/Source/cmJSONHelpers.cxx
@@ -0,0 +1,113 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmJSONHelpers.h"
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONState.h"
+
+namespace JsonErrors {
+ErrorGenerator EXPECTED_TYPE(const std::string& type)
+{
+  return [type](const Json::Value* value, cmJSONState* state) -> void {
+    if (state->key().empty()) {
+      state->AddErrorAtValue(cmStrCat("Expected ", type), value);
+      return;
+    }
+    std::string errMsg = cmStrCat("\"", state->key(), "\" expected ", type);
+    if (value && value->isConvertibleTo(Json::ValueType::stringValue)) {
+      errMsg = cmStrCat(errMsg, ", got: ", value->asString());
+    }
+    state->AddErrorAtValue(errMsg, value);
+  };
+}
+
+void INVALID_STRING(const Json::Value* value, cmJSONState* state)
+{
+  JsonErrors::EXPECTED_TYPE("a string")(value, state);
+}
+
+void INVALID_BOOL(const Json::Value* value, cmJSONState* state)
+{
+  JsonErrors::EXPECTED_TYPE("a bool")(value, state);
+}
+
+void INVALID_INT(const Json::Value* value, cmJSONState* state)
+{
+  JsonErrors::EXPECTED_TYPE("an integer")(value, state);
+}
+
+void INVALID_UINT(const Json::Value* value, cmJSONState* state)
+{
+  JsonErrors::EXPECTED_TYPE("an unsigned integer")(value, state);
+}
+
+ObjectErrorGenerator INVALID_NAMED_OBJECT(
+  const std::function<std::string(const Json::Value*, cmJSONState*)>&
+    nameGenerator)
+{
+  return [nameGenerator](
+           ObjectError errorType,
+           const Json::Value::Members& extraFields) -> ErrorGenerator {
+    return [nameGenerator, errorType, extraFields](
+             const Json::Value* value, cmJSONState* state) -> void {
+      std::string name = nameGenerator(value, state);
+      switch (errorType) {
+        case ObjectError::RequiredMissing:
+          state->AddErrorAtValue(cmStrCat("Invalid Required ", name), value);
+          break;
+        case ObjectError::InvalidObject:
+          state->AddErrorAtValue(cmStrCat("Invalid ", name), value);
+          break;
+        case ObjectError::ExtraField: {
+          for (auto const& member : extraFields) {
+            if (value) {
+              state->AddErrorAtValue(
+                cmStrCat("Invalid extra field \"", member, "\" in ", name),
+                &(*value)[member]);
+            } else {
+              state->AddError(
+                cmStrCat("Invalid extra field \"", member, "\" in ", name));
+            }
+          }
+        } break;
+        case ObjectError::MissingRequired:
+          state->AddErrorAtValue(cmStrCat("Missing required field \"",
+                                          state->key(), "\" in ", name),
+                                 value);
+          break;
+      }
+    };
+  };
+}
+
+ErrorGenerator INVALID_OBJECT(ObjectError errorType,
+                              const Json::Value::Members& extraFields)
+{
+  return INVALID_NAMED_OBJECT(
+    [](const Json::Value*, cmJSONState*) -> std::string { return "Object"; })(
+    errorType, extraFields);
+}
+
+ErrorGenerator INVALID_NAMED_OBJECT_KEY(
+  ObjectError errorType, const Json::Value::Members& extraFields)
+{
+  return INVALID_NAMED_OBJECT(
+    [](const Json::Value*, cmJSONState* state) -> std::string {
+      for (auto it = state->parseStack.rbegin();
+           it != state->parseStack.rend(); ++it) {
+        if (it->first.rfind("$vector_item_", 0) == 0) {
+          continue;
+        }
+        return cmStrCat("\"", it->first, "\"");
+      }
+      return "root";
+    })(errorType, extraFields);
+}
+}
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h
index 94641de..24884ed 100644
--- a/Source/cmJSONHelpers.h
+++ b/Source/cmJSONHelpers.h
@@ -5,11 +5,11 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <algorithm>
-#include <cstddef>
 #include <functional>
 #include <iostream>
 #include <map>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include <cm/optional>
@@ -18,6 +18,7 @@
 #include <cm3p/json/value.h>
 
 #include "cmJSONState.h"
+#include "cmStringAlgorithms.h"
 
 template <typename T>
 using cmJSONHelper =
@@ -36,98 +37,25 @@
 using ErrorGenerator = std::function<void(const Json::Value*, cmJSONState*)>;
 using ObjectErrorGenerator =
   std::function<ErrorGenerator(ObjectError, const Json::Value::Members&)>;
-const auto EXPECTED_TYPE = [](const std::string& type) {
-  return [type](const Json::Value* value, cmJSONState* state) -> void {
-#if !defined(CMAKE_BOOTSTRAP)
-    if (state->key().empty()) {
-      state->AddErrorAtValue(cmStrCat("Expected ", type), value);
-      return;
-    }
-    std::string errMsg = cmStrCat("\"", state->key(), "\" expected ", type);
-    if (value && value->isConvertibleTo(Json::ValueType::stringValue)) {
-      errMsg = cmStrCat(errMsg, ", got: ", value->asString());
-    }
-    state->AddErrorAtValue(errMsg, value);
-#endif
-  };
-};
-const auto INVALID_STRING = [](const Json::Value* value,
-                               cmJSONState* state) -> void {
-  JsonErrors::EXPECTED_TYPE("a string")(value, state);
-};
-const auto INVALID_BOOL = [](const Json::Value* value,
-                             cmJSONState* state) -> void {
-  JsonErrors::EXPECTED_TYPE("a bool")(value, state);
-};
-const auto INVALID_INT = [](const Json::Value* value,
-                            cmJSONState* state) -> void {
-  JsonErrors::EXPECTED_TYPE("an integer")(value, state);
-};
-const auto INVALID_UINT = [](const Json::Value* value,
-                             cmJSONState* state) -> void {
-  JsonErrors::EXPECTED_TYPE("an unsigned integer")(value, state);
-};
-const auto INVALID_NAMED_OBJECT =
-  [](const std::function<std::string(const Json::Value*, cmJSONState*)>&
-       nameGenerator) -> ObjectErrorGenerator {
-  return [nameGenerator](
-           ObjectError errorType,
-           const Json::Value::Members& extraFields) -> ErrorGenerator {
-    return [nameGenerator, errorType, extraFields](
-             const Json::Value* value, cmJSONState* state) -> void {
-#if !defined(CMAKE_BOOTSTRAP)
-      std::string name = nameGenerator(value, state);
-      switch (errorType) {
-        case ObjectError::RequiredMissing:
-          state->AddErrorAtValue(cmStrCat("Invalid Required ", name), value);
-          break;
-        case ObjectError::InvalidObject:
-          state->AddErrorAtValue(cmStrCat("Invalid ", name), value);
-          break;
-        case ObjectError::ExtraField: {
-          for (auto const& member : extraFields) {
-            if (value) {
-              state->AddErrorAtValue(
-                cmStrCat("Invalid extra field \"", member, "\" in ", name),
-                &(*value)[member]);
-            } else {
-              state->AddError(
-                cmStrCat("Invalid extra field \"", member, "\" in ", name));
-            }
-          }
-        } break;
-        case ObjectError::MissingRequired:
-          state->AddErrorAtValue(cmStrCat("Missing required field \"",
-                                          state->key(), "\" in ", name),
-                                 value);
-          break;
-      }
-#endif
-    };
-  };
-};
-const auto INVALID_OBJECT =
-  [](ObjectError errorType,
-     const Json::Value::Members& extraFields) -> ErrorGenerator {
-  return INVALID_NAMED_OBJECT(
-    [](const Json::Value*, cmJSONState*) -> std::string { return "Object"; })(
-    errorType, extraFields);
-};
-const auto INVALID_NAMED_OBJECT_KEY =
-  [](ObjectError errorType,
-     const Json::Value::Members& extraFields) -> ErrorGenerator {
-  return INVALID_NAMED_OBJECT(
-    [](const Json::Value*, cmJSONState* state) -> std::string {
-      for (auto it = state->parseStack.rbegin();
-           it != state->parseStack.rend(); ++it) {
-        if (it->first.rfind("$vector_item_", 0) == 0) {
-          continue;
-        }
-        return cmStrCat("\"", it->first, "\"");
-      }
-      return "root";
-    })(errorType, extraFields);
-};
+ErrorGenerator EXPECTED_TYPE(const std::string& type);
+
+void INVALID_STRING(const Json::Value* value, cmJSONState* state);
+
+void INVALID_BOOL(const Json::Value* value, cmJSONState* state);
+
+void INVALID_INT(const Json::Value* value, cmJSONState* state);
+
+void INVALID_UINT(const Json::Value* value, cmJSONState* state);
+
+ObjectErrorGenerator INVALID_NAMED_OBJECT(
+  const std::function<std::string(const Json::Value*, cmJSONState*)>&
+    nameGenerator);
+
+ErrorGenerator INVALID_OBJECT(ObjectError errorType,
+                              const Json::Value::Members& extraFields);
+
+ErrorGenerator INVALID_NAMED_OBJECT_KEY(
+  ObjectError errorType, const Json::Value::Members& extraFields);
 }
 
 struct cmJSONHelperBuilder
diff --git a/Source/cmJSONState.cxx b/Source/cmJSONState.cxx
index 92bde77..1abdaa6 100644
--- a/Source/cmJSONState.cxx
+++ b/Source/cmJSONState.cxx
@@ -3,10 +3,9 @@
 
 #include "cmJSONState.h"
 
+#include <iterator>
 #include <sstream>
 
-#include <cm/memory>
-
 #include <cm3p/json/reader.h>
 #include <cm3p/json/value.h>
 
diff --git a/Source/cmJSONState.h b/Source/cmJSONState.h
index 4984c81..9071268 100644
--- a/Source/cmJSONState.h
+++ b/Source/cmJSONState.h
@@ -9,7 +9,6 @@
 #include <utility>
 #include <vector>
 
-#include "cmJSONState.h"
 #include "cmStringAlgorithms.h"
 
 namespace Json {
diff --git a/Source/cmLDConfigLDConfigTool.cxx b/Source/cmLDConfigLDConfigTool.cxx
index 0752b33..154aa27 100644
--- a/Source/cmLDConfigLDConfigTool.cxx
+++ b/Source/cmLDConfigLDConfigTool.cxx
@@ -14,6 +14,7 @@
 #include "cmRuntimeDependencyArchive.h"
 #include "cmSystemTools.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmLDConfigLDConfigTool::cmLDConfigLDConfigTool(
   cmRuntimeDependencyArchive* archive)
@@ -43,14 +44,15 @@
   builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
     .AddCommand(ldConfigCommand);
   auto process = builder.Start();
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     this->Archive->SetError("Failed to start ldconfig process");
     return false;
   }
 
   std::string line;
   static const cmsys::RegularExpression regex("^([^\t:]*):");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (regex.find(line.c_str(), match)) {
       paths.push_back(match.match(1));
@@ -61,8 +63,7 @@
     this->Archive->SetError("Failed to wait on ldconfig process");
     return false;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
+  if (process.GetStatus(0).ExitStatus != 0) {
     this->Archive->SetError("Failed to run ldconfig");
     return false;
   }
diff --git a/Source/cmLinkDirectoriesCommand.cxx b/Source/cmLinkDirectoriesCommand.cxx
index 1ec071b..6775a60 100644
--- a/Source/cmLinkDirectoriesCommand.cxx
+++ b/Source/cmLinkDirectoriesCommand.cxx
@@ -6,6 +6,7 @@
 
 #include "cmExecutionStatus.h"
 #include "cmGeneratorExpression.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
@@ -39,7 +40,7 @@
     AddLinkDir(mf, *i, directories);
   }
 
-  mf.AddLinkDirectory(cmJoin(directories, ";"), before);
+  mf.AddLinkDirectory(cmList::to_string(directories), before);
 
   return true;
 }
diff --git a/Source/cmLinkLineComputer.cxx b/Source/cmLinkLineComputer.cxx
index b7ee4fa..a29e33f 100644
--- a/Source/cmLinkLineComputer.cxx
+++ b/Source/cmLinkLineComputer.cxx
@@ -65,7 +65,8 @@
   ItemVector const& items = cli.GetItems();
   for (auto const& item : items) {
     if (item.Target &&
-        item.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
+        (item.Target->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
+         item.Target->GetType() == cmStateEnums::OBJECT_LIBRARY)) {
       continue;
     }
 
@@ -191,24 +192,16 @@
 }
 
 std::string cmLinkLineComputer::ComputeFrameworkPath(
-  cmComputeLinkInformation& cli, cmValue fwSearchFlag, cmValue sysFwSearchFlag)
+  cmComputeLinkInformation& cli, cmValue fwSearchFlag)
 {
-  if (!fwSearchFlag && !sysFwSearchFlag) {
+  if (!fwSearchFlag) {
     return std::string{};
   }
 
   std::string frameworkPath;
-  auto const& fwDirs = cli.GetFrameworkPaths();
-  for (auto const& fd : fwDirs) {
-    if (sysFwSearchFlag &&
-        cli.GetTarget()->IsSystemIncludeDirectory(fd, cli.GetConfig(),
-                                                  cli.GetLinkLanguage())) {
-      frameworkPath += sysFwSearchFlag;
-    } else {
-      frameworkPath += fwSearchFlag;
-    }
-    frameworkPath += this->ConvertToOutputFormat(fd);
-    frameworkPath += " ";
+  for (auto const& fd : cli.GetFrameworkPaths()) {
+    frameworkPath +=
+      cmStrCat(fwSearchFlag, this->ConvertToOutputFormat(fd), ' ');
   }
   return frameworkPath;
 }
diff --git a/Source/cmLinkLineComputer.h b/Source/cmLinkLineComputer.h
index 4e285f2..afd3944 100644
--- a/Source/cmLinkLineComputer.h
+++ b/Source/cmLinkLineComputer.h
@@ -44,8 +44,7 @@
                        std::vector<BT<std::string>>& linkPath);
 
   std::string ComputeFrameworkPath(cmComputeLinkInformation& cli,
-                                   cmValue fwSearchFlag,
-                                   cmValue sysFwSearchFlag);
+                                   cmValue fwSearchFlag);
 
   std::string ComputeLinkLibraries(cmComputeLinkInformation& cli,
                                    std::string const& stdLibString);
diff --git a/Source/cmLinkLineDeviceComputer.cxx b/Source/cmLinkLineDeviceComputer.cxx
index b06dc3d..28aa5d9 100644
--- a/Source/cmLinkLineDeviceComputer.cxx
+++ b/Source/cmLinkLineDeviceComputer.cxx
@@ -101,9 +101,7 @@
   ItemVector const& items = cli.GetItems();
   std::string config = cli.GetConfig();
   bool skipItemAfterFramework = false;
-  // Note:
-  // Any modification of this algorithm should be reflected also in
-  // cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions
+
   for (auto const& item : items) {
     if (skipItemAfterFramework) {
       skipItemAfterFramework = false;
@@ -115,6 +113,7 @@
       switch (item.Target->GetType()) {
         case cmStateEnums::SHARED_LIBRARY:
         case cmStateEnums::MODULE_LIBRARY:
+        case cmStateEnums::OBJECT_LIBRARY:
         case cmStateEnums::INTERFACE_LIBRARY:
           skip = true;
           break;
@@ -131,11 +130,13 @@
 
     BT<std::string> linkLib;
     if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
-      // nvcc understands absolute paths to libraries ending in '.a' or '.lib'.
-      // These should be passed to nvlink.  Other extensions need to be left
-      // out because nvlink may not understand or need them.  Even though it
-      // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
-      if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
+      // nvcc understands absolute paths to libraries ending in '.o', .a', or
+      // '.lib'. These should be passed to nvlink.  Other extensions need to be
+      // left out because nvlink may not understand or need them.  Even though
+      // it can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
+      if (cmHasLiteralSuffix(item.Value.Value, ".o") ||
+          cmHasLiteralSuffix(item.Value.Value, ".obj") ||
+          cmHasLiteralSuffix(item.Value.Value, ".a") ||
           cmHasLiteralSuffix(item.Value.Value, ".lib")) {
         linkLib.Value = item
                           .GetFormattedItem(this->ConvertToOutputFormat(
diff --git a/Source/cmList.cxx b/Source/cmList.cxx
index 28d4791..939fbb5 100644
--- a/Source/cmList.cxx
+++ b/Source/cmList.cxx
@@ -19,6 +19,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmGeneratorExpression.h"
+#include "cmListFileCache.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
@@ -1004,3 +1005,8 @@
   }
   return container.begin() + delta;
 }
+
+std::string const& cmList::ToString(BT<std::string> const& s)
+{
+  return s.Value;
+}
diff --git a/Source/cmList.h b/Source/cmList.h
index df450fd..1b94c2f 100644
--- a/Source/cmList.h
+++ b/Source/cmList.h
@@ -21,6 +21,9 @@
 
 #include "cmValue.h"
 
+template <typename T>
+class BT;
+
 /**
  * CMake lists management
  * A CMake list is a string where list elements are separated by the ';'
@@ -744,8 +747,10 @@
                        ExpandElements expandElements = ExpandElements::Yes,
                        EmptyElements emptyElements = EmptyElements::No)
   {
-    this->insert(this->begin() + this->ComputeInsertIndex(index), first, last,
-                 expandElements, emptyElements);
+    auto const offset =
+      static_cast<difference_type>(this->ComputeInsertIndex(index));
+    this->insert(this->begin() + offset, first, last, expandElements,
+                 emptyElements);
     return *this;
   }
   template <typename InputIterator>
@@ -1121,6 +1126,13 @@
       list, cmList::Join(first, last, cmList::element_separator));
   }
 
+  template <typename Range,
+            cm::enable_if_t<cm::is_range<Range>::value, int> = 0>
+  static std::string to_string(Range const& r)
+  {
+    return cmList::Join(r, cmList::element_separator);
+  }
+
   // Non-members
   // ===========
   friend inline bool operator==(const cmList& lhs, const cmList& rhs) noexcept
@@ -1176,13 +1188,13 @@
         auto size = container.size();
         insertPos = cmList::Insert(container, insertPos, *first,
                                    expandElements, emptyElements);
-        insertPos += container.size() - size;
+        insertPos += static_cast<decltype(delta)>(container.size() - size);
       }
     } else {
       for (; first != last; ++first) {
         if (!first->empty() || emptyElements == EmptyElements::Yes) {
           insertPos = container.insert(insertPos, *first);
-          insertPos++;
+          ++insertPos;
         }
       }
     }
@@ -1192,6 +1204,7 @@
 
   static std::string const& ToString(std::string const& s) { return s; }
   static std::string ToString(cm::string_view s) { return std::string{ s }; }
+  static std::string const& ToString(BT<std::string> const&);
 
   template <typename Range>
   static std::string Join(Range const& r, cm::string_view glue)
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index da95b19..fe8d502 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -10,6 +10,7 @@
 #include <initializer_list>
 #include <iterator>
 #include <sstream>
+#include <type_traits>
 #include <unordered_set>
 #include <utility>
 #include <vector>
@@ -24,6 +25,7 @@
 
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
+#include "cmCryptoHash.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
 #include "cmCustomCommandLines.h"
@@ -40,6 +42,7 @@
 #include "cmLinkLineDeviceComputer.h"
 #include "cmList.h"
 #include "cmMakefile.h"
+#include "cmMessageType.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
@@ -57,11 +60,6 @@
 #include "cmVersion.h"
 #include "cmake.h"
 
-#if !defined(CMAKE_BOOTSTRAP)
-#  define CM_LG_ENCODE_OBJECT_NAMES
-#  include "cmCryptoHash.h"
-#endif
-
 #if defined(__HAIKU__)
 #  include <FindDirectory.h>
 #  include <StorageDefs.h>
@@ -89,6 +87,8 @@
                                 "CMAKE_TAPI",
                                 "CMAKE_CUDA_HOST_COMPILER",
                                 "CMAKE_CUDA_HOST_LINK_LAUNCHER",
+                                "CMAKE_HIP_HOST_COMPILER",
+                                "CMAKE_HIP_HOST_LINK_LAUNCHER",
                                 "CMAKE_CL_SHOWINCLUDES_PREFIX" };
 
 cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
@@ -1431,11 +1431,14 @@
   }
 
   this->AddVisibilityPresetFlags(linkFlags, target, "CUDA");
+  this->GetGlobalGenerator()->EncodeLiteral(linkFlags);
 
   std::vector<std::string> linkOpts;
   target->GetLinkOptions(linkOpts, config, "CUDA");
+  this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript());
   // LINK_OPTIONS are escaped.
   this->AppendCompileOptions(linkFlags, linkOpts);
+  this->SetLinkScriptShell(false);
 }
 
 void cmLocalGenerator::GetTargetFlags(
@@ -1501,13 +1504,17 @@
       }
 
       if (!sharedLibFlags.empty()) {
+        this->GetGlobalGenerator()->EncodeLiteral(sharedLibFlags);
         linkFlags.emplace_back(std::move(sharedLibFlags));
       }
 
       std::vector<BT<std::string>> linkOpts =
         target->GetLinkOptions(config, linkLanguage);
+      this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript());
       // LINK_OPTIONS are escaped.
       this->AppendCompileOptions(linkFlags, linkOpts);
+      this->SetLinkScriptShell(false);
+
       if (pcli) {
         this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
                                   frameworkPath, linkPath);
@@ -1581,13 +1588,16 @@
       }
 
       if (!exeFlags.empty()) {
+        this->GetGlobalGenerator()->EncodeLiteral(exeFlags);
         linkFlags.emplace_back(std::move(exeFlags));
       }
 
       std::vector<BT<std::string>> linkOpts =
         target->GetLinkOptions(config, linkLanguage);
+      this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript());
       // LINK_OPTIONS are escaped.
       this->AppendCompileOptions(linkFlags, linkOpts);
+      this->SetLinkScriptShell(false);
     } break;
     default:
       break;
@@ -1603,6 +1613,7 @@
                                    config);
 
   if (!extraLinkFlags.empty()) {
+    this->GetGlobalGenerator()->EncodeLiteral(extraLinkFlags);
     linkFlags.emplace_back(std::move(extraLinkFlags));
   }
 }
@@ -1648,6 +1659,8 @@
   this->AppendFlags(compileFlags, mf->GetDefineFlags());
   this->AppendFlags(compileFlags,
                     this->GetFrameworkFlags(lang, config, target));
+  this->AppendFlags(compileFlags,
+                    this->GetXcFrameworkFlags(lang, config, target));
 
   if (!compileFlags.empty()) {
     flags.emplace_back(std::move(compileFlags));
@@ -1713,6 +1726,43 @@
   return flags;
 }
 
+std::string cmLocalGenerator::GetXcFrameworkFlags(std::string const& lang,
+                                                  std::string const& config,
+                                                  cmGeneratorTarget* target)
+{
+  cmLocalGenerator* lg = target->GetLocalGenerator();
+  cmMakefile* mf = lg->GetMakefile();
+
+  if (!target->IsApple()) {
+    return std::string();
+  }
+
+  cmValue includeSearchFlag =
+    mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
+  cmValue sysIncludeSearchFlag =
+    mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
+
+  if (!includeSearchFlag && !sysIncludeSearchFlag) {
+    return std::string{};
+  }
+
+  std::string flags;
+  if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) {
+    std::vector<std::string> const& paths = cli->GetXcFrameworkHeaderPaths();
+    for (std::string const& path : paths) {
+      if (sysIncludeSearchFlag &&
+          target->IsSystemIncludeDirectory(path, config, lang)) {
+        flags += *sysIncludeSearchFlag;
+      } else {
+        flags += *includeSearchFlag;
+      }
+      flags += lg->ConvertToOutputFormat(path, cmOutputConverter::SHELL);
+      flags += " ";
+    }
+  }
+  return flags;
+}
+
 void cmLocalGenerator::GetTargetDefines(cmGeneratorTarget const* target,
                                         std::string const& config,
                                         std::string const& lang,
@@ -1802,11 +1852,8 @@
   // Append the framework search path flags.
   cmValue fwSearchFlag = this->Makefile->GetDefinition(
     cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG"));
-  cmValue sysFwSearchFlag = this->Makefile->GetDefinition(
-    cmStrCat("CMAKE_", linkLanguage, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
 
-  frameworkPath =
-    linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag, sysFwSearchFlag);
+  frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag);
   linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator,
                                     linkPath);
   linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries);
@@ -2010,7 +2057,7 @@
         this->Makefile->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID");
     }
   } else if (lang == "HIP") {
-    target->AddHIPArchitectureFlags(flags);
+    target->AddHIPArchitectureFlags(compileOrLink, config, flags);
   }
 
   // Add VFS Overlay for Clang compilers
@@ -2722,10 +2769,10 @@
                 }
 
                 if (editAndContinueDebugInfo || msvc2008OrLess) {
-                  this->CopyPchCompilePdb(config, target, *ReuseFrom,
+                  this->CopyPchCompilePdb(config, lang, target, *ReuseFrom,
                                           reuseTarget, { ".pdb", ".idb" });
                 } else if (programDatabaseDebugInfo) {
-                  this->CopyPchCompilePdb(config, target, *ReuseFrom,
+                  this->CopyPchCompilePdb(config, lang, target, *ReuseFrom,
                                           reuseTarget, { ".pdb" });
                 }
               }
@@ -2782,9 +2829,9 @@
 }
 
 void cmLocalGenerator::CopyPchCompilePdb(
-  const std::string& config, cmGeneratorTarget* target,
-  const std::string& ReuseFrom, cmGeneratorTarget* reuseTarget,
-  const std::vector<std::string>& extensions)
+  const std::string& config, const std::string& language,
+  cmGeneratorTarget* target, const std::string& ReuseFrom,
+  cmGeneratorTarget* reuseTarget, const std::vector<std::string>& extensions)
 {
   const std::string pdb_prefix =
     this->GetGlobalGenerator()->IsMultiConfig() ? cmStrCat(config, "/") : "";
@@ -2868,6 +2915,7 @@
   cc->SetCommandLines(commandLines);
   cc->SetComment(no_message);
   cc->SetStdPipesUTF8(true);
+  cc->AppendDepends({ reuseTarget->GetPchFile(config, language) });
 
   if (this->GetGlobalGenerator()->IsVisualStudio()) {
     cc->SetByproducts(outputs);
@@ -2974,13 +3022,11 @@
     } else {
       pathToHash = "ABS_" + sf_full_path;
     }
+    cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
     unity_file << "/* " << pathToHash << " */\n"
                << "#undef " << *uniqueIdName << "\n"
                << "#define " << *uniqueIdName << " unity_"
-#ifndef CMAKE_BOOTSTRAP
-               << cmSystemTools::ComputeStringMD5(pathToHash) << "\n"
-#endif
-      ;
+               << hasher.HashString(pathToHash) << "\n";
   }
 
   if (beforeInclude) {
@@ -3648,9 +3694,9 @@
   }
 }
 
-#if defined(CM_LG_ENCODE_OBJECT_NAMES)
-static bool cmLocalGeneratorShortenObjectName(std::string& objName,
-                                              std::string::size_type max_len)
+namespace {
+bool cmLocalGeneratorShortenObjectName(std::string& objName,
+                                       std::string::size_type max_len)
 {
   // Check if the path can be shortened using an md5 sum replacement for
   // a portion of the path.
@@ -3694,7 +3740,7 @@
   // already too deep.
   return false;
 }
-#endif
+}
 
 std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName(
   const std::string& sin, std::string const& dir_max)
@@ -3744,7 +3790,6 @@
       } while (!done);
     }
 
-#if defined(CM_LG_ENCODE_OBJECT_NAMES)
     if (!cmLocalGeneratorCheckObjectName(ssin, dir_max.size(),
                                          this->ObjectPathMax)) {
       // Warn if this is the first time the path has been seen.
@@ -3765,9 +3810,6 @@
         this->IssueMessage(MessageType::WARNING, m.str());
       }
     }
-#else
-    (void)dir_max;
-#endif
 
     // Insert the newly mapped object file name.
     std::map<std::string, std::string>::value_type e(sin, ssin);
diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h
index c811408..a920cfe 100644
--- a/Source/cmLocalGenerator.h
+++ b/Source/cmLocalGenerator.h
@@ -19,8 +19,9 @@
 #include <cm3p/kwiml/int.h>
 
 #include "cmCustomCommandTypes.h"
+#include "cmGeneratorTarget.h"
 #include "cmListFileCache.h"
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 #include "cmOutputConverter.h"
 #include "cmPolicies.h"
 #include "cmStateSnapshot.h"
@@ -31,7 +32,6 @@
 class cmCustomCommand;
 class cmCustomCommandGenerator;
 class cmCustomCommandLines;
-class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmImplicitDependsList;
 class cmLinkLineComputer;
@@ -520,6 +520,9 @@
   std::string GetFrameworkFlags(std::string const& l,
                                 std::string const& config,
                                 cmGeneratorTarget* target);
+  std::string GetXcFrameworkFlags(std::string const& l,
+                                  std::string const& config,
+                                  cmGeneratorTarget* target);
   virtual std::string GetTargetFortranFlags(cmGeneratorTarget const* target,
                                             std::string const& config);
 
@@ -646,7 +649,9 @@
   bool AllAppleArchSysrootsAreTheSame(const std::vector<std::string>& archs,
                                       cmValue sysroot);
 
-  void CopyPchCompilePdb(const std::string& config, cmGeneratorTarget* target,
+  void CopyPchCompilePdb(const std::string& config,
+                         const std::string& language,
+                         cmGeneratorTarget* target,
                          const std::string& ReuseFrom,
                          cmGeneratorTarget* reuseTarget,
                          std::vector<std::string> const& extensions);
@@ -698,12 +703,6 @@
     std::string const& filename_base);
 };
 
-#if !defined(CMAKE_BOOTSTRAP)
-bool cmLocalGeneratorCheckObjectName(std::string& objName,
-                                     std::string::size_type dir_len,
-                                     std::string::size_type max_total_len);
-#endif
-
 namespace detail {
 void AddCustomCommandToTarget(cmLocalGenerator& lg, cmCommandOrigin origin,
                               cmTarget* target, cmCustomCommandType type,
diff --git a/Source/cmLocalNinjaGenerator.cxx b/Source/cmLocalNinjaGenerator.cxx
index 4b0604c..bc3da6e 100644
--- a/Source/cmLocalNinjaGenerator.cxx
+++ b/Source/cmLocalNinjaGenerator.cxx
@@ -14,6 +14,8 @@
 
 #include "cmsys/FStream.hxx"
 
+#include "cm_codecvt_Encoding.hxx"
+
 #include "cmCryptoHash.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
@@ -536,13 +538,14 @@
 
   std::ostringstream cmd;
 #ifdef _WIN32
+  cmGlobalNinjaGenerator const* gg = this->GetGlobalNinjaGenerator();
   bool const needCMD =
     cmdLines.size() > 1 || (customStep.empty() && RuleNeedsCMD(cmdLines[0]));
   for (auto li = cmdLines.begin(); li != cmdLines.end(); ++li) {
     if (li != cmdLines.begin()) {
       cmd << " && ";
     } else if (needCMD) {
-      cmd << "cmd.exe /C \"";
+      cmd << gg->GetComspec() << " /C \"";
     }
     // Put current cmdLine in brackets if it contains "||" because it has
     // higher precedence than "&&" in cmd.exe
diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx
index 3c6b303..e26a6ea 100644
--- a/Source/cmLocalUnixMakefileGenerator3.cxx
+++ b/Source/cmLocalUnixMakefileGenerator3.cxx
@@ -1080,6 +1080,15 @@
   // Setup the proper working directory for the commands.
   this->CreateCDCommand(commands1, dir, relative);
 
+  cmGlobalUnixMakefileGenerator3* gg =
+    static_cast<cmGlobalUnixMakefileGenerator3*>(this->GlobalGenerator);
+
+  // Prefix the commands with the jobserver prefix "+"
+  if (ccg.GetCC().GetJobserverAware() && gg->IsGNUMakeJobServerAware()) {
+    std::transform(commands1.begin(), commands1.end(), commands1.begin(),
+                   [](std::string const& cmd) { return cmStrCat("+", cmd); });
+  }
+
   // push back the custom commands
   cm::append(commands, commands1);
 }
diff --git a/Source/cmLocalVisualStudio10Generator.cxx b/Source/cmLocalVisualStudio10Generator.cxx
index 8fe6677..165f0fd 100644
--- a/Source/cmLocalVisualStudio10Generator.cxx
+++ b/Source/cmLocalVisualStudio10Generator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmLocalVisualStudio10Generator.h"
 
+#include <cmext/string_view>
+
 #include <cm3p/expat.h>
 
 #include "cmGlobalGenerator.h"
@@ -37,7 +39,7 @@
     if (!this->GUID.empty()) {
       return;
     }
-    if ("ProjectGUID" == name || "ProjectGuid" == name) {
+    if (name == "ProjectGUID"_s || name == "ProjectGuid"_s) {
       this->DoGUID = true;
     }
   }
@@ -93,7 +95,7 @@
   std::string guidStoreName = cmStrCat(name, "_GUID_CMAKE");
   // save the GUID in the cache
   this->GlobalGenerator->GetCMakeInstance()->AddCacheEntry(
-    guidStoreName, parser.GUID.c_str(), "Stored GUID", cmStateEnums::INTERNAL);
+    guidStoreName, parser.GUID, "Stored GUID", cmStateEnums::INTERNAL);
 }
 
 const char* cmLocalVisualStudio10Generator::ReportErrorLabel() const
diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx
index af0e118..7b02c56 100644
--- a/Source/cmLocalVisualStudio7Generator.cxx
+++ b/Source/cmLocalVisualStudio7Generator.cxx
@@ -198,7 +198,7 @@
   // Intel Fortran always uses VS9 format ".vfproj" files.
   cmGlobalVisualStudioGenerator::VSVersion realVersion = gg->GetVersion();
   if (this->FortranProject &&
-      gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS11) {
+      gg->GetVersion() >= cmGlobalVisualStudioGenerator::VSVersion::VS12) {
     gg->SetVersion(cmGlobalVisualStudioGenerator::VSVersion::VS9);
   }
 
@@ -283,7 +283,7 @@
     file->ResolveFullPath();
     return file;
   }
-  cmSystemTools::Error("Error adding rule for " + makefileIn);
+  cmSystemTools::Error(cmStrCat("Error adding rule for ", makefileIn));
   return nullptr;
 }
 
@@ -673,8 +673,8 @@
                             : target->GetLinkerLanguage(configName));
     if (linkLanguage.empty()) {
       cmSystemTools::Error(
-        "CMake can not determine linker language for target: " +
-        target->GetName());
+        cmStrCat("CMake can not determine linker language for target: ",
+                 target->GetName()));
       return;
     }
     langForClCompile = linkLanguage;
@@ -961,7 +961,7 @@
 {
   std::string configTypeUpper = cmSystemTools::UpperCase(configName);
   std::string extraLinkOptionsBuildTypeDef =
-    rootLinkerFlags + "_" + configTypeUpper;
+    cmStrCat(rootLinkerFlags, '_', configTypeUpper);
 
   const std::string& extraLinkOptionsBuildType =
     this->Makefile->GetRequiredDefinition(extraLinkOptionsBuildTypeDef);
@@ -978,31 +978,31 @@
   std::string temp;
   std::string extraLinkOptions;
   if (target->GetType() == cmStateEnums::EXECUTABLE) {
-    extraLinkOptions =
-      this->Makefile->GetRequiredDefinition("CMAKE_EXE_LINKER_FLAGS") + " " +
-      GetBuildTypeLinkerFlags("CMAKE_EXE_LINKER_FLAGS", configName);
+    extraLinkOptions = cmStrCat(
+      this->Makefile->GetRequiredDefinition("CMAKE_EXE_LINKER_FLAGS"), ' ',
+      GetBuildTypeLinkerFlags("CMAKE_EXE_LINKER_FLAGS", configName));
   }
   if (target->GetType() == cmStateEnums::SHARED_LIBRARY) {
-    extraLinkOptions =
-      this->Makefile->GetRequiredDefinition("CMAKE_SHARED_LINKER_FLAGS") +
-      " " + GetBuildTypeLinkerFlags("CMAKE_SHARED_LINKER_FLAGS", configName);
+    extraLinkOptions = cmStrCat(
+      this->Makefile->GetRequiredDefinition("CMAKE_SHARED_LINKER_FLAGS"), ' ',
+      GetBuildTypeLinkerFlags("CMAKE_SHARED_LINKER_FLAGS", configName));
   }
   if (target->GetType() == cmStateEnums::MODULE_LIBRARY) {
-    extraLinkOptions =
-      this->Makefile->GetRequiredDefinition("CMAKE_MODULE_LINKER_FLAGS") +
-      " " + GetBuildTypeLinkerFlags("CMAKE_MODULE_LINKER_FLAGS", configName);
+    extraLinkOptions = cmStrCat(
+      this->Makefile->GetRequiredDefinition("CMAKE_MODULE_LINKER_FLAGS"), ' ',
+      GetBuildTypeLinkerFlags("CMAKE_MODULE_LINKER_FLAGS", configName));
   }
 
   cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS");
   if (targetLinkFlags) {
-    extraLinkOptions += " ";
+    extraLinkOptions += ' ';
     extraLinkOptions += *targetLinkFlags;
   }
   std::string configTypeUpper = cmSystemTools::UpperCase(configName);
   std::string linkFlagsConfig = cmStrCat("LINK_FLAGS_", configTypeUpper);
   targetLinkFlags = target->GetProperty(linkFlagsConfig);
   if (targetLinkFlags) {
-    extraLinkOptions += " ";
+    extraLinkOptions += ' ';
     extraLinkOptions += *targetLinkFlags;
   }
 
@@ -1285,7 +1285,8 @@
          << "\"/>\n";
 
     if (dir) {
-      std::string const exe = *dir + "\\" + target->GetFullName(config);
+      std::string const exe =
+        cmStrCat(*dir, '\\', target->GetFullName(config));
 
       fout << "\t\t\t<DebuggerTool\n"
               "\t\t\t\tRemoteExecutable=\""
@@ -1317,7 +1318,8 @@
       fout << (lib.HasFeature() ? lib.GetFormattedItem(rel).Value : rel)
            << " ";
     } else if (!lib.Target ||
-               lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+               (lib.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
+                lib.Target->GetType() != cmStateEnums::OBJECT_LIBRARY)) {
       fout << lib.Value.Value << " ";
     }
   }
@@ -1369,8 +1371,9 @@
     // First search a configuration-specific subdirectory and then the
     // original directory.
     fout << comma
-         << this->ConvertToXMLOutputPath(dir + "/$(ConfigurationName)") << ","
-         << this->ConvertToXMLOutputPath(dir);
+         << this->ConvertToXMLOutputPath(
+              cmStrCat(dir, "/$(ConfigurationName)"))
+         << ',' << this->ConvertToXMLOutputPath(dir);
     comma = ",";
   }
 }
@@ -1550,11 +1553,11 @@
       switch (cmOutputConverter::GetFortranFormat(
         sf.GetSafeProperty("Fortran_FORMAT"))) {
         case cmOutputConverter::FortranFormatFixed:
-          fc.CompileFlags = "-fixed " + fc.CompileFlags;
+          fc.CompileFlags = cmStrCat("-fixed ", fc.CompileFlags);
           needfc = true;
           break;
         case cmOutputConverter::FortranFormatFree:
-          fc.CompileFlags = "-free " + fc.CompileFlags;
+          fc.CompileFlags = cmStrCat("-free ", fc.CompileFlags);
           needfc = true;
           break;
         default:
diff --git a/Source/cmLocalVisualStudioGenerator.cxx b/Source/cmLocalVisualStudioGenerator.cxx
index 58d46f1..34b8ae3 100644
--- a/Source/cmLocalVisualStudioGenerator.cxx
+++ b/Source/cmLocalVisualStudioGenerator.cxx
@@ -5,6 +5,7 @@
 #include <utility>
 
 #include <cm/memory>
+#include <cmext/string_view>
 
 #include "windows.h"
 
@@ -204,7 +205,7 @@
     std::string suffix;
     if (cmd.size() > 4) {
       suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4));
-      if (suffix == ".bat" || suffix == ".cmd") {
+      if (suffix == ".bat"_s || suffix == ".cmd"_s) {
         script += "call ";
       }
     }
diff --git a/Source/cmLocalXCodeGenerator.cxx b/Source/cmLocalXCodeGenerator.cxx
index 759ee7b..9646e66 100644
--- a/Source/cmLocalXCodeGenerator.cxx
+++ b/Source/cmLocalXCodeGenerator.cxx
@@ -31,7 +31,7 @@
   cmGeneratorTarget const*) const
 {
   // No per-target directory for this generator (yet).
-  return "";
+  return std::string{};
 }
 
 void cmLocalXCodeGenerator::AppendFlagEscape(std::string& flags,
@@ -83,6 +83,12 @@
       case cmMakefile::AppleSDK::WatchSimulator:
         platformName = "watchsimulator";
         break;
+      case cmMakefile::AppleSDK::XROS:
+        platformName = "xros";
+        break;
+      case cmMakefile::AppleSDK::XRSimulator:
+        platformName = "xrsimulator";
+        break;
       case cmMakefile::AppleSDK::MacOS:
         break;
     }
diff --git a/Source/cmMachO.cxx b/Source/cmMachO.cxx
index 4fcaedf..91a7b84 100644
--- a/Source/cmMachO.cxx
+++ b/Source/cmMachO.cxx
@@ -69,7 +69,7 @@
     return true;
   }
   return static_cast<bool>(
-    fin.read(reinterpret_cast<char*>(&v[0]), sizeof(T) * v.size()));
+    fin.read(reinterpret_cast<char*>(v.data()), sizeof(T) * v.size()));
 }
 }
 
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 47ad749..3d7cd8b 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -12,6 +12,7 @@
 
 #include "cmExecutionStatus.h"
 #include "cmFunctionBlocker.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmPolicies.h"
@@ -66,8 +67,9 @@
   std::string argcDef = std::to_string(expandedArgs.size());
 
   auto eit = expandedArgs.begin() + (this->Args.size() - 1);
-  std::string expandedArgn = cmJoin(cmMakeRange(eit, expandedArgs.end()), ";");
-  std::string expandedArgv = cmJoin(expandedArgs, ";");
+  std::string expandedArgn =
+    cmList::to_string(cmMakeRange(eit, expandedArgs.end()));
+  std::string expandedArgv = cmList::to_string(expandedArgs);
   std::vector<std::string> variables;
   variables.reserve(this->Args.size() - 1);
   for (unsigned int j = 1; j < this->Args.size(); ++j) {
diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx
index 0af0ed0..80f8a77 100644
--- a/Source/cmMakefile.cxx
+++ b/Source/cmMakefile.cxx
@@ -32,6 +32,7 @@
 #include "cmCommandArgumentParserHelper.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
+#include "cmCustomCommandTypes.h"
 #include "cmExecutionStatus.h"
 #include "cmExpandedCommandArgument.h" // IWYU pragma: keep
 #include "cmExportBuildFileGenerator.h"
@@ -515,7 +516,7 @@
         if (!hadNestedError) {
           // The command invocation requested that we report an error.
           std::string const error =
-            std::string(lff.OriginalName()) + " " + status.GetError();
+            cmStrCat(lff.OriginalName(), ' ', status.GetError());
           this->IssueMessage(MessageType::FATAL_ERROR, error);
         }
         result = false;
@@ -1756,7 +1757,7 @@
     bool hasVersion = false;
     // search for the right policy command
     for (cmListFileFunction const& func : listFile.Functions) {
-      if (func.LowerCaseName() == "cmake_minimum_required") {
+      if (func.LowerCaseName() == "cmake_minimum_required"_s) {
         hasVersion = true;
         break;
       }
@@ -1803,7 +1804,7 @@
     bool hasProject = false;
     // search for a project command
     for (cmListFileFunction const& func : listFile.Functions) {
-      if (func.LowerCaseName() == "project") {
+      if (func.LowerCaseName() == "project"_s) {
         hasProject = true;
         break;
       }
@@ -1860,7 +1861,8 @@
     cmSystemTools::Message(msg);
   }
 
-  std::string const currentStartFile = currentStart + "/CMakeLists.txt";
+  std::string const currentStartFile =
+    cmStrCat(currentStart, "/CMakeLists.txt");
   if (!cmSystemTools::FileExists(currentStartFile, true)) {
     // The file is missing.  Check policy CMP0014.
     std::ostringstream e;
@@ -1980,7 +1982,7 @@
     return;
   }
 
-  std::string entryString = cmJoin(incs, ";");
+  std::string entryString = cmList::to_string(incs);
   if (before) {
     this->StateSnapshot.GetDirectory().PrependIncludeDirectoriesEntry(
       BT<std::string>(entryString, this->Backtrace));
@@ -2149,11 +2151,11 @@
     for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) {
       std::string libraryName = *j;
       cmTargetLinkLibraryType libType = GENERAL_LibraryType;
-      if (libraryName == "optimized") {
+      if (libraryName == "optimized"_s) {
         libType = OPTIMIZED_LibraryType;
         ++j;
         libraryName = *j;
-      } else if (libraryName == "debug") {
+      } else if (libraryName == "debug"_s) {
         libType = DEBUG_LibraryType;
         ++j;
         libraryName = *j;
@@ -2520,7 +2522,7 @@
 bool cmMakefile::PlatformIs32Bit() const
 {
   if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) {
-    if (*plat_abi == "ELF X32") {
+    if (*plat_abi == "ELF X32"_s) {
       return false;
     }
   }
@@ -2541,7 +2543,7 @@
 bool cmMakefile::PlatformIsx32() const
 {
   if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) {
-    if (*plat_abi == "ELF X32") {
+    if (*plat_abi == "ELF X32"_s) {
       return true;
     }
   }
@@ -2565,11 +2567,13 @@
     { "iphonesimulator", AppleSDK::IPhoneSimulator },
     { "watchos", AppleSDK::WatchOS },
     { "watchsimulator", AppleSDK::WatchSimulator },
+    { "xros", AppleSDK::XROS },
+    { "xrsimulator", AppleSDK::XRSimulator },
   };
 
   for (auto const& entry : sdkDatabase) {
     if (cmHasPrefix(sdkRoot, entry.name) ||
-        sdkRoot.find(std::string("/") + entry.name) != std::string::npos) {
+        sdkRoot.find(cmStrCat('/', entry.name)) != std::string::npos) {
       return entry.sdk;
     }
   }
@@ -2582,6 +2586,17 @@
   return this->GetAppleSDKType() != AppleSDK::MacOS;
 }
 
+bool cmMakefile::PlatformIsAppleSimulator() const
+{
+  return std::set<AppleSDK>{
+    AppleSDK::AppleTVSimulator,
+    AppleSDK::IPhoneSimulator,
+    AppleSDK::WatchSimulator,
+    AppleSDK::XRSimulator,
+  }
+    .count(this->GetAppleSDKType());
+}
+
 bool cmMakefile::PlatformSupportsAppleTextStubs() const
 {
   return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI");
@@ -3004,12 +3019,11 @@
 {
   cm::optional<std::string> ids;
   if (this->Defer) {
-    ids = cmJoin(
+    ids = cmList::to_string(
       cmMakeRange(this->Defer->Commands)
         .filter([](DeferCommand const& dc) -> bool { return !dc.Id.empty(); })
         .transform(
-          [](DeferCommand const& dc) -> std::string const& { return dc.Id; }),
-      ";");
+          [](DeferCommand const& dc) -> std::string const& { return dc.Id; }));
   }
   return ids;
 }
@@ -3152,15 +3166,15 @@
           char nextc = *next;
           if (nextc == 't') {
             result.append(last, in - last);
-            result.append("\t");
+            result.push_back('\t');
             last = next + 1;
           } else if (nextc == 'n') {
             result.append(last, in - last);
-            result.append("\n");
+            result.push_back('\n');
             last = next + 1;
           } else if (nextc == 'r') {
             result.append(last, in - last);
-            result.append("\r");
+            result.push_back('\r');
             last = next + 1;
           } else if (nextc == ';' && openstack.empty()) {
             // Handled in ExpandListArgument; pass the backslash literally.
@@ -3237,9 +3251,9 @@
           errorstr += "Invalid character (\'";
           errorstr += inc;
           result.append(last, in - last);
-          errorstr += "\') in a variable name: "
-                      "'" +
-            result.substr(openstack.back().loc) + "'";
+          errorstr += cmStrCat("\') in a variable name: "
+                               "'",
+                               result.substr(openstack.back().loc), '\'');
           mtype = MessageType::FATAL_ERROR;
           error = true;
         }
@@ -3646,13 +3660,13 @@
       }
     }
     if (!duplicate_languages.empty()) {
-      auto quantity = duplicate_languages.size() == 1 ? std::string(" has")
-                                                      : std::string("s have");
-      this->IssueMessage(MessageType::AUTHOR_WARNING,
-                         "Languages to be enabled may not be specified more "
-                         "than once at the same time. The following language" +
-                           quantity + " been specified multiple times: " +
-                           cmJoin(duplicate_languages, ", "));
+      auto quantity = duplicate_languages.size() == 1 ? " has"_s : "s have"_s;
+      this->IssueMessage(
+        MessageType::AUTHOR_WARNING,
+        cmStrCat("Languages to be enabled may not be specified more "
+                 "than once at the same time. The following language",
+                 quantity, " been specified multiple times: ",
+                 cmJoin(duplicate_languages, ", ")));
     }
   }
 
@@ -3663,7 +3677,7 @@
   std::vector<std::string> languages_for_RC;
   languages_without_RC.reserve(unique_languages.size());
   for (std::string const& language : unique_languages) {
-    if (language == "RC") {
+    if (language == "RC"_s) {
       languages_for_RC.push_back(language);
     } else {
       languages_without_RC.push_back(language);
@@ -3697,8 +3711,9 @@
   cmWorkingDirectory workdir(bindir);
   if (workdir.Failed()) {
     this->IssueMessage(MessageType::FATAL_ERROR,
-                       "Failed to set working directory to " + bindir + " : " +
-                         std::strerror(workdir.GetLastResult()));
+                       cmStrCat("Failed to set working directory to ", bindir,
+                                " : ",
+                                std::strerror(workdir.GetLastResult())));
     cmSystemTools::SetFatalErrorOccurred();
     this->IsSourceFileTryCompile = false;
     return 1;
@@ -3926,7 +3941,7 @@
 
   if (!moduleInCMakeModulePath.empty() && !moduleInCMakeRoot.empty()) {
     cmValue currentFile = this->GetDefinition("CMAKE_CURRENT_LIST_FILE");
-    std::string mods = cmSystemTools::GetCMakeRoot() + "/Modules/";
+    std::string mods = cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/");
     if (currentFile && cmSystemTools::IsSubDirectory(*currentFile, mods)) {
       switch (this->GetPolicyStatus(cmPolicies::CMP0017)) {
         case cmPolicies::WARN: {
@@ -3985,8 +4000,9 @@
       cmValue def = this->GetDefinition(this->cmDefineRegex.match(2));
       if (!cmIsOff(def)) {
         const std::string indentation = this->cmDefineRegex.match(1);
-        cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine",
-                                     "#" + indentation + "define");
+        cmSystemTools::ReplaceString(line,
+                                     cmStrCat("#", indentation, "cmakedefine"),
+                                     cmStrCat("#", indentation, "define"));
         output += line;
       } else {
         output += "/* #undef ";
@@ -3996,8 +4012,9 @@
     } else if (this->cmDefine01Regex.find(line)) {
       const std::string indentation = this->cmDefine01Regex.match(1);
       cmValue def = this->GetDefinition(this->cmDefine01Regex.match(2));
-      cmSystemTools::ReplaceString(line, "#" + indentation + "cmakedefine01",
-                                   "#" + indentation + "define");
+      cmSystemTools::ReplaceString(line,
+                                   cmStrCat("#", indentation, "cmakedefine01"),
+                                   cmStrCat("#", indentation, "define"));
       output += line;
       if (!cmIsOff(def)) {
         output += " 1";
@@ -4035,12 +4052,12 @@
 {
   int res = 1;
   if (!this->CanIWriteThisFile(outfile)) {
-    cmSystemTools::Error("Attempt to write file: " + outfile +
-                         " into a source directory.");
+    cmSystemTools::Error(cmStrCat("Attempt to write file: ", outfile,
+                                  " into a source directory."));
     return 0;
   }
   if (!cmSystemTools::FileExists(infile)) {
-    cmSystemTools::Error("File " + infile + " does not exist.");
+    cmSystemTools::Error(cmStrCat("File ", infile, " does not exist."));
     return 0;
   }
   std::string soutfile = outfile;
@@ -4153,14 +4170,14 @@
 {
   // Check for computed properties.
   static std::string output;
-  if (prop == "TESTS") {
+  if (prop == "TESTS"_s) {
     std::vector<std::string> keys;
     // get list of keys
     const auto* t = this;
     std::transform(
       t->Tests.begin(), t->Tests.end(), std::back_inserter(keys),
       [](decltype(t->Tests)::value_type const& pair) { return pair.first; });
-    output = cmJoin(keys, ";");
+    output = cmList::to_string(keys);
     return cmValue(output);
   }
 
@@ -4613,7 +4630,7 @@
   }
 
   // Deprecate old policies.
-  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0114 &&
+  if (status == cmPolicies::OLD && id <= cmPolicies::CMP0120 &&
       !(this->GetCMakeInstance()->GetIsInTryCompile() &&
         (
           // Policies set by cmCoreTryCompile::TryCompileCode.
diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h
index 7005942..24daa72 100644
--- a/Source/cmMakefile.h
+++ b/Source/cmMakefile.h
@@ -25,9 +25,9 @@
 
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
-#include "cmCustomCommandTypes.h"
+#include "cmFunctionBlocker.h"
 #include "cmListFileCache.h"
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 #include "cmNewLineStyle.h"
 #include "cmPolicies.h"
 #include "cmSourceFileLocationKind.h"
@@ -43,12 +43,14 @@
 #  include "cmSourceGroup.h"
 #endif
 
+enum class cmCustomCommandType;
+enum class cmObjectLibraryCommands;
+
 class cmCompiledGeneratorExpression;
 class cmCustomCommandLines;
 class cmExecutionStatus;
 class cmExpandedCommandArgument;
 class cmExportBuildFileGenerator;
-class cmFunctionBlocker;
 class cmGeneratorExpressionEvaluationFile;
 class cmGlobalGenerator;
 class cmInstallGenerator;
@@ -561,6 +563,8 @@
     AppleTVSimulator,
     WatchOS,
     WatchSimulator,
+    XROS,
+    XRSimulator,
   };
 
   /** What SDK type points CMAKE_OSX_SYSROOT to? */
@@ -569,6 +573,9 @@
   /** Return whether the target platform is Apple iOS.  */
   bool PlatformIsAppleEmbedded() const;
 
+  /** Return whether the target platform is an Apple simulator.  */
+  bool PlatformIsAppleSimulator() const;
+
   /** Return whether the target platform supports generation of text base stubs
      (.tbd file) describing exports (Apple specific). */
   bool PlatformSupportsAppleTextStubs() const;
diff --git a/Source/cmMakefileProfilingData.cxx b/Source/cmMakefileProfilingData.cxx
index e903ae1..9387582 100644
--- a/Source/cmMakefileProfilingData.cxx
+++ b/Source/cmMakefileProfilingData.cxx
@@ -4,7 +4,6 @@
 
 #include <chrono>
 #include <stdexcept>
-#include <type_traits>
 #include <utility>
 
 #include <cm3p/json/value.h>
diff --git a/Source/cmMakefileTargetGenerator.cxx b/Source/cmMakefileTargetGenerator.cxx
index 5f27856..0c2a719 100644
--- a/Source/cmMakefileTargetGenerator.cxx
+++ b/Source/cmMakefileTargetGenerator.cxx
@@ -17,7 +17,7 @@
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
-#include "cm_codecvt.hxx"
+#include "cm_codecvt_Encoding.hxx"
 
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommand.h"
@@ -126,8 +126,11 @@
   std::vector<std::string> linkOpts;
   this->GeneratorTarget->GetLinkOptions(linkOpts, this->GetConfigName(),
                                         linkLanguage);
+  this->LocalGenerator->SetLinkScriptShell(
+    this->GlobalGenerator->GetUseLinkScript());
   // LINK_OPTIONS are escaped.
   this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts);
+  this->LocalGenerator->SetLinkScriptShell(false);
 }
 
 void cmMakefileTargetGenerator::GetTargetLinkFlags(
@@ -144,8 +147,11 @@
   std::vector<std::string> opts;
   this->GeneratorTarget->GetLinkOptions(opts, this->GetConfigName(),
                                         linkLanguage);
+  this->LocalGenerator->SetLinkScriptShell(
+    this->GlobalGenerator->GetUseLinkScript());
   // LINK_OPTIONS are escaped.
   this->LocalGenerator->AppendCompileOptions(flags, opts);
+  this->LocalGenerator->SetLinkScriptShell(false);
 
   this->LocalGenerator->AppendPositionIndependentLinkerFlags(
     flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
@@ -198,14 +204,6 @@
 {
   this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName());
 
-  if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      cmStrCat("The \"", this->GeneratorTarget->GetName(),
-               "\" target contains C++ module sources which are not supported "
-               "by the generator"));
-  }
-
   // -- Write the custom commands for this target
 
   // Evaluates generator expressions and expands prop_value
@@ -2129,12 +2127,12 @@
   // FIXME: Find a better way to determine the response file encoding,
   // perhaps using tool-specific platform information variables.
   // For now, use the makefile encoding as a heuristic.
-  codecvt::Encoding responseEncoding =
+  codecvt_Encoding responseEncoding =
     this->GlobalGenerator->GetMakefileEncoding();
   // Non-MSVC tooling doesn't understand BOM encoded files.
-  if (responseEncoding == codecvt::UTF8_WITH_BOM &&
+  if (responseEncoding == codecvt_Encoding::UTF8_WITH_BOM &&
       (language == "CUDA" || !this->Makefile->IsOn("MSVC"))) {
-    responseEncoding = codecvt::UTF8;
+    responseEncoding = codecvt_Encoding::UTF8;
   }
 
   // Create the response file.
diff --git a/Source/cmMessenger.cxx b/Source/cmMessenger.cxx
index 4e975d1..b4ea71c 100644
--- a/Source/cmMessenger.cxx
+++ b/Source/cmMessenger.cxx
@@ -4,6 +4,7 @@
 
 #include "cmDocumentationFormatter.h"
 #include "cmMessageMetadata.h"
+#include "cmMessageType.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
diff --git a/Source/cmMessenger.h b/Source/cmMessenger.h
index bdefb00..d9462d4 100644
--- a/Source/cmMessenger.h
+++ b/Source/cmMessenger.h
@@ -11,7 +11,7 @@
 #include <cm/optional>
 
 #include "cmListFileCache.h"
-#include "cmMessageType.h"
+#include "cmMessageType.h" // IWYU pragma: keep
 
 #ifdef CMake_ENABLE_DEBUGGER
 namespace cmDebugger {
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 063ca6b..48c30b6 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <cassert>
 #include <iterator>
-#include <map>
 #include <set>
 #include <sstream>
 #include <unordered_set>
@@ -63,12 +62,15 @@
 
 void cmNinjaNormalTargetGenerator::Generate(const std::string& config)
 {
-  std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
-  if (this->TargetLinkLanguage(config).empty()) {
-    cmSystemTools::Error(
-      cmStrCat("CMake can not determine linker language for target: ",
-               this->GetGeneratorTarget()->GetName()));
-    return;
+  if (this->GetGeneratorTarget()->GetType() !=
+      cmStateEnums::INTERFACE_LIBRARY) {
+    std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
+    if (this->TargetLinkLanguage(config).empty()) {
+      cmSystemTools::Error(
+        cmStrCat("CMake can not determine linker language for target: ",
+                 this->GetGeneratorTarget()->GetName()));
+      return;
+    }
   }
 
   // Write the rules for each language.
@@ -88,6 +90,34 @@
 
   if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
     this->WriteObjectLibStatement(config);
+  } else if (this->GetGeneratorTarget()->GetType() ==
+             cmStateEnums::INTERFACE_LIBRARY) {
+    bool haveCxxModuleSources = false;
+    if (this->GetGeneratorTarget()->HaveCxx20ModuleSources()) {
+      haveCxxModuleSources = true;
+    }
+
+    if (!haveCxxModuleSources) {
+      cmSystemTools::Error(cmStrCat(
+        "Ninja does not support INTERFACE libraries without C++ module "
+        "sources as a normal target: ",
+        this->GetGeneratorTarget()->GetName()));
+      return;
+    }
+
+    firstForConfig = true;
+    for (auto const& fileConfig : this->GetConfigNames()) {
+      if (!this->GetGlobalGenerator()
+             ->GetCrossConfigs(fileConfig)
+             .count(config)) {
+        continue;
+      }
+      if (haveCxxModuleSources) {
+        this->WriteCxxModuleLibraryStatement(config, fileConfig,
+                                             firstForConfig);
+      }
+      firstForConfig = false;
+    }
   } else {
     firstForConfig = true;
     for (auto const& fileConfig : this->GetConfigNames()) {
@@ -124,12 +154,26 @@
 #endif
 
   // Write rules for languages compiled in this target.
-  std::set<std::string> languages;
-  std::vector<cmSourceFile const*> sourceFiles;
-  this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
-  if (this->HaveRequiredLanguages(sourceFiles, languages)) {
-    for (std::string const& language : languages) {
-      this->WriteLanguageRules(language, config);
+  {
+    std::set<std::string> languages;
+    std::vector<cmSourceFile const*> sourceFiles;
+    this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
+    if (this->HaveRequiredLanguages(sourceFiles, languages)) {
+      for (std::string const& language : languages) {
+        this->WriteLanguageRules(language, config);
+      }
+    }
+  }
+
+  // Write rules for languages in BMI-only rules.
+  {
+    std::set<std::string> languages;
+    std::vector<cmSourceFile const*> sourceFiles;
+    this->GetGeneratorTarget()->GetCxxModuleSources(sourceFiles, config);
+    if (this->HaveRequiredLanguages(sourceFiles, languages)) {
+      for (std::string const& language : languages) {
+        this->WriteLanguageRules(language, config);
+      }
     }
   }
 }
@@ -952,8 +996,6 @@
 
   this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, vars);
 
-  vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
-
   vars["MANIFESTS"] = this->GetManifests(config);
 
   vars["LINK_PATH"] = frameworkPath + linkPath;
@@ -1271,8 +1313,6 @@
     vars["LINK_FLAGS"], this->GetGeneratorTarget(),
     this->TargetLinkLanguage(config));
 
-  vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
-
   vars["MANIFESTS"] = this->GetManifests(config);
   vars["AIX_EXPORTS"] = this->GetAIXExports(config);
 
@@ -1474,9 +1514,11 @@
     gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact);
 
   // Add order-only dependencies on versioning symlinks of shared libs we link.
-  if (!this->GeneratorTarget->IsDLLPlatform()) {
-    if (cmComputeLinkInformation* cli =
-          this->GeneratorTarget->GetLinkInformation(config)) {
+  // If our target is not producing a runtime binary, it doesn't need the
+  // symlinks (anything that links to the target might, but that consumer will
+  // get its own order-only dependency).
+  if (!gt->IsDLLPlatform() && gt->IsRuntimeBinary()) {
+    if (cmComputeLinkInformation* cli = gt->GetLinkInformation(config)) {
       for (auto const& item : cli->GetItems()) {
         if (item.Target &&
             item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
@@ -1640,6 +1682,34 @@
     this->GetTargetName(), this->GetGeneratorTarget(), config);
 }
 
+void cmNinjaNormalTargetGenerator::WriteCxxModuleLibraryStatement(
+  const std::string& config, const std::string& /*fileConfig*/,
+  bool firstForConfig)
+{
+  // TODO: How to use `fileConfig` properly?
+
+  // Write a phony output that depends on the scanning output.
+  {
+    cmNinjaBuild build("phony");
+    build.Comment =
+      cmStrCat("Imported C++ module library ", this->GetTargetName());
+    this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
+                                                   build.Outputs, config);
+    if (firstForConfig) {
+      this->GetLocalGenerator()->AppendTargetOutputs(
+        this->GetGeneratorTarget(),
+        this->GetGlobalGenerator()->GetByproductsForCleanTarget(config),
+        config);
+    }
+    build.ExplicitDeps.emplace_back(this->GetDyndepFilePath("CXX", config));
+    this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
+  }
+
+  // Add aliases for the target name.
+  this->GetGlobalGenerator()->AddTargetAlias(
+    this->GetTargetName(), this->GetGeneratorTarget(), config);
+}
+
 cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames(
   const std::string& config) const
 {
diff --git a/Source/cmNinjaNormalTargetGenerator.h b/Source/cmNinjaNormalTargetGenerator.h
index 187ea46..3ef0230 100644
--- a/Source/cmNinjaNormalTargetGenerator.h
+++ b/Source/cmNinjaNormalTargetGenerator.h
@@ -49,6 +49,9 @@
                                       const std::string& output);
 
   void WriteObjectLibStatement(const std::string& config);
+  void WriteCxxModuleLibraryStatement(const std::string& config,
+                                      const std::string& fileConfig,
+                                      bool firstForConfig);
 
   std::vector<std::string> ComputeLinkCmd(const std::string& config);
   std::vector<std::string> ComputeDeviceLinkCmd();
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index d712d71..0bda945 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -9,7 +9,6 @@
 #include <iterator>
 #include <map>
 #include <ostream>
-#include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
@@ -29,6 +28,7 @@
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
+#include "cmGlobalCommonGenerator.h"
 #include "cmGlobalNinjaGenerator.h"
 #include "cmList.h"
 #include "cmLocalGenerator.h"
@@ -38,6 +38,7 @@
 #include "cmNinjaNormalTargetGenerator.h"
 #include "cmNinjaUtilityTargetGenerator.h"
 #include "cmOutputConverter.h"
+#include "cmPolicies.h"
 #include "cmRange.h"
 #include "cmRulePlaceholderExpander.h"
 #include "cmSourceFile.h"
@@ -49,6 +50,8 @@
 #include "cmValue.h"
 #include "cmake.h"
 
+class cmCustomCommand;
+
 std::unique_ptr<cmNinjaTargetGenerator> cmNinjaTargetGenerator::New(
   cmGeneratorTarget* target)
 {
@@ -60,8 +63,13 @@
     case cmStateEnums::OBJECT_LIBRARY:
       return cm::make_unique<cmNinjaNormalTargetGenerator>(target);
 
-    case cmStateEnums::UTILITY:
     case cmStateEnums::INTERFACE_LIBRARY:
+      if (target->HaveCxx20ModuleSources()) {
+        return cm::make_unique<cmNinjaNormalTargetGenerator>(target);
+      }
+      CM_FALLTHROUGH;
+
+    case cmStateEnums::UTILITY:
     case cmStateEnums::GLOBAL_TARGET:
       return cm::make_unique<cmNinjaUtilityTargetGenerator>(target);
 
@@ -162,13 +170,20 @@
     this->GeneratorTarget, config);
 }
 
+std::string cmNinjaTargetGenerator::OrderDependsTargetForTargetPrivate(
+  const std::string& config)
+{
+  return this->GetGlobalGenerator()->OrderDependsTargetForTargetPrivate(
+    this->GeneratorTarget, config);
+}
+
 // TODO: Most of the code is picked up from
 // void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink),
 // void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
 // Refactor it.
 std::string cmNinjaTargetGenerator::ComputeFlagsForObject(
   cmSourceFile const* source, const std::string& language,
-  const std::string& config)
+  const std::string& config, const std::string& objectFileName)
 {
   std::unordered_map<std::string, std::string> pchSources;
   std::vector<std::string> architectures =
@@ -248,6 +263,18 @@
                  "\nin a file set of type \"", fs->GetType(),
                  R"(" but the source is not classified as a "CXX" source.)"));
     }
+
+    if (!this->GeneratorTarget->Target->IsNormal()) {
+      auto flag = this->GetMakefile()->GetSafeDefinition(
+        "CMAKE_CXX_MODULE_BMI_ONLY_FLAG");
+      cmRulePlaceholderExpander::RuleVariables compileObjectVars;
+      compileObjectVars.Object = objectFileName.c_str();
+      auto rulePlaceholderExpander =
+        this->GetLocalGenerator()->CreateRulePlaceholderExpander();
+      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
+                                                   flag, compileObjectVars);
+      this->LocalGenerator->AppendCompileOptions(flags, flag);
+    }
   }
 
   return flags;
@@ -395,6 +422,31 @@
   return path;
 }
 
+std::string cmNinjaTargetGenerator::GetBmiFilePath(
+  cmSourceFile const* source, const std::string& config) const
+{
+  std::string path = this->LocalGenerator->GetHomeRelativeOutputPath();
+  if (!path.empty()) {
+    path += '/';
+  }
+
+  auto& importedConfigInfo = this->Configs.at(config).ImportedCxxModules;
+  if (!importedConfigInfo.Initialized()) {
+    std::string configUpper = cmSystemTools::UpperCase(config);
+    std::string propName = cmStrCat("IMPORTED_CXX_MODULES_", configUpper);
+    auto value = this->GeneratorTarget->GetSafeProperty(propName);
+    importedConfigInfo.Initialize(value);
+  }
+
+  std::string bmiName =
+    importedConfigInfo.BmiNameForSource(source->GetFullPath());
+
+  path += cmStrCat(
+    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
+    this->GetGlobalGenerator()->ConfigDirectory(config), '/', bmiName);
+  return path;
+}
+
 std::string cmNinjaTargetGenerator::GetClangTidyReplacementsFilePath(
   std::string const& directory, cmSourceFile const& source,
   std::string const& config) const
@@ -658,7 +710,7 @@
     }
   }
   std::string const modmapFormatVar =
-    cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FORMAT");
+    cmStrCat("CMAKE_", lang, "_MODULE_MAP_FORMAT");
   std::string const modmapFormat =
     this->Makefile->GetSafeDefinition(modmapFormatVar);
 
@@ -682,7 +734,7 @@
 
   if (withScanning == WithScanning::Yes) {
     const auto& scanDepType = this->GetMakefile()->GetSafeDefinition(
-      cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_DEPFILE_FORMAT"));
+      cmStrCat("CMAKE_", lang, "_SCANDEP_DEPFILE_FORMAT"));
 
     // Rule to scan dependencies of sources that need preprocessing.
     {
@@ -693,7 +745,7 @@
         scanRuleName = this->LanguageScanRule(lang, config);
         ppFileName = "$PREPROCESSED_OUTPUT_FILE";
         std::string const& scanCommand = mf->GetRequiredDefinition(
-          cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_SCANDEP_SOURCE"));
+          cmStrCat("CMAKE_", lang, "_SCANDEP_SOURCE"));
         scanCommands.assign(scanCommand);
         for (auto& i : scanCommands) {
           i = cmStrCat(launcher, i);
@@ -841,8 +893,8 @@
   }
 
   if (withScanning == WithScanning::Yes && !modmapFormat.empty()) {
-    std::string modmapFlags = mf->GetRequiredDefinition(
-      cmStrCat("CMAKE_EXPERIMENTAL_", lang, "_MODULE_MAP_FLAG"));
+    std::string modmapFlags =
+      mf->GetRequiredDefinition(cmStrCat("CMAKE_", lang, "_MODULE_MAP_FLAG"));
     cmSystemTools::ReplaceString(modmapFlags, "<MODULE_MAP_FILE>",
                                  "$DYNDEP_MODULE_MAP_FILE");
     flags += cmStrCat(' ', modmapFlags);
@@ -930,16 +982,15 @@
     << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
     << " target " << this->GetTargetName() << "\n\n";
 
+  std::vector<cmCustomCommand const*> customCommands;
   {
-    std::vector<cmSourceFile const*> customCommands;
-    this->GeneratorTarget->GetCustomCommands(customCommands, config);
-    for (cmSourceFile const* sf : customCommands) {
+    std::vector<cmSourceFile const*> customCommandSources;
+    this->GeneratorTarget->GetCustomCommands(customCommandSources, config);
+    for (cmSourceFile const* sf : customCommandSources) {
       cmCustomCommand const* cc = sf->GetCustomCommand();
       this->GetLocalGenerator()->AddCustomCommandTarget(
         cc, this->GetGeneratorTarget());
-      // Record the custom commands for this target. The container is used
-      // in WriteObjectBuildStatement when called in a loop below.
-      this->Configs[config].CustomCommands.push_back(cc);
+      customCommands.push_back(cc);
     }
   }
   {
@@ -977,6 +1028,45 @@
       cmStrCat("Order-only phony target for ", this->GetTargetName());
     build.Outputs.push_back(this->OrderDependsTargetForTarget(config));
 
+    // Gather order-only dependencies on custom command outputs.
+    std::vector<std::string> ccouts;
+    std::vector<std::string> ccouts_private;
+    bool usePrivateGeneratedSources = false;
+    if (this->GeneratorTarget->Target->HasFileSets()) {
+      switch (this->GetGeneratorTarget()->GetPolicyStatusCMP0154()) {
+        case cmPolicies::WARN:
+        case cmPolicies::OLD:
+          break;
+        case cmPolicies::REQUIRED_ALWAYS:
+        case cmPolicies::REQUIRED_IF_USED:
+        case cmPolicies::NEW:
+          usePrivateGeneratedSources = true;
+          break;
+      }
+    }
+    for (cmCustomCommand const* cc : customCommands) {
+      cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator());
+      const std::vector<std::string>& ccoutputs = ccg.GetOutputs();
+      const std::vector<std::string>& ccbyproducts = ccg.GetByproducts();
+      ccouts.insert(ccouts.end(), ccoutputs.begin(), ccoutputs.end());
+      ccouts.insert(ccouts.end(), ccbyproducts.begin(), ccbyproducts.end());
+      if (usePrivateGeneratedSources) {
+        auto it = ccouts.begin();
+        while (it != ccouts.end()) {
+          cmFileSet const* fileset =
+            this->GeneratorTarget->GetFileSetForSource(
+              config, this->Makefile->GetOrCreateGeneratedSource(*it));
+          if (fileset &&
+              fileset->GetVisibility() != cmFileSetVisibility::Private) {
+            ++it;
+          } else {
+            ccouts_private.push_back(*it);
+            it = ccouts.erase(it);
+          }
+        }
+      }
+    }
+
     cmNinjaDeps& orderOnlyDeps = build.OrderOnlyDeps;
     this->GetLocalGenerator()->AppendTargetDepends(
       this->GeneratorTarget, orderOnlyDeps, config, fileConfig,
@@ -986,17 +1076,8 @@
     cm::append(orderOnlyDeps, this->Configs[config].ExtraFiles);
 
     // Add order-only dependencies on custom command outputs.
-    for (cmCustomCommand const* cc : this->Configs[config].CustomCommands) {
-      cmCustomCommandGenerator ccg(*cc, config, this->GetLocalGenerator());
-      const std::vector<std::string>& ccoutputs = ccg.GetOutputs();
-      const std::vector<std::string>& ccbyproducts = ccg.GetByproducts();
-      std::transform(ccoutputs.begin(), ccoutputs.end(),
-                     std::back_inserter(orderOnlyDeps),
-                     this->MapToNinjaPath());
-      std::transform(ccbyproducts.begin(), ccbyproducts.end(),
-                     std::back_inserter(orderOnlyDeps),
-                     this->MapToNinjaPath());
-    }
+    std::transform(ccouts.begin(), ccouts.end(),
+                   std::back_inserter(orderOnlyDeps), this->MapToNinjaPath());
 
     std::sort(orderOnlyDeps.begin(), orderOnlyDeps.end());
     orderOnlyDeps.erase(
@@ -1017,8 +1098,32 @@
 
     this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
                                            build);
-  }
 
+    // Add order-only dependencies on custom command outputs that are
+    // private to this target.
+    this->HasPrivateGeneratedSources = !ccouts_private.empty();
+    if (this->HasPrivateGeneratedSources) {
+      cmNinjaBuild buildPrivate("phony");
+      cmNinjaDeps& orderOnlyDepsPrivate = buildPrivate.OrderOnlyDeps;
+      orderOnlyDepsPrivate.push_back(
+        this->OrderDependsTargetForTarget(config));
+
+      buildPrivate.Outputs.push_back(
+        this->OrderDependsTargetForTargetPrivate(config));
+
+      std::transform(ccouts_private.begin(), ccouts_private.end(),
+                     std::back_inserter(orderOnlyDepsPrivate),
+                     this->MapToNinjaPath());
+
+      std::sort(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end());
+      orderOnlyDepsPrivate.erase(
+        std::unique(orderOnlyDepsPrivate.begin(), orderOnlyDepsPrivate.end()),
+        orderOnlyDepsPrivate.end());
+
+      this->GetGlobalGenerator()->WriteBuild(
+        this->GetImplFileStream(fileConfig), buildPrivate);
+    }
+  }
   {
     std::vector<cmSourceFile const*> objectSources;
     this->GeneratorTarget->GetObjectSources(objectSources, config);
@@ -1028,6 +1133,40 @@
     }
   }
 
+  {
+    std::vector<cmSourceFile const*> bmiOnlySources;
+    this->GeneratorTarget->GetCxxModuleSources(bmiOnlySources, config);
+
+    for (cmSourceFile const* sf : bmiOnlySources) {
+      this->WriteCxxModuleBmiBuildStatement(sf, config, fileConfig,
+                                            firstForConfig);
+    }
+  }
+
+  // Detect sources in `CXX_MODULES` which are not compiled.
+  {
+    std::vector<cmSourceFile*> sources;
+    this->GeneratorTarget->GetSourceFiles(sources, config);
+    for (cmSourceFile const* sf : sources) {
+      cmFileSet const* fs =
+        this->GeneratorTarget->GetFileSetForSource(config, sf);
+      if (!fs) {
+        continue;
+      }
+      if (fs->GetType() != "CXX_MODULES"_s) {
+        continue;
+      }
+      if (sf->GetLanguage().empty()) {
+        this->GeneratorTarget->Makefile->IssueMessage(
+          MessageType::FATAL_ERROR,
+          cmStrCat("Target \"", this->GeneratorTarget->GetName(),
+                   "\" has source file\n  ", sf->GetFullPath(),
+                   "\nin a \"FILE_SET TYPE CXX_MODULES\" but it is not "
+                   "scheduled for compilation."));
+      }
+    }
+  }
+
   for (auto const& langScanningFiles : this->Configs[config].ScanningInfo) {
     std::string const& language = langScanningFiles.first;
     std::vector<ScanningFiles> const& scanningFiles = langScanningFiles.second;
@@ -1052,7 +1191,8 @@
 
     for (std::string const& l :
          this->GetLinkedTargetDirectories(language, config)) {
-      build.ImplicitDeps.push_back(cmStrCat(l, '/', language, "Modules.json"));
+      build.ImplicitDeps.emplace_back(
+        cmStrCat(l, '/', language, "Modules.json"));
     }
 
     this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
@@ -1149,22 +1289,22 @@
   scanBuild.Variables["OBJ_FILE"] = objectFileName;
 
   // Tell dependency scanner where to store dyndep intermediate results.
-  std::string const& ddiFile = cmStrCat(objectFileName, ".ddi");
-  scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFile;
+  std::string ddiFileName = cmStrCat(objectFileName, ".ddi");
+  scanBuild.Variables["DYNDEP_INTERMEDIATE_FILE"] = ddiFileName;
 
   // Outputs of the scan/preprocessor build statement.
   if (compilePP) {
     scanBuild.Outputs.push_back(ppFileName);
-    scanBuild.ImplicitOuts.push_back(ddiFile);
+    scanBuild.ImplicitOuts.push_back(ddiFileName);
   } else {
-    scanBuild.Outputs.push_back(ddiFile);
+    scanBuild.Outputs.push_back(ddiFileName);
     scanBuild.Variables["PREPROCESSED_OUTPUT_FILE"] = ppFileName;
     if (!compilationPreprocesses) {
       // Compilation does not preprocess and we are not compiling an
       // already-preprocessed source.  Make compilation depend on the scan
       // results to honor implicit dependencies discovered during scanning
       // (such as Fortran INCLUDE directives).
-      objBuild.ImplicitDeps.emplace_back(ddiFile);
+      objBuild.ImplicitDeps.emplace_back(ddiFileName);
     }
   }
 
@@ -1214,7 +1354,8 @@
   cmNinjaBuild objBuild(this->LanguageCompilerRule(
     language, config, needDyndep ? WithScanning::Yes : WithScanning::No));
   cmNinjaVars& vars = objBuild.Variables;
-  vars["FLAGS"] = this->ComputeFlagsForObject(source, language, config);
+  vars["FLAGS"] =
+    this->ComputeFlagsForObject(source, language, config, objectFileName);
   vars["DEFINES"] = this->ComputeDefines(source, language, config);
   vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
 
@@ -1333,7 +1474,13 @@
                    this->MapToNinjaPath());
   }
 
-  objBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
+  if (this->HasPrivateGeneratedSources) {
+    objBuild.OrderOnlyDeps.push_back(
+      this->OrderDependsTargetForTargetPrivate(config));
+  } else {
+    objBuild.OrderOnlyDeps.push_back(
+      this->OrderDependsTargetForTarget(config));
+  }
 
   // If the source file is GENERATED and does not have a custom command
   // (either attached to this source file or another one), assume that one of
@@ -1354,7 +1501,7 @@
   std::string modmapFormat;
   if (needDyndep) {
     std::string const modmapFormatVar =
-      cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT");
+      cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
     modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
   }
 
@@ -1545,6 +1692,155 @@
   }
 }
 
+void cmNinjaTargetGenerator::WriteCxxModuleBmiBuildStatement(
+  cmSourceFile const* source, const std::string& config,
+  const std::string& fileConfig, bool firstForConfig)
+{
+  std::string const language = source->GetLanguage();
+  if (language != "CXX"_s) {
+    this->GetMakefile()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Source file '", source->GetFullPath(), "' of target '",
+               this->GetTargetName(), "' is a '", language,
+               "' source but must be 'CXX' in order to have a BMI build "
+               "statement generated."));
+    return;
+  }
+
+  std::string const sourceFilePath = this->GetCompiledSourceNinjaPath(source);
+  std::string const bmiDir = this->ConvertToNinjaPath(
+    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
+             this->GetGlobalGenerator()->ConfigDirectory(config)));
+  std::string const bmiFileName =
+    this->ConvertToNinjaPath(this->GetBmiFilePath(source, config));
+  std::string const bmiFileDir = cmSystemTools::GetFilenamePath(bmiFileName);
+
+  int const commandLineLengthLimit = this->ForceResponseFile() ? -1 : 0;
+
+  cmNinjaBuild bmiBuild(
+    this->LanguageCompilerRule(language, config, WithScanning::Yes));
+  cmNinjaVars& vars = bmiBuild.Variables;
+  vars["FLAGS"] =
+    this->ComputeFlagsForObject(source, language, config, bmiFileName);
+  vars["DEFINES"] = this->ComputeDefines(source, language, config);
+  vars["INCLUDES"] = this->ComputeIncludes(source, language, config);
+
+  if (this->GetMakefile()->GetSafeDefinition(
+        cmStrCat("CMAKE_", language, "_DEPFILE_FORMAT")) != "msvc"_s) {
+    bool replaceExt(false);
+    if (!language.empty()) {
+      std::string repVar =
+        cmStrCat("CMAKE_", language, "_DEPFILE_EXTENSION_REPLACE");
+      replaceExt = this->Makefile->IsOn(repVar);
+    }
+    if (!replaceExt) {
+      // use original code
+      vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
+        cmStrCat(bmiFileName, ".d"), cmOutputConverter::SHELL);
+    } else {
+      // Replace the original source file extension with the
+      // depend file extension.
+      std::string dependFileName = cmStrCat(
+        cmSystemTools::GetFilenameWithoutLastExtension(bmiFileName), ".d");
+      vars["DEP_FILE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
+        cmStrCat(bmiFileDir, '/', dependFileName), cmOutputConverter::SHELL);
+    }
+  }
+
+  std::string d =
+    this->GeneratorTarget->GetClangTidyExportFixesDirectory(language);
+  if (!d.empty()) {
+    this->GlobalCommonGenerator->AddClangTidyExportFixesDir(d);
+    std::string fixesFile =
+      this->GetClangTidyReplacementsFilePath(d, *source, config);
+    this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile);
+    cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(fixesFile));
+    fixesFile = this->ConvertToNinjaPath(fixesFile);
+    vars["CLANG_TIDY_EXPORT_FIXES"] = fixesFile;
+  }
+
+  if (firstForConfig) {
+    this->ExportObjectCompileCommand(
+      language, sourceFilePath, bmiDir, bmiFileName, bmiFileDir, vars["FLAGS"],
+      vars["DEFINES"], vars["INCLUDES"], config);
+  }
+
+  bmiBuild.Outputs.push_back(bmiFileName);
+  bmiBuild.ExplicitDeps.push_back(sourceFilePath);
+
+  std::vector<std::string> depList;
+
+  std::vector<std::string> architectures =
+    this->GeneratorTarget->GetAppleArchs(config, language);
+  if (architectures.empty()) {
+    architectures.emplace_back();
+  }
+
+  bmiBuild.OrderOnlyDeps.push_back(this->OrderDependsTargetForTarget(config));
+
+  // For some cases we scan to dynamically discover dependencies.
+  std::string modmapFormat;
+  if (true) {
+    std::string const modmapFormatVar =
+      cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
+    modmapFormat = this->Makefile->GetSafeDefinition(modmapFormatVar);
+  }
+
+  {
+    bool const compilePPWithDefines = this->CompileWithDefines(language);
+
+    std::string scanRuleName = this->LanguageScanRule(language, config);
+    std::string ppFileName = cmStrCat(bmiFileName, ".ddi.i");
+
+    cmNinjaBuild ppBuild = GetScanBuildStatement(
+      scanRuleName, ppFileName, false, compilePPWithDefines, true, bmiBuild,
+      vars, bmiFileName, this->LocalGenerator);
+
+    ScanningFiles scanningFiles;
+
+    if (firstForConfig) {
+      scanningFiles.ScanningOutput = cmStrCat(bmiFileName, ".ddi");
+    }
+
+    this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
+                               ppBuild.Variables);
+
+    this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
+                                           ppBuild, commandLineLengthLimit);
+
+    std::string const dyndep = this->GetDyndepFilePath(language, config);
+    bmiBuild.OrderOnlyDeps.push_back(dyndep);
+    vars["dyndep"] = dyndep;
+
+    if (!modmapFormat.empty()) {
+      std::string ddModmapFile = cmStrCat(bmiFileName, ".modmap");
+      vars["DYNDEP_MODULE_MAP_FILE"] = ddModmapFile;
+      scanningFiles.ModuleMapFile = std::move(ddModmapFile);
+    }
+
+    if (!scanningFiles.IsEmpty()) {
+      this->Configs[config].ScanningInfo[language].emplace_back(scanningFiles);
+    }
+  }
+
+  this->EnsureParentDirectoryExists(bmiFileName);
+
+  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
+    bmiDir, cmOutputConverter::SHELL);
+  vars["OBJECT_FILE_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
+    bmiFileDir, cmOutputConverter::SHELL);
+
+  this->addPoolNinjaVariable("JOB_POOL_COMPILE", this->GetGeneratorTarget(),
+                             vars);
+
+  this->SetMsvcTargetPdbVariable(vars, config);
+
+  bmiBuild.RspFile = cmStrCat(bmiFileName, ".rsp");
+
+  this->GetGlobalGenerator()->WriteBuild(this->GetImplFileStream(fileConfig),
+                                         bmiBuild, commandLineLengthLimit);
+}
+
 void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
                                                    const std::string& config)
 {
@@ -1605,6 +1901,9 @@
   cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) {
     return this->GetObjectFilePath(sf, cnf);
   };
+  cb.BmiFilePath = [this](cmSourceFile const* sf, std::string const& cnf) {
+    return this->GetBmiFilePath(sf, cnf);
+  };
 
 #if !defined(CMAKE_BOOTSTRAP)
   cmDyndepCollation::AddCollationInformation(tdi, this->GeneratorTarget,
@@ -1694,12 +1993,12 @@
     bool const needDyndep =
       this->GetGeneratorTarget()->NeedDyndep(language, outputConfig);
     std::string const modmapFormatVar =
-      cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FORMAT");
+      cmStrCat("CMAKE_", language, "_MODULE_MAP_FORMAT");
     std::string const modmapFormat =
       this->Makefile->GetSafeDefinition(modmapFormatVar);
     if (needDyndep && !modmapFormat.empty()) {
       std::string modmapFlags = this->GetMakefile()->GetRequiredDefinition(
-        cmStrCat("CMAKE_EXPERIMENTAL_", language, "_MODULE_MAP_FLAG"));
+        cmStrCat("CMAKE_", language, "_MODULE_MAP_FLAG"));
       // XXX(modmap): If changing this path construction, change
       // `cmGlobalNinjaGenerator::WriteDyndep` and
       // `cmNinjaTargetGenerator::WriteObjectBuildStatement` to expect the
diff --git a/Source/cmNinjaTargetGenerator.h b/Source/cmNinjaTargetGenerator.h
index 8c38499..3f56113 100644
--- a/Source/cmNinjaTargetGenerator.h
+++ b/Source/cmNinjaTargetGenerator.h
@@ -15,10 +15,10 @@
 
 #include "cmCommonTargetGenerator.h"
 #include "cmGlobalNinjaGenerator.h"
+#include "cmImportedCxxModuleInfo.h"
 #include "cmNinjaTypes.h"
 #include "cmOSXBundleGenerator.h"
 
-class cmCustomCommand;
 class cmGeneratedFileStream;
 class cmGeneratorTarget;
 class cmLocalNinjaGenerator;
@@ -81,6 +81,7 @@
   bool CompileWithDefines(std::string const& lang) const;
 
   std::string OrderDependsTargetForTarget(const std::string& config);
+  std::string OrderDependsTargetForTargetPrivate(const std::string& config);
 
   std::string ComputeOrderDependsForTarget();
 
@@ -91,7 +92,8 @@
    */
   std::string ComputeFlagsForObject(cmSourceFile const* source,
                                     const std::string& language,
-                                    const std::string& config);
+                                    const std::string& config,
+                                    const std::string& objectFileName);
 
   void AddIncludeFlags(std::string& flags, std::string const& lang,
                        const std::string& config) override;
@@ -129,6 +131,8 @@
   /// @return the object file path for the given @a source.
   std::string GetObjectFilePath(cmSourceFile const* source,
                                 const std::string& config) const;
+  std::string GetBmiFilePath(cmSourceFile const* source,
+                             const std::string& config) const;
 
   /// @return the preprocessed source file path for the given @a source.
   std::string GetPreprocessedFilePath(cmSourceFile const* source,
@@ -163,6 +167,10 @@
   void WriteObjectBuildStatements(const std::string& config,
                                   const std::string& fileConfig,
                                   bool firstForConfig);
+  void WriteCxxModuleBmiBuildStatement(cmSourceFile const* source,
+                                       const std::string& config,
+                                       const std::string& fileConfig,
+                                       bool firstForConfig);
   void WriteObjectBuildStatement(cmSourceFile const* source,
                                  const std::string& config,
                                  const std::string& fileConfig,
@@ -221,6 +229,7 @@
 
 private:
   cmLocalNinjaGenerator* LocalGenerator;
+  bool HasPrivateGeneratedSources = false;
 
   struct ScanningFiles
   {
@@ -239,9 +248,10 @@
     cmNinjaDeps Objects;
     // Dyndep Support
     std::map<std::string, std::vector<ScanningFiles>> ScanningInfo;
+    // Imported C++ module info.
+    mutable ImportedCxxModuleLookup ImportedCxxModules;
     // Swift Support
     Json::Value SwiftOutputMap;
-    std::vector<cmCustomCommand const*> CustomCommands;
     cmNinjaDeps ExtraFiles;
     std::unique_ptr<MacOSXContentGeneratorType> MacOSXContentGenerator;
   };
diff --git a/Source/cmOrderDirectories.cxx b/Source/cmOrderDirectories.cxx
index 37c4afc..a46c87b 100644
--- a/Source/cmOrderDirectories.cxx
+++ b/Source/cmOrderDirectories.cxx
@@ -6,6 +6,7 @@
 #include <cassert>
 #include <functional>
 #include <sstream>
+#include <type_traits>
 #include <vector>
 
 #include <cm/memory>
diff --git a/Source/cmOutputConverter.cxx b/Source/cmOutputConverter.cxx
index 02981ae..9c30d74 100644
--- a/Source/cmOutputConverter.cxx
+++ b/Source/cmOutputConverter.cxx
@@ -6,6 +6,7 @@
 #include <cassert>
 #include <cctype>
 #include <set>
+#include <vector>
 
 #ifdef _WIN32
 #  include <unordered_map>
diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx
index f147ed2..75d1501 100644
--- a/Source/cmOutputRequiredFilesCommand.cxx
+++ b/Source/cmOutputRequiredFilesCommand.cxx
@@ -5,6 +5,7 @@
 #include <cstdio>
 #include <map>
 #include <set>
+#include <unordered_map>
 #include <utility>
 
 #include <cm/memory>
@@ -413,8 +414,7 @@
         path += "/";
       }
       path += fname;
-      if (cmSystemTools::FileExists(path, true) &&
-          !cmSystemTools::FileIsDirectory(path)) {
+      if (cmSystemTools::FileExists(path, true)) {
         std::string fp = cmSystemTools::CollapseFullPath(path);
         this->DirectoryToFileToPathMap[extraPath][fname] = fp;
         return fp;
@@ -427,8 +427,7 @@
         path = path + "/";
       }
       path = path + fname;
-      if (cmSystemTools::FileExists(path, true) &&
-          !cmSystemTools::FileIsDirectory(path)) {
+      if (cmSystemTools::FileExists(path, true)) {
         std::string fp = cmSystemTools::CollapseFullPath(path);
         this->DirectoryToFileToPathMap[extraPath][fname] = fp;
         return fp;
diff --git a/Source/cmParseArgumentsCommand.cxx b/Source/cmParseArgumentsCommand.cxx
index b0462f0..f193ed9 100644
--- a/Source/cmParseArgumentsCommand.cxx
+++ b/Source/cmParseArgumentsCommand.cxx
@@ -20,7 +20,9 @@
 #include "cmSystemTools.h"
 #include "cmValue.h"
 
-static std::string EscapeArg(const std::string& arg)
+namespace {
+
+std::string EscapeArg(const std::string& arg)
 {
   // replace ";" with "\;" so output argument lists will split correctly
   std::string escapedArg;
@@ -33,14 +35,12 @@
   return escapedArg;
 }
 
-static std::string JoinList(std::vector<std::string> const& arg, bool escape)
+std::string JoinList(std::vector<std::string> const& arg, bool escape)
 {
-  return escape ? cmJoin(cmMakeRange(arg).transform(EscapeArg), ";")
-                : cmJoin(cmMakeRange(arg), ";");
+  return escape ? cmList::to_string(cmMakeRange(arg).transform(EscapeArg))
+                : cmList::to_string(cmMakeRange(arg));
 }
 
-namespace {
-
 using options_map = std::map<std::string, bool>;
 using single_map = std::map<std::string, std::string>;
 using multi_map =
@@ -108,8 +108,9 @@
   }
 
   if (!keywordsMissingValues.empty()) {
-    makefile.AddDefinition(prefix + "KEYWORDS_MISSING_VALUES",
-                           cmJoin(cmMakeRange(keywordsMissingValues), ";"));
+    makefile.AddDefinition(
+      prefix + "KEYWORDS_MISSING_VALUES",
+      cmList::to_string(cmMakeRange(keywordsMissingValues)));
   } else {
     makefile.RemoveDefinition(prefix + "KEYWORDS_MISSING_VALUES");
   }
diff --git a/Source/cmPlistParser.cxx b/Source/cmPlistParser.cxx
new file mode 100644
index 0000000..ce3c171
--- /dev/null
+++ b/Source/cmPlistParser.cxx
@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmPlistParser.h"
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
+
+cm::optional<Json::Value> cmParsePlist(const std::string& filename)
+{
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(
+    { "/usr/bin/plutil", "-convert", "json", "-o", "-", filename });
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  auto chain = builder.Start();
+  chain.Wait();
+
+  auto const& status = chain.GetStatus(0);
+  if (status.ExitStatus != 0) {
+    return cm::nullopt;
+  }
+
+  Json::Reader reader;
+  Json::Value value;
+  cmUVPipeIStream outputStream(chain.GetLoop(), chain.OutputStream());
+  if (!reader.parse(outputStream, value)) {
+    return cm::nullopt;
+  }
+  return cm::optional<Json::Value>(value);
+}
diff --git a/Source/cmPlistParser.h b/Source/cmPlistParser.h
new file mode 100644
index 0000000..2ace254
--- /dev/null
+++ b/Source/cmPlistParser.h
@@ -0,0 +1,13 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+
+#include <cm/optional>
+
+namespace Json {
+class Value;
+}
+
+cm::optional<Json::Value> cmParsePlist(const std::string& filename);
diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx
index d5e5725..d89c8c8 100644
--- a/Source/cmPolicies.cxx
+++ b/Source/cmPolicies.cxx
@@ -123,23 +123,23 @@
 {
   std::ostringstream e;
   e << "The project requests behavior compatible with CMake version \""
-    << majorVer << "." << minorVer << "." << patchVer
+    << majorVer << '.' << minorVer << '.' << patchVer
     << "\", which requires the OLD behavior for some policies:\n";
   for (cmPolicies::PolicyID i : ancient) {
-    e << "  " << idToString(i) << ": " << idToShortDescription(i) << "\n";
+    e << "  " << idToString(i) << ": " << idToShortDescription(i) << '\n';
   }
   e << "However, this version of CMake no longer supports the OLD "
-    << "behavior for these policies.  "
-    << "Please either update your CMakeLists.txt files to conform to "
-    << "the new behavior or use an older version of CMake that still "
-    << "supports the old behavior.";
+       "behavior for these policies.  "
+       "Please either update your CMakeLists.txt files to conform to "
+       "the new behavior or use an older version of CMake that still "
+       "supports the old behavior.";
   mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
 }
 
 static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy,
                              cmPolicies::PolicyStatus* defaultSetting)
 {
-  std::string defaultVar = "CMAKE_POLICY_DEFAULT_" + policy;
+  std::string defaultVar = cmStrCat("CMAKE_POLICY_DEFAULT_", policy);
   std::string const& defaultValue = mf->GetSafeDefinition(defaultVar);
   if (defaultValue == "NEW") {
     *defaultSetting = cmPolicies::NEW;
@@ -148,10 +148,10 @@
   } else if (defaultValue.empty()) {
     *defaultSetting = cmPolicies::WARN;
   } else {
-    std::ostringstream e;
-    e << defaultVar << " has value \"" << defaultValue
-      << R"(" but must be "OLD", "NEW", or "" (empty).)";
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat(defaultVar, " has value \"", defaultValue,
+               R"(" but must be "OLD", "NEW", or "" (empty).)"));
     return false;
   }
 
@@ -170,10 +170,11 @@
   unsigned int minTweak = 0;
   if (sscanf(version_min.c_str(), "%u.%u.%u.%u", &minMajor, &minMinor,
              &minPatch, &minTweak) < 2) {
-    std::ostringstream e;
-    e << "Invalid policy version value \"" << version_min << "\".  "
-      << "A numeric major.minor[.patch[.tweak]] must be given.";
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Invalid policy version value \"", version_min,
+               "\".  "
+               "A numeric major.minor[.patch[.tweak]] must be given."));
     return false;
   }
 
@@ -199,13 +200,14 @@
        minMinor == cmVersion::GetMinorVersion() &&
        minPatch == cmVersion::GetPatchVersion() &&
        minTweak > cmVersion::GetTweakVersion())) {
-    std::ostringstream e;
-    e << "An attempt was made to set the policy version of CMake to \""
-      << version_min << "\" which is greater than this version of CMake.  "
-      << "This is not allowed because the greater version may have new "
-      << "policies not known to this CMake.  "
-      << "You may need a newer CMake version to build this project.";
-    mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+    mf->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("An attempt was made to set the policy version of CMake to \"",
+               version_min,
+               "\" which is greater than this version of CMake.  ",
+               "This is not allowed because the greater version may have new "
+               "policies not known to this CMake.  "
+               "You may need a newer CMake version to build this project."));
     return false;
   }
 
@@ -221,10 +223,11 @@
     unsigned int maxTweak = 0;
     if (sscanf(version_max.c_str(), "%u.%u.%u.%u", &maxMajor, &maxMinor,
                &maxPatch, &maxTweak) < 2) {
-      std::ostringstream e;
-      e << "Invalid policy max version value \"" << version_max << "\".  "
-        << "A numeric major.minor[.patch[.tweak]] must be given.";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Invalid policy max version value \"", version_max,
+                 "\".  "
+                 "A numeric major.minor[.patch[.tweak]] must be given."));
       return false;
     }
 
@@ -234,11 +237,10 @@
          minPatch > maxPatch) ||
         (minMajor == maxMajor && minMinor == maxMinor &&
          minPatch == maxPatch && minTweak > maxTweak)) {
-      std::ostringstream e;
-      e << "Policy VERSION range \"" << version_min << "..." << version_max
-        << "\""
-        << " specifies a larger minimum than maximum.";
-      mf->IssueMessage(MessageType::FATAL_ERROR, e.str());
+      mf->IssueMessage(
+        MessageType::FATAL_ERROR,
+        cmStrCat("Policy VERSION range \"", version_min, "...", version_max,
+                 "\" specifies a larger minimum than maximum."));
       return false;
     }
 
@@ -328,61 +330,49 @@
 //! return a warning string for a given policy
 std::string cmPolicies::GetPolicyWarning(cmPolicies::PolicyID id)
 {
-  std::ostringstream msg;
-  msg << "Policy " << idToString(id)
-      << " is not set: "
-         ""
-      << idToShortDescription(id)
-      << "  "
-         "Run \"cmake --help-policy "
-      << idToString(id)
-      << "\" for "
-         "policy details.  "
-         "Use the cmake_policy command to set the policy "
-         "and suppress this warning.";
-  return msg.str();
+  return cmStrCat("Policy ", idToString(id),
+                  " is not set: ", idToShortDescription(id),
+                  "  "
+                  "Run \"cmake --help-policy ",
+                  idToString(id),
+                  "\" for "
+                  "policy details.  "
+                  "Use the cmake_policy command to set the policy "
+                  "and suppress this warning.");
 }
 
 std::string cmPolicies::GetPolicyDeprecatedWarning(cmPolicies::PolicyID id)
 {
-  std::ostringstream msg;
-  /* clang-format off */
-  msg <<
-    "The OLD behavior for policy " << idToString(id) << " "
+  return cmStrCat(
+    "The OLD behavior for policy ", idToString(id),
+    " "
     "will be removed from a future version of CMake.\n"
     "The cmake-policies(7) manual explains that the OLD behaviors of all "
     "policies are deprecated and that a policy should be set to OLD only "
     "under specific short-term circumstances.  Projects should be ported "
-    "to the NEW behavior and not rely on setting a policy to OLD."
-    ;
-  /* clang-format on */
-  return msg.str();
+    "to the NEW behavior and not rely on setting a policy to OLD.");
 }
 
 //! return an error string for when a required policy is unspecified
 std::string cmPolicies::GetRequiredPolicyError(cmPolicies::PolicyID id)
 {
-  std::ostringstream error;
-  error << "Policy " << idToString(id)
-        << " is not set to NEW: "
-           ""
-        << idToShortDescription(id)
-        << "  "
-           "Run \"cmake --help-policy "
-        << idToString(id)
-        << "\" for "
-           "policy details.  "
-           "CMake now requires this policy to be set to NEW by the project.  "
-           "The policy may be set explicitly using the code\n"
-           "  cmake_policy(SET "
-        << idToString(id)
-        << " NEW)\n"
-           "or by upgrading all policies with the code\n"
-           "  cmake_policy(VERSION "
-        << idToVersion(id)
-        << ") # or later\n"
-           "Run \"cmake --help-command cmake_policy\" for more information.";
-  return error.str();
+  return cmStrCat(
+    "Policy ", idToString(id),
+    " is not set to NEW: ", idToShortDescription(id),
+    "  "
+    "Run \"cmake --help-policy ",
+    idToString(id),
+    "\" for policy details.  "
+    "CMake now requires this policy to be set to NEW by the project.  "
+    "The policy may be set explicitly using the code\n"
+    "  cmake_policy(SET ",
+    idToString(id),
+    " NEW)\n"
+    "or by upgrading all policies with the code\n"
+    "  cmake_policy(VERSION ",
+    idToVersion(id),
+    ") # or later\n"
+    "Run \"cmake --help-command cmake_policy\" for more information.");
 }
 
 //! Get the default status for a policy
@@ -395,18 +385,18 @@
 std::string cmPolicies::GetRequiredAlwaysPolicyError(cmPolicies::PolicyID id)
 {
   std::string pid = idToString(id);
-  std::ostringstream e;
-  e << "Policy " << pid << " may not be set to OLD behavior because this "
-    << "version of CMake no longer supports it.  "
-    << "The policy was introduced in "
-    << "CMake version " << idToVersion(id)
-    << ", and use of NEW behavior is now required."
-    << "\n"
-    << "Please either update your CMakeLists.txt files to conform to "
-    << "the new behavior or use an older version of CMake that still "
-    << "supports the old behavior.  "
-    << "Run cmake --help-policy " << pid << " for more information.";
-  return e.str();
+  return cmStrCat(
+    "Policy ", pid,
+    " may not be set to OLD behavior because this "
+    "version of CMake no longer supports it.  "
+    "The policy was introduced in CMake version ",
+    idToVersion(id),
+    ", and use of NEW behavior is now required."
+    "\n"
+    "Please either update your CMakeLists.txt files to conform to "
+    "the new behavior or use an older version of CMake that still "
+    "supports the old behavior.  Run cmake --help-policy ",
+    pid, " for more information.");
 }
 
 cmPolicies::PolicyStatus cmPolicies::PolicyMap::Get(
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index a0030d3..1ea2ce2 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -459,7 +459,21 @@
   SELECT(POLICY, CMP0151,                                                     \
          "AUTOMOC include directory is a system include directory by "        \
          "default.",                                                          \
-         3, 27, 0, cmPolicies::WARN)
+         3, 27, 0, cmPolicies::WARN)                                          \
+  SELECT(                                                                     \
+    POLICY, CMP0152,                                                          \
+    "file(REAL_PATH) resolves symlinks before collapsing ../ components.", 3, \
+    28, 0, cmPolicies::WARN)                                                  \
+  SELECT(POLICY, CMP0153, "The exec_program command should not be called.",   \
+         3, 28, 0, cmPolicies::WARN)                                          \
+  SELECT(                                                                     \
+    POLICY, CMP0154,                                                          \
+    "Generated files are private by default in targets using file sets.", 3,  \
+    28, 0, cmPolicies::WARN)                                                  \
+  SELECT(                                                                     \
+    POLICY, CMP0155,                                                          \
+    "C++ sources in targets with at least C++20 are scanned for imports", 3,  \
+    28, 0, cmPolicies::WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \
@@ -497,7 +511,9 @@
   F(CMP0113)                                                                  \
   F(CMP0119)                                                                  \
   F(CMP0131)                                                                  \
-  F(CMP0142)
+  F(CMP0142)                                                                  \
+  F(CMP0154)                                                                  \
+  F(CMP0155)
 
 #define CM_FOR_EACH_CUSTOM_COMMAND_POLICY(F)                                  \
   F(CMP0116)                                                                  \
diff --git a/Source/cmProcessTools.cxx b/Source/cmProcessTools.cxx
index 9e7854b..1dd1dce 100644
--- a/Source/cmProcessTools.cxx
+++ b/Source/cmProcessTools.cxx
@@ -2,48 +2,68 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProcessTools.h"
 
+#include <algorithm>
+#include <iterator>
 #include <ostream>
 
-#include "cmsys/Process.h"
+#include <cm3p/uv.h>
 
 #include "cmProcessOutput.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVStream.h"
 
-void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
-                                OutputParser* err, Encoding encoding)
+std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess(
+  cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err,
+  Encoding encoding)
 {
-  cmsysProcess_Execute(cp);
-  char* data = nullptr;
-  int length = 0;
-  int p;
   cmProcessOutput processOutput(encoding);
+
+  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+
+  auto chain = builder.Start();
+
   std::string strdata;
-  while ((out || err) &&
-         (p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
-    if (out && p == cmsysProcess_Pipe_STDOUT) {
-      processOutput.DecodeText(data, length, strdata, 1);
-      if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
-        out = nullptr;
+  cm::uv_pipe_ptr outputPipe;
+  outputPipe.init(chain.GetLoop(), 0);
+  uv_pipe_open(outputPipe, chain.OutputStream());
+  auto outputHandle = cmUVStreamRead(
+    outputPipe,
+    [&out, &processOutput, &strdata](std::vector<char> data) {
+      if (out) {
+        processOutput.DecodeText(data.data(), data.size(), strdata, 1);
+        if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
+          out = nullptr;
+        }
       }
-    } else if (err && p == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 2);
-      if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
-        err = nullptr;
+    },
+    [&out]() { out = nullptr; });
+  cm::uv_pipe_ptr errorPipe;
+  errorPipe.init(chain.GetLoop(), 0);
+  uv_pipe_open(errorPipe, chain.ErrorStream());
+  auto errorHandle = cmUVStreamRead(
+    errorPipe,
+    [&err, &processOutput, &strdata](std::vector<char> data) {
+      if (err) {
+        processOutput.DecodeText(data.data(), data.size(), strdata, 2);
+        if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
+          err = nullptr;
+        }
       }
-    }
+    },
+    [&err]() { err = nullptr; });
+  while (out || err || !chain.Finished()) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
   }
-  if (out) {
-    processOutput.DecodeText(std::string(), strdata, 1);
-    if (!strdata.empty()) {
-      out->Process(strdata.c_str(), static_cast<int>(strdata.size()));
-    }
-  }
-  if (err) {
-    processOutput.DecodeText(std::string(), strdata, 2);
-    if (!strdata.empty()) {
-      err->Process(strdata.c_str(), static_cast<int>(strdata.size()));
-    }
-  }
-  cmsysProcess_WaitForExit(cp, nullptr);
+
+  std::vector<cmUVProcessChain::Status> result;
+  auto status = chain.GetStatus();
+  std::transform(
+    status.begin(), status.end(), std::back_inserter(result),
+    [](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status {
+      return *s;
+    });
+  return result;
 }
 
 cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR)
diff --git a/Source/cmProcessTools.h b/Source/cmProcessTools.h
index 74ec5e0..2bdabea 100644
--- a/Source/cmProcessTools.h
+++ b/Source/cmProcessTools.h
@@ -7,8 +7,10 @@
 #include <cstring>
 #include <iosfwd>
 #include <string>
+#include <vector>
 
 #include "cmProcessOutput.h"
+#include "cmUVProcessChain.h"
 
 /** \class cmProcessTools
  * \brief Helper classes for process output parsing
@@ -81,7 +83,7 @@
   };
 
   /** Run a process and send output to given parsers.  */
-  static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
-                         OutputParser* err = nullptr,
-                         Encoding encoding = cmProcessOutput::Auto);
+  static std::vector<cmUVProcessChain::Status> RunProcess(
+    cmUVProcessChainBuilder& builder, OutputParser* out,
+    OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto);
 };
diff --git a/Source/cmProjectCommand.cxx b/Source/cmProjectCommand.cxx
index 4d1ccfe..3aef299 100644
--- a/Source/cmProjectCommand.cxx
+++ b/Source/cmProjectCommand.cxx
@@ -3,7 +3,6 @@
 #include "cmProjectCommand.h"
 
 #include <array>
-#include <cstddef>
 #include <cstdio>
 #include <functional>
 #include <limits>
diff --git a/Source/cmPropertyMap.cxx b/Source/cmPropertyMap.cxx
index 568a3d2..5bb769a 100644
--- a/Source/cmPropertyMap.cxx
+++ b/Source/cmPropertyMap.cxx
@@ -10,10 +10,6 @@
   this->Map_.clear();
 }
 
-void cmPropertyMap::SetProperty(const std::string& name, std::nullptr_t)
-{
-  this->Map_.erase(name);
-}
 void cmPropertyMap::SetProperty(const std::string& name, cmValue value)
 {
   if (!value) {
diff --git a/Source/cmPropertyMap.h b/Source/cmPropertyMap.h
index 23b50a5..3cd90ec 100644
--- a/Source/cmPropertyMap.h
+++ b/Source/cmPropertyMap.h
@@ -4,7 +4,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <cstddef>
 #include <string>
 #include <unordered_map>
 #include <utility>
@@ -26,7 +25,6 @@
   // -- Properties
 
   //! Set the property value
-  void SetProperty(const std::string& name, std::nullptr_t);
   void SetProperty(const std::string& name, cmValue value);
   void SetProperty(const std::string& name, const std::string& value)
   {
diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h
index d111422..2e750b6 100644
--- a/Source/cmQtAutoGen.h
+++ b/Source/cmQtAutoGen.h
@@ -6,6 +6,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include <cm/string_view>
@@ -16,6 +17,22 @@
 class cmQtAutoGen
 {
 public:
+  /** String value with per configuration variants.  */
+  class ConfigString
+  {
+  public:
+    std::string Default;
+    std::unordered_map<std::string, std::string> Config;
+  };
+
+  /** String values with per configuration variants.  */
+  template <typename C>
+  class ConfigStrings
+  {
+  public:
+    C Default;
+    std::unordered_map<std::string, C> Config;
+  };
   /** Integer version.  */
   struct IntegerVersion
   {
diff --git a/Source/cmQtAutoGenGlobalInitializer.cxx b/Source/cmQtAutoGenGlobalInitializer.cxx
index 1da8847..214df25 100644
--- a/Source/cmQtAutoGenGlobalInitializer.cxx
+++ b/Source/cmQtAutoGenGlobalInitializer.cxx
@@ -213,24 +213,81 @@
   }
 }
 
-cmQtAutoGen::CompilerFeaturesHandle
+cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
 cmQtAutoGenGlobalInitializer::GetCompilerFeatures(
-  std::string const& generator, std::string const& executable,
-  std::string& error)
+  std::string const& generator, cmQtAutoGen::ConfigString const& executable,
+  std::string& error, bool const isMultiConfig)
 {
+  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> res;
+  if (isMultiConfig) {
+    for (auto const& config : executable.Config) {
+      auto const exe = config.second;
+      // Check if we have cached features
+      {
+        auto it = this->CompilerFeatures_.Config[config.first].find(exe);
+        if (it != this->CompilerFeatures_.Config[config.first].end()) {
+          res.Config[config.first] = it->second;
+          continue;
+        }
+      }
+
+      // Check if the executable exists
+      if (!cmSystemTools::FileExists(exe, true)) {
+        error = cmStrCat("The \"", generator, "\" executable ",
+                         cmQtAutoGen::Quoted(exe), " does not exist.");
+        res.Config[config.first] = {};
+        continue;
+      }
+
+      // Test the executable
+      std::string stdOut;
+      {
+        std::string stdErr;
+        std::vector<std::string> command;
+        command.emplace_back(exe);
+        command.emplace_back("-h");
+        int retVal = 0;
+        const bool runResult = cmSystemTools::RunSingleCommand(
+          command, &stdOut, &stdErr, &retVal, nullptr,
+          cmSystemTools::OUTPUT_NONE, cmDuration::zero(),
+          cmProcessOutput::Auto);
+        if (!runResult) {
+          error = cmStrCat("Test run of \"", generator, "\" executable ",
+                           cmQtAutoGen::Quoted(exe), " failed.\n",
+                           cmQtAutoGen::QuotedCommand(command), '\n', stdOut,
+                           '\n', stdErr);
+          res.Config[config.first] = {};
+          continue;
+        }
+      }
+
+      // Create valid handle
+      res.Config[config.first] =
+        std::make_shared<cmQtAutoGen::CompilerFeatures>();
+      res.Config[config.first]->HelpOutput = std::move(stdOut);
+
+      // Register compiler features
+      this->CompilerFeatures_.Config[config.first].emplace(
+        exe, res.Config[config.first]);
+    }
+    return res;
+  }
+
   // Check if we have cached features
   {
-    auto it = this->CompilerFeatures_.find(executable);
-    if (it != this->CompilerFeatures_.end()) {
-      return it->second;
+    auto it = this->CompilerFeatures_.Default.find(executable.Default);
+    if (it != this->CompilerFeatures_.Default.end()) {
+      res.Default = it->second;
+      return res;
     }
   }
 
   // Check if the executable exists
-  if (!cmSystemTools::FileExists(executable, true)) {
-    error = cmStrCat("The \"", generator, "\" executable ",
-                     cmQtAutoGen::Quoted(executable), " does not exist.");
-    return cmQtAutoGen::CompilerFeaturesHandle();
+  if (!cmSystemTools::FileExists(executable.Default, true)) {
+    error =
+      cmStrCat("The \"", generator, "\" executable ",
+               cmQtAutoGen::Quoted(executable.Default), " does not exist.");
+    return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
   }
 
   // Test the executable
@@ -238,7 +295,7 @@
   {
     std::string stdErr;
     std::vector<std::string> command;
-    command.emplace_back(executable);
+    command.emplace_back(executable.Default);
     command.emplace_back("-h");
     int retVal = 0;
     const bool runResult = cmSystemTools::RunSingleCommand(
@@ -246,20 +303,18 @@
       cmDuration::zero(), cmProcessOutput::Auto);
     if (!runResult) {
       error = cmStrCat("Test run of \"", generator, "\" executable ",
-                       cmQtAutoGen::Quoted(executable), " failed.\n",
+                       cmQtAutoGen::Quoted(executable.Default), " failed.\n",
                        cmQtAutoGen::QuotedCommand(command), '\n', stdOut, '\n',
                        stdErr);
-      return cmQtAutoGen::CompilerFeaturesHandle();
+      return cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>();
     }
   }
 
-  // Create valid handle
-  cmQtAutoGen::CompilerFeaturesHandle res =
-    std::make_shared<cmQtAutoGen::CompilerFeatures>();
-  res->HelpOutput = std::move(stdOut);
+  res.Default = std::make_shared<cmQtAutoGen::CompilerFeatures>();
+  res.Default->HelpOutput = std::move(stdOut);
 
   // Register compiler features
-  this->CompilerFeatures_.emplace(executable, res);
+  this->CompilerFeatures_.Default.emplace(executable.Default, res.Default);
 
   return res;
 }
diff --git a/Source/cmQtAutoGenGlobalInitializer.h b/Source/cmQtAutoGenGlobalInitializer.h
index e8569a5..5ea5997 100644
--- a/Source/cmQtAutoGenGlobalInitializer.h
+++ b/Source/cmQtAutoGenGlobalInitializer.h
@@ -66,14 +66,16 @@
   void AddToGlobalAutoRcc(cmLocalGenerator* localGen,
                           std::string const& targetName);
 
-  cmQtAutoGen::CompilerFeaturesHandle GetCompilerFeatures(
-    std::string const& generator, std::string const& executable,
-    std::string& error);
+  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle>
+  GetCompilerFeatures(std::string const& generator,
+                      cmQtAutoGen::ConfigString const& executable,
+                      std::string& error, bool isMultiConfig);
 
   std::vector<std::unique_ptr<cmQtAutoGenInitializer>> Initializers_;
   std::map<cmLocalGenerator*, std::string> GlobalAutoGenTargets_;
   std::map<cmLocalGenerator*, std::string> GlobalAutoRccTargets_;
-  std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>
+  cmQtAutoGen::ConfigStrings<
+    std::unordered_map<std::string, cmQtAutoGen::CompilerFeaturesHandle>>
     CompilerFeatures_;
   Keywords const Keywords_;
 };
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index c9f65f6..a33c5db 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenInitializer.h"
 
+#include <array>
 #include <cstddef>
 #include <deque>
 #include <initializer_list>
@@ -17,6 +18,7 @@
 #include <cm/algorithm>
 #include <cm/iterator>
 #include <cm/memory>
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
@@ -301,15 +303,22 @@
   return fileStream.Close();
 }
 
-void AddAutogenExecutableToDependencies(
-  cmQtAutoGenInitializer::GenVarsT const& genVars,
-  std::vector<std::string>& dependencies)
+cmQtAutoGen::ConfigStrings<std::vector<std::string>> generateListOptions(
+  cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> const&
+    executableFeatures,
+  bool IsMultiConfig)
 {
-  if (genVars.ExecutableTarget != nullptr) {
-    dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
-  } else if (!genVars.Executable.empty()) {
-    dependencies.push_back(genVars.Executable);
+  cmQtAutoGen::ConfigStrings<std::vector<std::string>> tempListOptions;
+  if (IsMultiConfig) {
+    for (auto const& executableFeature : executableFeatures.Config) {
+      tempListOptions.Config[executableFeature.first] =
+        executableFeature.second->ListOptions;
+    }
+  } else {
+    tempListOptions.Default = executableFeatures.Default->ListOptions;
   }
+
+  return tempListOptions;
 }
 
 } // End of unnamed namespace
@@ -332,6 +341,36 @@
   this->Uic.Enabled = uicEnabled;
   this->Rcc.Enabled = rccEnabled;
   this->Rcc.GlobalTarget = globalAutoRccTarget;
+  this->CrossConfig =
+    !this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty();
+}
+
+void cmQtAutoGenInitializer::AddAutogenExecutableToDependencies(
+  cmQtAutoGenInitializer::GenVarsT const& genVars,
+  std::vector<std::string>& dependencies) const
+{
+  if (genVars.ExecutableTarget != nullptr) {
+    dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
+  } else if (this->MultiConfig) {
+    cm::string_view const& configGenexWithCommandConfig =
+      "$<COMMAND_CONFIG:$<$<CONFIG:";
+    cm::string_view const& configGenex = "$<$<CONFIG:";
+    cm::string_view const& configGenexEnd = ">";
+    cm::string_view const& configGenexEndWithCommandConfig = ">>";
+    auto genexBegin =
+      this->CrossConfig ? configGenexWithCommandConfig : configGenex;
+    auto genexEnd =
+      this->CrossConfig ? configGenexEndWithCommandConfig : configGenexEnd;
+    for (auto const& config : genVars.Executable.Config) {
+      auto executableWithConfig =
+        cmStrCat(genexBegin, config.first, ">:", config.second, genexEnd);
+      dependencies.emplace_back(std::move(executableWithConfig));
+    }
+  } else {
+    if (!genVars.Executable.Default.empty()) {
+      dependencies.push_back(genVars.Executable.Default);
+    }
+  }
 }
 
 bool cmQtAutoGenInitializer::InitCustomTargets()
@@ -777,18 +816,30 @@
       return false;
     }
     // Evaluate test output on demand
-    CompilerFeatures& features = *this->Rcc.ExecutableFeatures;
-    if (!features.Evaluated) {
-      // Look for list options
-      if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
-        if (features.HelpOutput.find("--list") != std::string::npos) {
-          features.ListOptions.emplace_back("--list");
-        } else if (features.HelpOutput.find("-list") != std::string::npos) {
-          features.ListOptions.emplace_back("-list");
+    auto& features = this->Rcc.ExecutableFeatures;
+    auto checkAndAddOptions = [this](CompilerFeaturesHandle& feature) {
+      if (!feature->Evaluated) {
+        // Look for list options
+        if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
+          static std::array<std::string, 2> const listOptions{ { "--list",
+                                                                 "-list" } };
+          for (std::string const& opt : listOptions) {
+            if (feature->HelpOutput.find(opt) != std::string::npos) {
+              feature->ListOptions.emplace_back(opt);
+              break;
+            }
+          }
         }
+        // Evaluation finished
+        feature->Evaluated = true;
       }
-      // Evaluation finished
-      features.Evaluated = true;
+    };
+    if (this->MultiConfig) {
+      for (auto const& config : this->ConfigsList) {
+        checkAndAddOptions(features.Config[config]);
+      }
+    } else {
+      checkAndAddOptions(features.Default);
     }
   }
 
@@ -904,13 +955,13 @@
       if (muf.MocIt || muf.UicIt) {
         // Search for the default header file and a private header
         std::string const& srcFullPath = muf.SF->ResolveFullPath();
-        std::string basePath = cmStrCat(
+        std::string const basePath = cmStrCat(
           cmQtAutoGen::SubDirPrefix(srcFullPath),
           cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath));
         for (auto const& suffix : suffixes) {
           std::string const suffixedPath = cmStrCat(basePath, suffix);
           for (auto const& ext : exts) {
-            std::string fullPath = cmStrCat(suffixedPath, '.', ext);
+            std::string const fullPath = cmStrCat(suffixedPath, '.', ext);
 
             auto constexpr locationKind = cmSourceFileLocationKind::Known;
             cmSourceFile* sf =
@@ -1026,8 +1077,7 @@
   if (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) {
     if (this->CMP0071Accept) {
       // Let the autogen target depend on the GENERATED files
-      if (this->MultiConfig &&
-          this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty()) {
+      if (this->MultiConfig && !this->CrossConfig) {
         for (MUFile const* muf : this->AutogenTarget.FilesGenerated) {
           if (muf->Configs.empty()) {
             this->AutogenTarget.DependFiles.insert(muf->FullPath);
@@ -1125,8 +1175,13 @@
       // Path checksum
       qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
       // Output file name
-      qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
-                                "/qrc_", qrc.QrcName, ".cpp");
+      if (this->MultiConfig && !this->GlobalGen->IsXcode()) {
+        qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
+                                  "_$<CONFIG>", "/qrc_", qrc.QrcName, ".cpp");
+      } else {
+        qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
+                                  "/qrc_", qrc.QrcName, ".cpp");
+      }
       std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
                                         qrc.QrcName, '_', qrc.QrcPathChecksum);
       qrc.LockFile = cmStrCat(base, "_Lock.lock");
@@ -1158,11 +1213,25 @@
     for (Qrc& qrc : this->Rcc.Qrcs) {
       if (!qrc.Generated) {
         std::string error;
-        RccLister const lister(this->Rcc.Executable,
-                               this->Rcc.ExecutableFeatures->ListOptions);
-        if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
-          cmSystemTools::Error(error);
-          return false;
+        if (this->MultiConfig) {
+          for (auto const& config : this->ConfigsList) {
+            RccLister const lister(
+              this->Rcc.Executable.Config[config],
+              this->Rcc.ExecutableFeatures.Config[config]->ListOptions);
+            if (!lister.list(qrc.QrcFile, qrc.Resources.Config[config],
+                             error)) {
+              cmSystemTools::Error(error);
+              return false;
+            }
+          }
+        } else {
+          RccLister const lister(
+            this->Rcc.Executable.Default,
+            this->Rcc.ExecutableFeatures.Default->ListOptions);
+          if (!lister.list(qrc.QrcFile, qrc.Resources.Default, error)) {
+            cmSystemTools::Error(error);
+            return false;
+          }
         }
       }
     }
@@ -1177,27 +1246,20 @@
   this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
 
   // Determine whether to use a depfile for the AUTOGEN target.
-  const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) &&
-    this->GlobalGen->GetName().find("Ninja") != std::string::npos;
+  bool const useDepfile = [this]() -> bool {
+    auto const& gen = this->GlobalGen->GetName();
+    return this->QtVersion >= IntegerVersion(5, 15) &&
+      (gen.find("Ninja") != std::string::npos ||
+       gen.find("Make") != std::string::npos);
+  }();
 
   // Files provided by the autogen target
   std::vector<std::string> autogenByproducts;
   std::vector<std::string> timestampByproducts;
   if (this->Moc.Enabled) {
     this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
-    if (useNinjaDepfile) {
-      if (this->MultiConfig &&
-          !this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty()) {
-        // Make all mocs_compilation_<CONFIG>.cpp files byproducts of the
-        // ${target}_autogen/timestamp custom command.
-        // We cannot just use Moc.CompilationFileGenex here, because that
-        // custom command runs cmake_autogen for each configuration.
-        for (const auto& p : this->Moc.CompilationFile.Config) {
-          timestampByproducts.push_back(p.second);
-        }
-      } else {
-        timestampByproducts.push_back(this->Moc.CompilationFileGenex);
-      }
+    if (useDepfile) {
+      timestampByproducts.push_back(this->Moc.CompilationFileGenex);
     } else {
       autogenByproducts.push_back(this->Moc.CompilationFileGenex);
     }
@@ -1230,28 +1292,11 @@
   // Compose command lines
   // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
   // instead of fiddling with the include directories
-  std::vector<std::string> configs;
-  this->GlobalGen->GetQtAutoGenConfigs(configs);
+
   bool constexpr stdPipesUTF8 = true;
   cmCustomCommandLines commandLines;
-  if (this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty()) {
-    std::string autugenInfoFileconfig;
-    if (this->MultiConfig) {
-      autugenInfoFileconfig = "$<CONFIG>";
-    } else {
-      autugenInfoFileconfig = configs[0];
-    }
-    commandLines.push_back(cmMakeCommandLine(
-      { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
-        this->AutogenTarget.InfoFile, autugenInfoFileconfig }));
-
-  } else {
-    for (auto const& config : configs) {
-      commandLines.push_back(cmMakeCommandLine(
-        { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
-          this->AutogenTarget.InfoFile, config }));
-    }
-  }
+  AddCMakeProcessToCommandLines(this->AutogenTarget.InfoFile, "cmake_autogen",
+                                commandLines);
 
   // Use PRE_BUILD on demand
   bool usePRE_BUILD = false;
@@ -1365,7 +1410,7 @@
       this->AutogenTarget.DependFiles.begin(),
       this->AutogenTarget.DependFiles.end());
 
-    if (useNinjaDepfile) {
+    if (useDepfile) {
       // Create a custom command that generates a timestamp file and
       // has a depfile assigned. The depfile is created by JobDepFilesMergeT.
       //
@@ -1419,18 +1464,47 @@
 
       AddAutogenExecutableToDependencies(this->Moc, dependencies);
       AddAutogenExecutableToDependencies(this->Uic, dependencies);
-
+      std::string outputFile;
+      std::string depFile;
       // Create the custom command that outputs the timestamp file.
-      const char timestampFileName[] = "timestamp";
-      const std::string outputFile =
-        cmStrCat(this->Dir.Build, "/", timestampFileName);
-      this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
-      this->AutogenTarget.DepFileRuleName =
-        cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
-      commandLines.push_back(cmMakeCommandLine(
-        { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
+      if (this->MultiConfig) {
+        // create timestamp file with $<CONFIG> in the name so that
+        // every cmake_autogen target has its own timestamp file
+        std::string const configView = "$<CONFIG>";
+        std::string const timestampFileWithoutConfig = "timestamp_";
+        std::string const depFileWithoutConfig =
+          cmStrCat(this->Dir.Build, "/deps_");
+        std::string const timestampFileName =
+          timestampFileWithoutConfig + configView;
+        outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
+        auto const depFileWithConfig =
+          cmStrCat(depFileWithoutConfig, configView);
+        depFile = depFileWithConfig;
+        commandLines.push_back(cmMakeCommandLine(
+          { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
 
-      this->AddGeneratedSource(outputFile, this->Moc);
+        ConfigString outputFileWithConfig;
+        for (std::string const& config : this->ConfigsList) {
+          auto tempTimestampFileName = timestampFileWithoutConfig + config;
+          auto tempDepFile = depFileWithoutConfig + config;
+          outputFileWithConfig.Config[config] = tempTimestampFileName;
+          this->AutogenTarget.DepFileRuleName.Config[config] =
+            cmStrCat(this->Dir.RelativeBuild, "/", tempTimestampFileName);
+          this->AutogenTarget.DepFile.Config[config] = tempDepFile;
+        }
+        this->AddGeneratedSource(outputFileWithConfig, this->Moc);
+      } else {
+        cm::string_view const timestampFileName = "timestamp";
+        outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
+        this->AutogenTarget.DepFile.Default =
+          cmStrCat(this->Dir.Build, "/deps");
+        depFile = this->AutogenTarget.DepFile.Default;
+        this->AutogenTarget.DepFileRuleName.Default =
+          cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
+        commandLines.push_back(cmMakeCommandLine(
+          { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
+        this->AddGeneratedSource(outputFile, this->Moc);
+      }
       cc = cm::make_unique<cmCustomCommand>();
       cc->SetOutputs(outputFile);
       cc->SetByproducts(timestampByproducts);
@@ -1439,14 +1513,11 @@
       cc->SetComment(autogenComment.c_str());
       cc->SetWorkingDirectory(this->Dir.Work.c_str());
       cc->SetEscapeOldStyle(false);
-      cc->SetDepfile(this->AutogenTarget.DepFile);
+      cc->SetDepfile(depFile);
       cc->SetStdPipesUTF8(stdPipesUTF8);
       this->LocalGen->AddCustomCommandToOutput(std::move(cc));
-
-      // Alter variables for the autogen target which now merely wraps the
-      // custom command
       dependencies.clear();
-      dependencies.push_back(outputFile);
+      dependencies.emplace_back(std::move(outputFile));
       commandLines.clear();
       autogenComment.clear();
     }
@@ -1472,7 +1543,7 @@
         autogenTarget->AddUtility(depName.Value.first, false, this->Makefile);
       }
     }
-    if (!useNinjaDepfile) {
+    if (!useDepfile) {
       // Add additional autogen target dependencies to autogen target
       for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) {
         autogenTarget->AddUtility(depTarget->GetName(), false, this->Makefile);
@@ -1498,6 +1569,35 @@
   return true;
 }
 
+void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines(
+  std::string const& infoFile, std::string const& processName,
+  cmCustomCommandLines& commandLines)
+{
+  if (this->CrossConfig) {
+    commandLines.push_back(cmMakeCommandLine(
+      { cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile,
+        "$<CONFIG>", "$<COMMAND_CONFIG:$<CONFIG>>" }));
+  } else if (this->MultiConfig && this->GlobalGen->IsXcode()) {
+    for (std::string const& config : this->ConfigsList) {
+      commandLines.push_back(
+        cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
+                            processName, infoFile, config }));
+    }
+  } else {
+    std::string autoInfoFileConfig;
+    if (this->MultiConfig) {
+      autoInfoFileConfig = "$<CONFIG>";
+    } else {
+      std::vector<std::string> configs;
+      this->GlobalGen->GetQtAutoGenConfigs(configs);
+      autoInfoFileConfig = configs[0];
+    }
+    commandLines.push_back(
+      cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName,
+                          infoFile, autoInfoFileConfig }));
+  }
+}
+
 bool cmQtAutoGenInitializer::InitRccTargets()
 {
   for (Qrc const& qrc : this->Rcc.Qrcs) {
@@ -1518,19 +1618,9 @@
     ccDepends.push_back(qrc.InfoFile);
 
     cmCustomCommandLines commandLines;
-    if (this->MultiConfig) {
-      // Build for all configurations
-      for (std::string const& config : this->ConfigsList) {
-        commandLines.push_back(
-          cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
-                              "cmake_autorcc", qrc.InfoFile, config }));
-      }
-    } else {
-      commandLines.push_back(
-        cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
-                            "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
-    }
-    std::string ccComment =
+    AddCMakeProcessToCommandLines(qrc.InfoFile, "cmake_autorcc", commandLines);
+
+    std::string const ccComment =
       cmStrCat("Automatic RCC for ",
                FileProjectRelativePath(this->Makefile, qrc.QrcFile));
 
@@ -1578,18 +1668,30 @@
     } else {
       // Create custom rcc command
       {
-        std::vector<std::string> ccByproducts;
-
         // Add the resource files to the dependencies
-        for (std::string const& fileName : qrc.Resources) {
-          // Add resource file to the custom command dependencies
-          ccDepends.push_back(fileName);
+        if (this->MultiConfig) {
+          for (auto const& config : this->ConfigsList) {
+            // Add resource file to the custom command dependencies
+            auto resourceFilesWithConfig = cmStrCat(
+              "$<$<CONFIG:", config,
+              ">:", cmList{ qrc.Resources.Config.at(config) }.to_string(),
+              ">");
+            ccDepends.emplace_back(std::move(resourceFilesWithConfig));
+          }
+        } else {
+          for (std::string const& fileName : qrc.Resources.Default) {
+            // Add resource file to the custom command dependencies
+            ccDepends.push_back(fileName);
+          }
         }
+
         if (!this->Rcc.ExecutableTargetName.empty()) {
           ccDepends.push_back(this->Rcc.ExecutableTargetName);
         }
+
+        AddAutogenExecutableToDependencies(this->Rcc, ccDepends);
+
         cc->SetOutputs(ccOutput);
-        cc->SetByproducts(ccByproducts);
         cc->SetDepends(ccDepends);
         this->LocalGen->AddCustomCommandToOutput(std::move(cc));
       }
@@ -1688,6 +1790,7 @@
 
   // General
   info.SetBool("MULTI_CONFIG", this->MultiConfig);
+  info.SetBool("CROSS_CONFIG", this->CrossConfig);
   info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
   info.SetUInt("VERBOSITY", this->Verbosity);
 
@@ -1701,14 +1804,14 @@
 
   info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
   info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
-  info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
-  info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable);
+  info.SetConfig("QT_MOC_EXECUTABLE", this->Moc.Executable);
+  info.SetConfig("QT_UIC_EXECUTABLE", this->Uic.Executable);
 
   info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
   info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
   info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
-  info.Set("DEP_FILE", this->AutogenTarget.DepFile);
-  info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
+  info.SetConfig("DEP_FILE", this->AutogenTarget.DepFile);
+  info.SetConfig("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
   info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles());
   info.SetArray("HEADER_EXTENSIONS",
                 this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
@@ -1835,7 +1938,9 @@
 
     // General
     info.SetBool("MULTI_CONFIG", this->MultiConfig);
+    info.SetBool("CROSS_CONFIG", this->CrossConfig);
     info.SetUInt("VERBOSITY", this->Verbosity);
+    info.Set("GENERATOR", this->GlobalGen->GetName());
 
     // Files
     info.Set("LOCK_FILE", qrc.LockFile);
@@ -1850,16 +1955,17 @@
     info.SetConfig("INCLUDE_DIR", this->Dir.Include);
 
     // rcc executable
-    info.Set("RCC_EXECUTABLE", this->Rcc.Executable);
-    info.SetArray("RCC_LIST_OPTIONS",
-                  this->Rcc.ExecutableFeatures->ListOptions);
+    info.SetConfig("RCC_EXECUTABLE", this->Rcc.Executable);
+    info.SetConfigArray(
+      "RCC_LIST_OPTIONS",
+      generateListOptions(this->Rcc.ExecutableFeatures, this->MultiConfig));
 
     // qrc file
     info.Set("SOURCE", qrc.QrcFile);
     info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
     info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
     info.SetArray("OPTIONS", qrc.Options);
-    info.SetArray("INPUTS", qrc.Resources);
+    info.SetConfigArray("INPUTS", qrc.Resources);
 
     info.Save(qrc.InfoFile);
   }
@@ -2203,16 +2309,32 @@
         cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
         cmGeneratorExpression ge(*this->Makefile->GetCMakeInstance(), lfbt);
         std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
-        genVars.Executable = cge->Evaluate(this->LocalGen, "");
+        if (this->MultiConfig) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.Executable.Config[config] =
+              cge->Evaluate(this->LocalGen, config);
+          }
+        } else {
+          genVars.Executable.Default = cge->Evaluate(this->LocalGen, "");
+        }
       }
-      if (genVars.Executable.empty() && !ignoreMissingTarget) {
+
+      if (genVars.Executable.Default.empty() &&
+          genVars.Executable.Config.empty() && !ignoreMissingTarget) {
         print_err(prop + " evaluates to an empty value");
         return false;
       }
 
       // Create empty compiler features.
-      genVars.ExecutableFeatures =
-        std::make_shared<cmQtAutoGen::CompilerFeatures>();
+      if (this->MultiConfig) {
+        for (auto const& config : this->ConfigsList) {
+          genVars.ExecutableFeatures.Config[config] =
+            std::make_shared<cmQtAutoGen::CompilerFeatures>();
+        }
+      } else {
+        genVars.ExecutableFeatures.Default =
+          std::make_shared<cmQtAutoGen::CompilerFeatures>();
+      }
       return true;
     }
   }
@@ -2237,15 +2359,39 @@
       genVars.ExecutableTargetName = targetName;
       genVars.ExecutableTarget = genTarget;
       if (genTarget->IsImported()) {
-        genVars.Executable = genTarget->ImportedGetLocation("");
+        if (this->MultiConfig) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.Executable.Config[config] =
+              genTarget->ImportedGetLocation(config);
+          }
+        } else {
+          genVars.Executable.Default =
+            genTarget->ImportedGetLocation(this->ConfigDefault);
+        }
+
       } else {
-        genVars.Executable = genTarget->GetLocation("");
+        if (this->MultiConfig) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.Executable.Config[config] = genTarget->GetLocation(config);
+          }
+        } else {
+          genVars.Executable.Default =
+            genTarget->GetLocation(this->ConfigDefault);
+        }
       }
     } else {
       if (ignoreMissingTarget) {
         // Create empty compiler features.
-        genVars.ExecutableFeatures =
-          std::make_shared<cmQtAutoGen::CompilerFeatures>();
+        if (this->MultiConfig) {
+          for (auto const& config : this->ConfigsList) {
+            genVars.ExecutableFeatures.Config[config] =
+              std::make_shared<cmQtAutoGen::CompilerFeatures>();
+          }
+        } else {
+          genVars.ExecutableFeatures.Default =
+            std::make_shared<cmQtAutoGen::CompilerFeatures>();
+        }
+
         return true;
       }
       print_err(cmStrCat("Could not find ", executable, " executable target ",
@@ -2258,10 +2404,21 @@
   {
     std::string err;
     genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures(
-      executable, genVars.Executable, err);
-    if (!genVars.ExecutableFeatures) {
-      print_err(err);
-      return false;
+      executable, genVars.Executable, err, this->MultiConfig);
+    if (this->MultiConfig) {
+      for (auto const& config : this->ConfigsList) {
+        if (!genVars.ExecutableFeatures.Config[config]) {
+          if (!genVars.ExecutableFeatures.Config[config]) {
+            print_err(err);
+            return false;
+          }
+        }
+      }
+    } else {
+      if (!genVars.ExecutableFeatures.Default) {
+        print_err(err);
+        return false;
+      }
     }
   }
 
diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h
index 6d5261a..ee37d2f 100644
--- a/Source/cmQtAutoGenInitializer.h
+++ b/Source/cmQtAutoGenInitializer.h
@@ -18,6 +18,7 @@
 #include "cmFilePathChecksum.h"
 #include "cmQtAutoGen.h"
 
+class cmCustomCommandLines;
 class cmGeneratorTarget;
 class cmGlobalGenerator;
 class cmLocalGenerator;
@@ -32,23 +33,6 @@
 class cmQtAutoGenInitializer : public cmQtAutoGen
 {
 public:
-  /** String value with per configuration variants.  */
-  class ConfigString
-  {
-  public:
-    std::string Default;
-    std::unordered_map<std::string, std::string> Config;
-  };
-
-  /** String values with per configuration variants.  */
-  template <typename C>
-  class ConfigStrings
-  {
-  public:
-    C Default;
-    std::unordered_map<std::string, C> Config;
-  };
-
   /** rcc job.  */
   class Qrc
   {
@@ -63,7 +47,7 @@
     bool Generated = false;
     bool Unique = false;
     std::vector<std::string> Options;
-    std::vector<std::string> Resources;
+    ConfigStrings<std::vector<std::string>> Resources;
   };
 
   /** moc and/or uic file.  */
@@ -90,8 +74,8 @@
     // Executable
     std::string ExecutableTargetName;
     cmGeneratorTarget* ExecutableTarget = nullptr;
-    std::string Executable;
-    CompilerFeaturesHandle ExecutableFeatures;
+    ConfigString Executable;
+    ConfigStrings<CompilerFeaturesHandle> ExecutableFeatures;
 
     GenVarsT(GenT gen)
       : Gen(gen)
@@ -141,6 +125,9 @@
                           GenVarsT const& genVars, bool prepend = false);
   void AddToSourceGroup(std::string const& fileName,
                         cm::string_view genNameUpper);
+  void AddCMakeProcessToCommandLines(std::string const& infoFile,
+                                     std::string const& processName,
+                                     cmCustomCommandLines& commandLines);
   void AddCleanFile(std::string const& fileName);
 
   void ConfigFileNames(ConfigString& configString, cm::string_view prefix,
@@ -155,6 +142,9 @@
                        bool ignoreMissingTarget) const;
 
   void handleSkipPch(cmSourceFile* sf);
+  void AddAutogenExecutableToDependencies(
+    cmQtAutoGenInitializer::GenVarsT const& genVars,
+    std::vector<std::string>& dependencies) const;
 
   cmQtAutoGenGlobalInitializer* GlobalInitializer = nullptr;
   cmGeneratorTarget* GenTarget = nullptr;
@@ -167,6 +157,7 @@
   IntegerVersion QtVersion;
   unsigned int Verbosity = 0;
   bool MultiConfig = false;
+  bool CrossConfig = false;
   bool CMP0071Accept = false;
   bool CMP0071Warn = false;
   bool CMP0100Accept = false;
@@ -201,8 +192,8 @@
     bool DependOrigin = false;
     std::set<std::string> DependFiles;
     std::set<cmTarget*> DependTargets;
-    std::string DepFile;
-    std::string DepFileRuleName;
+    ConfigString DepFile;
+    ConfigString DepFileRuleName;
     // Sources to process
     std::unordered_map<cmSourceFile*, MUFileHandle> Headers;
     std::unordered_map<cmSourceFile*, MUFileHandle> Sources;
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index e5c7bda..ebdec12 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -2,6 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmQtAutoGenerator.h"
 
+#include <iterator>
+
 #include <cm3p/json/reader.h>
 
 #include "cmsys/FStream.hxx"
@@ -428,10 +430,12 @@
   return cmQtAutoGen::Quoted(res);
 }
 
-bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config)
+bool cmQtAutoGenerator::Run(cm::string_view infoFile, cm::string_view config,
+                            cm::string_view executableConfig)
 {
   // Info config
   this->InfoConfig_ = std::string(config);
+  this->ExecutableConfig_ = std::string(executableConfig);
 
   // Info file
   this->InfoFile_ = std::string(infoFile);
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 5c3a8ad..4b15fc7 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -90,6 +90,10 @@
   std::string const& InfoDir() const { return this->InfoDir_; }
   cmFileTime const& InfoFileTime() const { return this->InfoFileTime_; }
   std::string const& InfoConfig() const { return this->InfoConfig_; }
+  std::string const& ExecutableConfig() const
+  {
+    return this->ExecutableConfig_;
+  }
 
   // -- Info file parsing
   /** Info file reader class. */
@@ -151,7 +155,8 @@
   std::string MessagePath(cm::string_view path) const;
 
   // -- Run
-  bool Run(cm::string_view infoFile, cm::string_view config);
+  bool Run(cm::string_view infoFile, cm::string_view config,
+           cm::string_view executableConfig);
 
 protected:
   // -- Abstract processing interface
@@ -170,6 +175,7 @@
   std::string InfoDir_;
   cmFileTime InfoFileTime_;
   std::string InfoConfig_;
+  std::string ExecutableConfig_;
   // -- Directories
   ProjectDirsT ProjectDirs_;
 };
diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
index a101a81..68d2897 100644
--- a/Source/cmQtAutoMocUic.cxx
+++ b/Source/cmQtAutoMocUic.cxx
@@ -27,6 +27,7 @@
 #include "cmCryptoHash.h"
 #include "cmFileTime.h"
 #include "cmGccDepfileReader.h"
+#include "cmGccDepfileReaderTypes.h"
 #include "cmGeneratedFileStream.h"
 #include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
@@ -169,6 +170,7 @@
     // -- Attributes
     // - Config
     bool MultiConfig = false;
+    bool CrossConfig = false;
     IntegerVersion QtVersion = { 4, 0 };
     unsigned int ThreadCount = 0;
     // - Directories
@@ -2116,7 +2118,7 @@
     cmd.resize(1);
 
     // Specify response file
-    cmd.push_back(cmStrCat('@', responseFile));
+    cmd.emplace_back(cmStrCat('@', responseFile));
   }
 #else
   static_cast<void>(outputFile);
@@ -2371,6 +2373,7 @@
 {
   // -- Required settings
   if (!info.GetBool("MULTI_CONFIG", this->BaseConst_.MultiConfig, true) ||
+      !info.GetBool("CROSS_CONFIG", this->BaseConst_.CrossConfig, true) ||
       !info.GetUInt("QT_VERSION_MAJOR", this->BaseConst_.QtVersion.Major,
                     true) ||
       !info.GetUInt("QT_VERSION_MINOR", this->BaseConst_.QtVersion.Minor,
@@ -2383,19 +2386,34 @@
                       true) ||
       !info.GetStringConfig("PARSE_CACHE_FILE",
                             this->BaseConst_.ParseCacheFile, true) ||
-      !info.GetString("DEP_FILE", this->BaseConst_.DepFile, false) ||
-      !info.GetString("DEP_FILE_RULE_NAME", this->BaseConst_.DepFileRuleName,
-                      false) ||
+      !info.GetStringConfig("DEP_FILE", this->BaseConst_.DepFile, false) ||
+      !info.GetStringConfig("DEP_FILE_RULE_NAME",
+                            this->BaseConst_.DepFileRuleName, false) ||
       !info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) ||
       !info.GetArray("CMAKE_LIST_FILES", this->BaseConst_.ListFiles, true) ||
       !info.GetArray("HEADER_EXTENSIONS", this->BaseConst_.HeaderExtensions,
-                     true) ||
-      !info.GetString("QT_MOC_EXECUTABLE", this->MocConst_.Executable,
-                      false) ||
-      !info.GetString("QT_UIC_EXECUTABLE", this->UicConst_.Executable,
-                      false)) {
+                     true)) {
     return false;
   }
+  if (this->BaseConst_.CrossConfig) {
+    std::string const mocExecutableWithConfig =
+      "QT_MOC_EXECUTABLE_" + this->ExecutableConfig();
+    std::string const uicExecutableWithConfig =
+      "QT_UIC_EXECUTABLE_" + this->ExecutableConfig();
+    if (!info.GetString(mocExecutableWithConfig, this->MocConst_.Executable,
+                        false) ||
+        !info.GetString(uicExecutableWithConfig, this->UicConst_.Executable,
+                        false)) {
+      return false;
+    }
+  } else {
+    if (!info.GetStringConfig("QT_MOC_EXECUTABLE", this->MocConst_.Executable,
+                              false) ||
+        !info.GetStringConfig("QT_UIC_EXECUTABLE", this->UicConst_.Executable,
+                              false)) {
+      return false;
+    }
+  }
 
   // -- Checks
   if (!this->BaseConst_.CMakeExecutableTime.Load(
@@ -3062,7 +3080,8 @@
 
 } // End of unnamed namespace
 
-bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config)
+bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config,
+                    cm::string_view executableConfig)
 {
-  return cmQtAutoMocUicT().Run(infoFile, config);
+  return cmQtAutoMocUicT().Run(infoFile, config, executableConfig);
 }
diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h
index 20f9d6e..5cb4ff1 100644
--- a/Source/cmQtAutoMocUic.h
+++ b/Source/cmQtAutoMocUic.h
@@ -10,4 +10,5 @@
  * Process AUTOMOC and AUTOUIC
  * @return true on success
  */
-bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config);
+bool cmQtAutoMocUic(cm::string_view infoFile, cm::string_view config,
+                    cm::string_view executableConfig);
diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index 414a692..576f611 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -14,7 +14,6 @@
 #include "cmFileLockResult.h"
 #include "cmFileTime.h"
 #include "cmProcessOutput.h"
-#include "cmQtAutoGen.h"
 #include "cmQtAutoGenerator.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -36,6 +35,11 @@
 private:
   // -- Utility
   bool IsMultiConfig() const { return this->MultiConfig_; }
+  std::string const& GetGenerator() const { return this->Generator_; }
+  bool IsXcode() const
+  {
+    return this->GetGenerator().find("Xcode") != std::string::npos;
+  }
   std::string MultiConfigOutput() const;
 
   // -- Abstract processing interface
@@ -54,6 +58,8 @@
 
   // -- Config settings
   bool MultiConfig_ = false;
+  bool CrossConfig_ = false;
+  std::string Generator_;
   // -- Directories
   std::string AutogenBuildDir_;
   std::string IncludeDir_;
@@ -93,26 +99,45 @@
 {
   // -- Required settings
   if (!info.GetBool("MULTI_CONFIG", this->MultiConfig_, true) ||
+      !info.GetString("GENERATOR", this->Generator_, true) ||
+      !info.GetBool("CROSS_CONFIG", this->CrossConfig_, true) ||
       !info.GetString("BUILD_DIR", this->AutogenBuildDir_, true) ||
       !info.GetStringConfig("INCLUDE_DIR", this->IncludeDir_, true) ||
-      !info.GetString("RCC_EXECUTABLE", this->RccExecutable_, true) ||
-      !info.GetArray("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
+      !info.GetArrayConfig("RCC_LIST_OPTIONS", this->RccListOptions_, false) ||
       !info.GetString("LOCK_FILE", this->LockFile_, true) ||
       !info.GetStringConfig("SETTINGS_FILE", this->SettingsFile_, true) ||
       !info.GetString("SOURCE", this->QrcFile_, true) ||
       !info.GetString("OUTPUT_CHECKSUM", this->RccPathChecksum_, true) ||
       !info.GetString("OUTPUT_NAME", this->RccFileName_, true) ||
       !info.GetArray("OPTIONS", this->Options_, false) ||
-      !info.GetArray("INPUTS", this->Inputs_, false)) {
+      !info.GetArrayConfig("INPUTS", this->Inputs_, false)) {
     return false;
   }
 
+  if (this->CrossConfig_) {
+    std::string const rccExecutableWithConfig =
+      "RCC_EXECUTABLE_" + this->ExecutableConfig();
+    if (!info.GetString(rccExecutableWithConfig, this->RccExecutable_, true)) {
+      return false;
+    }
+  } else {
+    if (!info.GetStringConfig("RCC_EXECUTABLE", this->RccExecutable_, true)) {
+      return false;
+    }
+  }
+
   // -- Derive information
   this->QrcFileName_ = cmSystemTools::GetFilenameName(this->QrcFile_);
   this->QrcFileDir_ = cmSystemTools::GetFilenamePath(this->QrcFile_);
-  this->RccFilePublic_ =
-    cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/',
-             this->RccFileName_);
+  if (IsMultiConfig() && !this->IsXcode()) {
+    this->RccFilePublic_ =
+      cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, "_",
+               this->InfoConfig(), '/', this->RccFileName_);
+  } else {
+    this->RccFilePublic_ =
+      cmStrCat(this->AutogenBuildDir_, '/', this->RccPathChecksum_, '/',
+               this->RccFileName_);
+  }
 
   // rcc output file name
   if (this->IsMultiConfig()) {
@@ -521,7 +546,8 @@
 
 } // End of unnamed namespace
 
-bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config)
+bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config,
+                 cm::string_view executableConfig)
 {
-  return cmQtAutoRccT().Run(infoFile, config);
+  return cmQtAutoRccT().Run(infoFile, config, executableConfig);
 }
diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h
index d525efa..9c0a4e9 100644
--- a/Source/cmQtAutoRcc.h
+++ b/Source/cmQtAutoRcc.h
@@ -10,4 +10,5 @@
  * Process AUTORCC
  * @return true on success
  */
-bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config);
+bool cmQtAutoRcc(cm::string_view infoFile, cm::string_view config,
+                 cm::string_view executableConfig);
diff --git a/Source/cmSeparateArgumentsCommand.cxx b/Source/cmSeparateArgumentsCommand.cxx
index 17285e7..3576e4f 100644
--- a/Source/cmSeparateArgumentsCommand.cxx
+++ b/Source/cmSeparateArgumentsCommand.cxx
@@ -9,6 +9,7 @@
 
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
@@ -159,7 +160,7 @@
       pos += 2;
     }
   });
-  auto value = cmJoin(values, ";");
+  auto value = cmList::to_string(values);
   status.GetMakefile().AddDefinition(var, value);
 
   return true;
diff --git a/Source/cmSetCommand.cxx b/Source/cmSetCommand.cxx
index 040eb08..c98745a 100644
--- a/Source/cmSetCommand.cxx
+++ b/Source/cmSetCommand.cxx
@@ -3,6 +3,7 @@
 #include "cmSetCommand.h"
 
 #include "cmExecutionStatus.h"
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmRange.h"
@@ -103,7 +104,8 @@
   }
 
   // collect any values into a single semi-colon separated value list
-  value = cmJoin(cmMakeRange(args).advance(1).retreat(ignoreLastArgs), ";");
+  value =
+    cmList::to_string(cmMakeRange(args).advance(1).retreat(ignoreLastArgs));
 
   if (parentScope) {
     status.GetMakefile().RaiseScope(variable, value.c_str());
@@ -113,10 +115,18 @@
   // we should be nice and try to catch some simple screwups if the last or
   // next to last args are CACHE then they screwed up.  If they used FORCE
   // without CACHE they screwed up
-  if ((args.back() == "CACHE") ||
-      (args.size() > 1 && args[args.size() - 2] == "CACHE") ||
-      (force && !cache)) {
-    status.SetError("given invalid arguments for CACHE mode.");
+  if (args.back() == "CACHE") {
+    status.SetError(
+      "given invalid arguments for CACHE mode: missing type and docstring");
+    return false;
+  }
+  if (args.size() > 1 && args[args.size() - 2] == "CACHE") {
+    status.SetError(
+      "given invalid arguments for CACHE mode: missing type or docstring");
+    return false;
+  }
+  if (force && !cache) {
+    status.SetError("given invalid arguments: FORCE specified without CACHE");
     return false;
   }
 
diff --git a/Source/cmSetPropertyCommand.cxx b/Source/cmSetPropertyCommand.cxx
index de1e3b0..60610a4 100644
--- a/Source/cmSetPropertyCommand.cxx
+++ b/Source/cmSetPropertyCommand.cxx
@@ -58,7 +58,8 @@
 bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
                     const std::string& propertyName,
                     const std::string& propertyValue, bool appendAsString,
-                    bool appendMode, bool remove);
+                    bool appendMode, bool remove,
+                    cmMakefile* test_directory_makefile);
 bool HandleTest(cmTest* test, const std::string& propertyName,
                 const std::string& propertyValue, bool appendAsString,
                 bool appendMode, bool remove);
@@ -184,6 +185,60 @@
   return scope_options_valid;
 }
 
+bool HandleTestDirectoryScopes(cmExecutionStatus& status,
+                               std::string& test_directory,
+                               cmMakefile*& directory_makefile)
+{
+  cmMakefile* current_dir_mf = &status.GetMakefile();
+  if (!test_directory.empty()) {
+    const std::string absolute_dir_path = cmSystemTools::CollapseFullPath(
+      test_directory, current_dir_mf->GetCurrentSourceDirectory());
+    cmMakefile* dir_mf =
+      status.GetMakefile().GetGlobalGenerator()->FindMakefile(
+        absolute_dir_path);
+    if (!dir_mf) {
+      status.SetError(
+        cmStrCat("given non-existent DIRECTORY ", test_directory));
+      return false;
+    }
+    directory_makefile = dir_mf;
+  } else {
+    directory_makefile = current_dir_mf;
+  }
+  return true;
+}
+
+bool HandleTestDirectoryScopeValidation(cmExecutionStatus& status,
+                                        bool test_directory_option_enabled,
+                                        std::string& test_directory)
+{
+  // Validate source file directory scopes.
+  if (test_directory_option_enabled && test_directory.empty()) {
+    std::string errors = "called with incorrect number of arguments "
+                         "no value provided to the DIRECTORY option";
+    status.SetError(errors);
+    return false;
+  }
+  return true;
+}
+
+bool HandleAndValidateTestDirectoryScopes(cmExecutionStatus& status,
+                                          bool test_directory_option_enabled,
+                                          std::string& test_directory,
+                                          cmMakefile*& test_directory_makefile)
+{
+  bool scope_options_valid =
+    SetPropertyCommand::HandleTestDirectoryScopeValidation(
+      status, test_directory_option_enabled, test_directory);
+  if (!scope_options_valid) {
+    return false;
+  }
+
+  scope_options_valid = SetPropertyCommand::HandleTestDirectoryScopes(
+    status, test_directory, test_directory_makefile);
+  return scope_options_valid;
+}
+
 std::string MakeSourceFilePathAbsoluteIfNeeded(
   cmExecutionStatus& status, const std::string& source_file_path,
   const bool needed)
@@ -293,7 +348,7 @@
         sf->AppendProperty("GENERATED", propertyValue, true);
         break;
       case PropertyOp::Remove:
-        sf->SetProperty("GENERATED", nullptr);
+        sf->RemoveProperty("GENERATED");
         break;
       case PropertyOp::Set:
         sf->SetProperty("GENERATED", propertyValue);
@@ -352,6 +407,9 @@
   bool source_file_directory_option_enabled = false;
   bool source_file_target_option_enabled = false;
 
+  std::string test_directory;
+  bool test_directory_option_enabled = false;
+
   // Parse the rest of the arguments up to the values.
   enum Doing
   {
@@ -360,7 +418,8 @@
     DoingProperty,
     DoingValues,
     DoingSourceDirectory,
-    DoingSourceTargetDirectory
+    DoingSourceTargetDirectory,
+    DoingTestDirectory,
   };
   Doing doing = DoingNames;
   const char* sep = "";
@@ -385,12 +444,19 @@
                scope == cmProperty::SOURCE_FILE && arg == "TARGET_DIRECTORY") {
       doing = DoingSourceTargetDirectory;
       source_file_target_option_enabled = true;
+    } else if (doing != DoingProperty && doing != DoingValues &&
+               scope == cmProperty::TEST && arg == "DIRECTORY") {
+      doing = DoingTestDirectory;
+      test_directory_option_enabled = true;
     } else if (doing == DoingNames) {
       names.insert(arg);
     } else if (doing == DoingSourceDirectory) {
       source_file_directories.push_back(arg);
     } else if (doing == DoingSourceTargetDirectory) {
       source_file_target_directories.push_back(arg);
+    } else if (doing == DoingTestDirectory) {
+      test_directory = arg;
+      doing = DoingNone;
     } else if (doing == DoingProperty) {
       propertyName = arg;
       doing = DoingValues;
@@ -412,12 +478,17 @@
   }
 
   std::vector<cmMakefile*> source_file_directory_makefiles;
-  bool file_scopes_handled =
+  bool source_file_scopes_handled =
     SetPropertyCommand::HandleAndValidateSourceFileDirectoryScopes(
       status, source_file_directory_option_enabled,
       source_file_target_option_enabled, source_file_directories,
       source_file_target_directories, source_file_directory_makefiles);
-  if (!file_scopes_handled) {
+  cmMakefile* test_directory_makefile;
+  bool test_scopes_handled =
+    SetPropertyCommand::HandleAndValidateTestDirectoryScopes(
+      status, test_directory_option_enabled, test_directory,
+      test_directory_makefile);
+  if (!(source_file_scopes_handled && test_scopes_handled)) {
     return false;
   }
   bool source_file_paths_should_be_absolute =
@@ -441,7 +512,8 @@
                               source_file_paths_should_be_absolute);
     case cmProperty::TEST:
       return HandleTestMode(status, names, propertyName, propertyValue,
-                            appendAsString, appendMode, remove);
+                            appendAsString, appendMode, remove,
+                            test_directory_makefile);
     case cmProperty::CACHE:
       return HandleCacheMode(status, names, propertyName, propertyValue,
                              appendAsString, appendMode, remove);
@@ -631,7 +703,7 @@
     sf->AppendProperty(propertyName, propertyValue, appendAsString);
   } else {
     if (remove) {
-      sf->SetProperty(propertyName, nullptr);
+      sf->RemoveProperty(propertyName);
     } else {
       sf->SetProperty(propertyName, propertyValue);
     }
@@ -642,14 +714,14 @@
 bool HandleTestMode(cmExecutionStatus& status, std::set<std::string>& names,
                     const std::string& propertyName,
                     const std::string& propertyValue, bool appendAsString,
-                    bool appendMode, bool remove)
+                    bool appendMode, bool remove, cmMakefile* test_makefile)
 {
   // Look for tests with all names given.
   std::set<std::string>::iterator next;
   for (auto ni = names.begin(); ni != names.end(); ni = next) {
     next = ni;
     ++next;
-    if (cmTest* test = status.GetMakefile().GetTest(*ni)) {
+    if (cmTest* test = test_makefile->GetTest(*ni)) {
       if (HandleTest(test, propertyName, propertyValue, appendAsString,
                      appendMode, remove)) {
         names.erase(ni);
diff --git a/Source/cmSetPropertyCommand.h b/Source/cmSetPropertyCommand.h
index 05c4873..4d9480d 100644
--- a/Source/cmSetPropertyCommand.h
+++ b/Source/cmSetPropertyCommand.h
@@ -33,6 +33,18 @@
   std::vector<std::string>& source_target_directories,
   std::vector<cmMakefile*>& source_file_directory_makefiles);
 
+bool HandleTestDirectoryScopes(cmExecutionStatus& status,
+                               std::string& test_directory,
+                               cmMakefile*& directory_makefile);
+
+bool HandleTestDirectoryScopeValidation(cmExecutionStatus& status,
+                                        bool test_directory_option_enabled,
+                                        std::string& test_directory);
+
+bool HandleAndValidateTestDirectoryScopes(
+  cmExecutionStatus& status, bool test_directory_option_encountered,
+  std::string& test_directory, cmMakefile*& test_directory_makefile);
+
 std::string MakeSourceFilePathAbsoluteIfNeeded(
   cmExecutionStatus& status, const std::string& source_file_path, bool needed);
 void MakeSourceFilePathsAbsoluteIfNeeded(
diff --git a/Source/cmSetTestsPropertiesCommand.cxx b/Source/cmSetTestsPropertiesCommand.cxx
index a17c964..2ac445c 100644
--- a/Source/cmSetTestsPropertiesCommand.cxx
+++ b/Source/cmSetTestsPropertiesCommand.cxx
@@ -5,9 +5,15 @@
 #include <algorithm>
 #include <iterator>
 
+#include <cmext/string_view>
+
+#include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
+#include "cmGlobalGenerator.h"
 #include "cmMakefile.h"
+#include "cmRange.h"
 #include "cmStringAlgorithms.h"
+#include "cmSystemTools.h"
 #include "cmTest.h"
 
 bool cmSetTestsPropertiesCommand(std::vector<std::string> const& args,
@@ -31,9 +37,29 @@
     return false;
   }
 
+  std::vector<std::string> tests;
+  std::string directory;
+  cmArgumentParser<void> parser;
+  parser.Bind("DIRECTORY"_s, directory);
+  auto result = parser.Parse(cmStringRange{ args.begin(), propsIter }, &tests);
+
+  cmMakefile* mf = &status.GetMakefile();
+  if (result.MaybeReportError(*mf)) {
+    return false;
+  }
+  if (!directory.empty()) {
+    std::string absDirectory = cmSystemTools::CollapseFullPath(
+      directory, mf->GetCurrentSourceDirectory());
+    mf = mf->GetGlobalGenerator()->FindMakefile(absDirectory);
+    if (!mf) {
+      status.SetError(cmStrCat("given non-existent DIRECTORY ", directory));
+      return false;
+    }
+  }
+
   // loop over all the tests
-  for (const std::string& tname : cmStringRange{ args.begin(), propsIter }) {
-    if (cmTest* test = status.GetMakefile().GetTest(tname)) {
+  for (const std::string& tname : tests) {
+    if (cmTest* test = mf->GetTest(tname)) {
       // loop through all the props and set them
       for (auto k = propsIter + 1; k != args.end(); k += 2) {
         if (!k->empty()) {
diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx
index 3403745..1be680a 100644
--- a/Source/cmSourceFile.cxx
+++ b/Source/cmSourceFile.cxx
@@ -8,6 +8,7 @@
 #include <cmext/string_view>
 
 #include "cmGlobalGenerator.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -390,7 +391,7 @@
     }
 
     static std::string output;
-    output = cmJoin(this->IncludeDirectories, ";");
+    output = cmList::to_string(this->IncludeDirectories);
     return cmValue(output);
   }
 
@@ -400,7 +401,7 @@
     }
 
     static std::string output;
-    output = cmJoin(this->CompileOptions, ";");
+    output = cmList::to_string(this->CompileOptions);
     return cmValue(output);
   }
 
@@ -410,7 +411,7 @@
     }
 
     static std::string output;
-    output = cmJoin(this->CompileDefinitions, ";");
+    output = cmList::to_string(this->CompileDefinitions);
     return cmValue(output);
   }
 
diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h
index 3f070a7..be2023f 100644
--- a/Source/cmSourceFile.h
+++ b/Source/cmSourceFile.h
@@ -4,7 +4,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <cstddef>
 #include <memory>
 #include <string>
 #include <vector>
@@ -43,7 +42,7 @@
 
   //! Set/Get a property of this source file
   void SetProperty(const std::string& prop, cmValue value);
-  void SetProperty(const std::string& prop, std::nullptr_t)
+  void RemoveProperty(const std::string& prop)
   {
     this->SetProperty(prop, cmValue{ nullptr });
   }
diff --git a/Source/cmSourceGroupCommand.cxx b/Source/cmSourceGroupCommand.cxx
index bb75a14..4b1685f 100644
--- a/Source/cmSourceGroupCommand.cxx
+++ b/Source/cmSourceGroupCommand.cxx
@@ -4,6 +4,7 @@
 
 #include <cstddef>
 #include <map>
+#include <memory>
 #include <set>
 #include <utility>
 
@@ -11,6 +12,8 @@
 
 #include "cmExecutionStatus.h"
 #include "cmMakefile.h"
+#include "cmSourceFile.h"
+#include "cmSourceFileLocation.h"
 #include "cmSourceGroup.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -194,7 +197,10 @@
 
   // If only two arguments are given, the pre-1.8 version of the
   // command is being invoked.
-  if (args.size() == 2 && args[1] != "FILES") {
+  bool isShortTreeSyntax =
+    ((args.size() == 2) && (args[0] == kTreeOptionName) &&
+     cmSystemTools::FileIsDirectory(args[1]));
+  if (args.size() == 2 && args[1] != kFilesOptionName && !isShortTreeSyntax) {
     cmSourceGroup* sg = mf.GetOrCreateSourceGroup(args[0]);
 
     if (!sg) {
@@ -274,8 +280,19 @@
     ? ""
     : parsedArguments[kPrefixOptionName].front();
 
-  const std::vector<std::string> filesVector = prepareFilesPathsForTree(
-    parsedArguments[kFilesOptionName], mf.GetCurrentSourceDirectory());
+  std::vector<std::string> files = parsedArguments[kFilesOptionName];
+  if (files.empty()) {
+    const std::vector<std::unique_ptr<cmSourceFile>>& srcFiles =
+      mf.GetSourceFiles();
+    for (const auto& srcFile : srcFiles) {
+      if (!srcFile->GetIsGenerated()) {
+        files.push_back(srcFile->GetLocation().GetFullPath());
+      }
+    }
+  }
+
+  const std::vector<std::string> filesVector =
+    prepareFilesPathsForTree(files, mf.GetCurrentSourceDirectory());
 
   if (!rootIsPrefix(root, filesVector, errorMsg)) {
     return false;
diff --git a/Source/cmStandardLevel.h b/Source/cmStandardLevel.h
new file mode 100644
index 0000000..86d178b
--- /dev/null
+++ b/Source/cmStandardLevel.h
@@ -0,0 +1,21 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+class cmStandardLevel
+{
+  size_t index_;
+
+public:
+  cmStandardLevel(size_t index)
+    : index_(index)
+  {
+  }
+  size_t Index() const { return index_; }
+  friend bool operator<(cmStandardLevel const& l, cmStandardLevel const& r)
+  {
+    return l.index_ < r.index_;
+  }
+};
diff --git a/Source/cmStandardLevelResolver.cxx b/Source/cmStandardLevelResolver.cxx
index f6e8bc6..e814690 100644
--- a/Source/cmStandardLevelResolver.cxx
+++ b/Source/cmStandardLevelResolver.cxx
@@ -13,7 +13,10 @@
 #include <vector>
 
 #include <cm/iterator>
+#include <cm/optional>
+#include <cm/string_view>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -23,6 +26,7 @@
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmPolicies.h"
+#include "cmStandardLevel.h"
 #include "cmStringAlgorithms.h"
 #include "cmTarget.h"
 #include "cmValue.h"
@@ -44,12 +48,6 @@
   FEATURE_STRING) };
 #undef FEATURE_STRING
 
-struct StandardNeeded
-{
-  int index;
-  int value;
-};
-
 int ParseStd(std::string const& level)
 {
   try {
@@ -71,6 +69,8 @@
     assert(this->Levels.size() == this->LevelsAsStrings.size());
   }
 
+  // Note that the logic here is shadowed in `GetEffectiveStandard`; if one is
+  // changed, the other needs changed as well.
   std::string GetCompileOptionDef(cmMakefile* makefile,
                                   cmGeneratorTarget const* target,
                                   std::string const& config) const
@@ -107,7 +107,7 @@
       if (cmp0128 == cmPolicies::NEW) {
         // Add extension flag if compiler's default doesn't match.
         if (ext != defaultExt) {
-          return cmStrCat("CMAKE_", this->Language, *defaultStd, "_", type,
+          return cmStrCat("CMAKE_", this->Language, *defaultStd, '_', type,
                           "_COMPILE_OPTION");
         }
       } else {
@@ -130,7 +130,7 @@
               cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0128),
                        "\nFor compatibility with older versions of CMake, "
                        "compiler extensions won't be ",
-                       state, "."));
+                       state, '.'));
           }
         }
 
@@ -144,7 +144,7 @@
 
     if (target->GetLanguageStandardRequired(this->Language)) {
       std::string option_flag = cmStrCat(
-        "CMAKE_", this->Language, *standardProp, "_", type, "_COMPILE_OPTION");
+        "CMAKE_", this->Language, *standardProp, '_', type, "_COMPILE_OPTION");
 
       cmValue opt = target->Target->GetMakefile()->GetDefinition(option_flag);
       if (!opt) {
@@ -155,8 +155,8 @@
           << this->Language << *standardProp << "\" "
           << (ext ? "(with compiler extensions)" : "")
           << ". But the current compiler \""
-          << makefile->GetSafeDefinition("CMAKE_" + this->Language +
-                                         "_COMPILER_ID")
+          << makefile->GetSafeDefinition(
+               cmStrCat("CMAKE_", this->Language, "_COMPILER_ID"))
           << "\" does not support this, or "
              "CMake does not know the flags to enable it.";
 
@@ -185,7 +185,7 @@
     }
 
     std::string standardStr(*standardProp);
-    if (this->Language == "CUDA" && standardStr == "98") {
+    if (this->Language == "CUDA"_s && standardStr == "98"_s) {
       standardStr = "03";
     }
 
@@ -194,7 +194,7 @@
     if (stdIt == cm::cend(stds)) {
       std::string e =
         cmStrCat(this->Language, "_STANDARD is set to invalid value '",
-                 standardStr, "'");
+                 standardStr, '\'');
       makefile->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e,
                                                  target->GetBacktrace());
       return std::string{};
@@ -205,7 +205,7 @@
     if (defaultStdIt == cm::cend(stds)) {
       std::string e = cmStrCat("CMAKE_", this->Language,
                                "_STANDARD_DEFAULT is set to invalid value '",
-                               *defaultStd, "'");
+                               *defaultStd, '\'');
       makefile->IssueMessage(MessageType::INTERNAL_ERROR, e);
       return std::string{};
     }
@@ -216,7 +216,7 @@
         (cmp0128 == cmPolicies::NEW &&
          (stdIt < defaultStdIt || ext != defaultExt))) {
       auto offset = std::distance(cm::cbegin(stds), stdIt);
-      return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type,
+      return cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
                       "_COMPILE_OPTION");
     }
 
@@ -226,7 +226,7 @@
     for (; defaultStdIt < stdIt; --stdIt) {
       auto offset = std::distance(cm::cbegin(stds), stdIt);
       std::string option_flag =
-        cmStrCat("CMAKE_", this->Language, stdsStrings[offset], "_", type,
+        cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
                  "_COMPILE_OPTION");
       if (target->Target->GetMakefile()->GetDefinition(option_flag)) {
         return option_flag;
@@ -236,9 +236,108 @@
     return std::string{};
   }
 
+  std::string GetEffectiveStandard(cmMakefile* makefile,
+                                   cmGeneratorTarget const* target,
+                                   std::string const& config) const
+  {
+    const auto& stds = this->Levels;
+    const auto& stdsStrings = this->LevelsAsStrings;
+
+    cmValue defaultStd = makefile->GetDefinition(
+      cmStrCat("CMAKE_", this->Language, "_STANDARD_DEFAULT"));
+    if (!cmNonempty(defaultStd)) {
+      // this compiler has no notion of language standard levels
+      return std::string{};
+    }
+
+    cmPolicies::PolicyStatus const cmp0128{ makefile->GetPolicyStatus(
+      cmPolicies::CMP0128) };
+    bool const defaultExt{ cmIsOn(*makefile->GetDefinition(
+      cmStrCat("CMAKE_", this->Language, "_EXTENSIONS_DEFAULT"))) };
+    bool ext = true;
+
+    if (cmp0128 == cmPolicies::NEW) {
+      ext = defaultExt;
+    }
+
+    if (cmValue extPropValue = target->GetLanguageExtensions(this->Language)) {
+      ext = cmIsOn(*extPropValue);
+    }
+
+    std::string const type{ ext ? "EXTENSION" : "STANDARD" };
+
+    cmValue standardProp = target->GetLanguageStandard(this->Language, config);
+    if (!standardProp) {
+      if (cmp0128 == cmPolicies::NEW) {
+        // Add extension flag if compiler's default doesn't match.
+        if (ext != defaultExt) {
+          return *defaultStd;
+        }
+      } else {
+        if (ext) {
+          return *defaultStd;
+        }
+      }
+      return std::string{};
+    }
+
+    if (target->GetLanguageStandardRequired(this->Language)) {
+      return *standardProp;
+    }
+
+    // If the request matches the compiler's defaults we don't need to add
+    // anything.
+    if (*standardProp == *defaultStd && ext == defaultExt) {
+      if (cmp0128 == cmPolicies::NEW) {
+        return std::string{};
+      }
+    }
+
+    std::string standardStr(*standardProp);
+    if (this->Language == "CUDA"_s && standardStr == "98"_s) {
+      standardStr = "03";
+    }
+
+    auto stdIt =
+      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
+    if (stdIt == cm::cend(stds)) {
+      return std::string{};
+    }
+
+    auto defaultStdIt =
+      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(*defaultStd));
+    if (defaultStdIt == cm::cend(stds)) {
+      return std::string{};
+    }
+
+    // If the standard requested is older than the compiler's default or the
+    // extension mode doesn't match then we need to use a flag.
+    if ((cmp0128 != cmPolicies::NEW && stdIt <= defaultStdIt) ||
+        (cmp0128 == cmPolicies::NEW &&
+         (stdIt < defaultStdIt || ext != defaultExt))) {
+      auto offset = std::distance(cm::cbegin(stds), stdIt);
+      return stdsStrings[offset];
+    }
+
+    // The compiler's default is at least as new as the requested standard,
+    // and the requested standard is not required.  Decay to the newest
+    // standard for which a flag is defined.
+    for (; defaultStdIt < stdIt; --stdIt) {
+      auto offset = std::distance(cm::cbegin(stds), stdIt);
+      std::string option_flag =
+        cmStrCat("CMAKE_", this->Language, stdsStrings[offset], '_', type,
+                 "_COMPILE_OPTION");
+      if (target->Target->GetMakefile()->GetDefinition(option_flag)) {
+        return stdsStrings[offset];
+      }
+    }
+
+    return std::string{};
+  }
+
   bool GetNewRequiredStandard(cmMakefile* makefile,
                               std::string const& targetName,
-                              const std::string& feature,
+                              cm::optional<cmStandardLevel> featureLevel,
                               cmValue currentLangStandardValue,
                               std::string& newRequiredStandard,
                               std::string* error) const
@@ -249,8 +348,6 @@
       newRequiredStandard.clear();
     }
 
-    auto needed = this->HighestStandardNeeded(makefile, feature);
-
     cmValue existingStandard = currentLangStandardValue;
     if (!existingStandard) {
       cmValue defaultStandard = makefile->GetDefinition(
@@ -279,12 +376,12 @@
       }
     }
 
-    if (needed.index != -1) {
+    if (featureLevel) {
       // Ensure the C++ language level is high enough to support
       // the needed C++ features.
       if (existingLevelIter == cm::cend(this->Levels) ||
-          existingLevelIter < this->Levels.begin() + needed.index) {
-        newRequiredStandard = this->LevelsAsStrings[needed.index];
+          existingLevelIter < this->Levels.begin() + featureLevel->Index()) {
+        newRequiredStandard = this->LevelsAsStrings[featureLevel->Index()];
       }
     }
 
@@ -336,29 +433,43 @@
       return false;
     }
 
-    auto needed = this->HighestStandardNeeded(makefile, feature);
+    cm::optional<cmStandardLevel> needed =
+      this->CompileFeatureStandardLevel(makefile, feature);
 
-    return (needed.index == -1) ||
-      (this->Levels.begin() + needed.index) <= existingLevelIter;
+    return !needed ||
+      (this->Levels.begin() + needed->Index()) <= existingLevelIter;
   }
 
-  StandardNeeded HighestStandardNeeded(cmMakefile* makefile,
-                                       std::string const& feature) const
+  cm::optional<cmStandardLevel> CompileFeatureStandardLevel(
+    cmMakefile* makefile, std::string const& feature) const
   {
     std::string prefix = cmStrCat("CMAKE_", this->Language);
-    StandardNeeded maxLevel = { -1, -1 };
+    cm::optional<cmStandardLevel> maxLevel;
     for (size_t i = 0; i < this->Levels.size(); ++i) {
       if (cmValue prop = makefile->GetDefinition(
             cmStrCat(prefix, this->LevelsAsStrings[i], "_COMPILE_FEATURES"))) {
         cmList props{ *prop };
         if (cm::contains(props, feature)) {
-          maxLevel = { static_cast<int>(i), this->Levels[i] };
+          maxLevel = cmStandardLevel(i);
         }
       }
     }
     return maxLevel;
   }
 
+  cm::optional<cmStandardLevel> LanguageStandardLevel(
+    std::string const& standardStr) const
+  {
+    cm::optional<cmStandardLevel> langLevel;
+    auto const& stds = this->Levels;
+    auto stdIt =
+      std::find(cm::cbegin(stds), cm::cend(stds), ParseStd(standardStr));
+    if (stdIt != cm::cend(stds)) {
+      langLevel = cmStandardLevel(std::distance(cm::cbegin(stds), stdIt));
+    }
+    return langLevel;
+  }
+
   bool IsLaterStandard(int lhs, int rhs) const
   {
     auto rhsIt =
@@ -373,37 +484,33 @@
   std::vector<std::string> LevelsAsStrings;
 };
 
-std::unordered_map<std::string, StandardLevelComputer>
-  StandardComputerMapping = {
-    { "C",
-      StandardLevelComputer{
-        "C", std::vector<int>{ 90, 99, 11, 17, 23 },
-        std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
-    { "CXX",
-      StandardLevelComputer{ "CXX",
-                             std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
-                             std::vector<std::string>{ "98", "11", "14", "17",
-                                                       "20", "23", "26" } } },
-    { "CUDA",
-      StandardLevelComputer{ "CUDA",
-                             std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 },
-                             std::vector<std::string>{ "03", "11", "14", "17",
-                                                       "20", "23", "26" } } },
-    { "OBJC",
-      StandardLevelComputer{
-        "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
-        std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
-    { "OBJCXX",
-      StandardLevelComputer{ "OBJCXX",
-                             std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
-                             std::vector<std::string>{ "98", "11", "14", "17",
-                                                       "20", "23", "26" } } },
-    { "HIP",
-      StandardLevelComputer{ "HIP",
-                             std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
-                             std::vector<std::string>{ "98", "11", "14", "17",
-                                                       "20", "23", "26" } } }
-  };
+std::unordered_map<std::string,
+                   StandardLevelComputer> const StandardComputerMapping = {
+  { "C",
+    StandardLevelComputer{
+      "C", std::vector<int>{ 90, 99, 11, 17, 23 },
+      std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
+  { "CXX",
+    StandardLevelComputer{
+      "CXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
+      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
+  { "CUDA",
+    StandardLevelComputer{
+      "CUDA", std::vector<int>{ 03, 11, 14, 17, 20, 23, 26 },
+      std::vector<std::string>{ "03", "11", "14", "17", "20", "23", "26" } } },
+  { "OBJC",
+    StandardLevelComputer{
+      "OBJC", std::vector<int>{ 90, 99, 11, 17, 23 },
+      std::vector<std::string>{ "90", "99", "11", "17", "23" } } },
+  { "OBJCXX",
+    StandardLevelComputer{
+      "OBJCXX", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
+      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } },
+  { "HIP",
+    StandardLevelComputer{
+      "HIP", std::vector<int>{ 98, 11, 14, 17, 20, 23, 26 },
+      std::vector<std::string>{ "98", "11", "14", "17", "20", "23", "26" } } }
+};
 }
 
 std::string cmStandardLevelResolver::GetCompileOptionDef(
@@ -418,6 +525,18 @@
   return mapping->second.GetCompileOptionDef(this->Makefile, target, config);
 }
 
+std::string cmStandardLevelResolver::GetEffectiveStandard(
+  cmGeneratorTarget const* target, std::string const& lang,
+  std::string const& config) const
+{
+  const auto& mapping = StandardComputerMapping.find(lang);
+  if (mapping == cm::cend(StandardComputerMapping)) {
+    return std::string{};
+  }
+
+  return mapping->second.GetEffectiveStandard(this->Makefile, target, config);
+}
+
 bool cmStandardLevelResolver::AddRequiredTargetFeature(
   cmTarget* target, const std::string& feature, std::string* error) const
 {
@@ -441,15 +560,16 @@
   // should be done purely at generate time based on whatever the project
   // code put in these properties explicitly.  That is mostly true now,
   // but for compatibility we need to continue updating the property here.
+  cm::optional<cmStandardLevel> featureLevel;
   std::string newRequiredStandard;
-  bool newRequired = this->GetNewRequiredStandard(
+  bool succeeded = this->GetNewRequiredStandard(
     target->GetName(), feature,
-    target->GetProperty(cmStrCat(lang, "_STANDARD")), newRequiredStandard,
-    error);
+    target->GetProperty(cmStrCat(lang, "_STANDARD")), featureLevel,
+    newRequiredStandard, error);
   if (!newRequiredStandard.empty()) {
     target->SetProperty(cmStrCat(lang, "_STANDARD"), newRequiredStandard);
   }
-  return newRequired;
+  return succeeded;
 }
 
 bool cmStandardLevelResolver::CheckCompileFeaturesAvailable(
@@ -474,11 +594,12 @@
     std::ostringstream e;
     e << "The compiler feature \"" << feature << "\" is not known to " << lang
       << " compiler\n\""
-      << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
+      << this->Makefile->GetSafeDefinition(
+           cmStrCat("CMAKE_", lang, "_COMPILER_ID"))
       << "\"\nversion "
-      << this->Makefile->GetSafeDefinition("CMAKE_" + lang +
-                                           "_COMPILER_VERSION")
-      << ".";
+      << this->Makefile->GetSafeDefinition(
+           cmStrCat("CMAKE_", lang, "_COMPILER_VERSION"))
+      << '.';
     if (error) {
       *error = e.str();
     } else {
@@ -542,6 +663,27 @@
   return false;
 }
 
+cm::optional<cmStandardLevel>
+cmStandardLevelResolver::CompileFeatureStandardLevel(
+  std::string const& lang, std::string const& feature) const
+{
+  auto mapping = StandardComputerMapping.find(lang);
+  if (mapping == cm::cend(StandardComputerMapping)) {
+    return cm::nullopt;
+  }
+  return mapping->second.CompileFeatureStandardLevel(this->Makefile, feature);
+}
+
+cm::optional<cmStandardLevel> cmStandardLevelResolver::LanguageStandardLevel(
+  std::string const& lang, std::string const& standardStr) const
+{
+  auto mapping = StandardComputerMapping.find(lang);
+  if (mapping == cm::cend(StandardComputerMapping)) {
+    return cm::nullopt;
+  }
+  return mapping->second.LanguageStandardLevel(standardStr);
+}
+
 cmValue cmStandardLevelResolver::CompileFeaturesAvailable(
   const std::string& lang, std::string* error) const
 {
@@ -561,8 +703,8 @@
     return nullptr;
   }
 
-  cmValue featuresKnown =
-    this->Makefile->GetDefinition("CMAKE_" + lang + "_COMPILE_FEATURES");
+  cmValue featuresKnown = this->Makefile->GetDefinition(
+    cmStrCat("CMAKE_", lang, "_COMPILE_FEATURES"));
 
   if (!cmNonempty(featuresKnown)) {
     std::ostringstream e;
@@ -572,11 +714,12 @@
       e << "No";
     }
     e << " known features for " << lang << " compiler\n\""
-      << this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER_ID")
+      << this->Makefile->GetSafeDefinition(
+           cmStrCat("CMAKE_", lang, "_COMPILER_ID"))
       << "\"\nversion "
-      << this->Makefile->GetSafeDefinition("CMAKE_" + lang +
-                                           "_COMPILER_VERSION")
-      << ".";
+      << this->Makefile->GetSafeDefinition(
+           cmStrCat("CMAKE_", lang, "_COMPILER_VERSION"))
+      << '.';
     if (error) {
       *error = e.str();
     } else {
@@ -589,18 +732,21 @@
 
 bool cmStandardLevelResolver::GetNewRequiredStandard(
   const std::string& targetName, const std::string& feature,
-  cmValue currentLangStandardValue, std::string& newRequiredStandard,
-  std::string* error) const
+  cmValue currentLangStandardValue,
+  cm::optional<cmStandardLevel>& featureLevel,
+  std::string& newRequiredStandard, std::string* error) const
 {
   std::string lang;
   if (!this->CheckCompileFeaturesAvailable(targetName, feature, lang, error)) {
     return false;
   }
 
+  featureLevel = this->CompileFeatureStandardLevel(lang, feature);
+
   auto mapping = StandardComputerMapping.find(lang);
   if (mapping != cm::cend(StandardComputerMapping)) {
     return mapping->second.GetNewRequiredStandard(
-      this->Makefile, targetName, feature, currentLangStandardValue,
+      this->Makefile, targetName, featureLevel, currentLangStandardValue,
       newRequiredStandard, error);
   }
   return false;
diff --git a/Source/cmStandardLevelResolver.h b/Source/cmStandardLevelResolver.h
index 4226456..29cab55 100644
--- a/Source/cmStandardLevelResolver.h
+++ b/Source/cmStandardLevelResolver.h
@@ -4,10 +4,13 @@
 
 #include <string>
 
+#include <cm/optional>
+
 #include "cmValue.h"
 
 class cmMakefile;
 class cmGeneratorTarget;
+class cmStandardLevel;
 class cmTarget;
 
 class cmStandardLevelResolver
@@ -22,6 +25,9 @@
   std::string GetCompileOptionDef(cmGeneratorTarget const* target,
                                   std::string const& lang,
                                   std::string const& config) const;
+  std::string GetEffectiveStandard(cmGeneratorTarget const* target,
+                                   std::string const& lang,
+                                   std::string const& config) const;
 
   bool AddRequiredTargetFeature(cmTarget* target, const std::string& feature,
                                 std::string* error = nullptr) const;
@@ -30,12 +36,19 @@
                            const std::string& feature, std::string& lang,
                            std::string* error) const;
 
+  cm::optional<cmStandardLevel> CompileFeatureStandardLevel(
+    std::string const& lang, std::string const& feature) const;
+
+  cm::optional<cmStandardLevel> LanguageStandardLevel(
+    std::string const& lang, std::string const& standardStr) const;
+
   cmValue CompileFeaturesAvailable(const std::string& lang,
                                    std::string* error) const;
 
   bool GetNewRequiredStandard(const std::string& targetName,
                               const std::string& feature,
                               cmValue currentLangStandardValue,
+                              cm::optional<cmStandardLevel>& featureLevel,
                               std::string& newRequiredStandard,
                               std::string* error = nullptr) const;
 
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index 2596d8c..d41e8e5 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -17,6 +17,7 @@
 #include "cmDefinitions.h"
 #include "cmExecutionStatus.h"
 #include "cmGlobVerificationManager.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -584,10 +585,10 @@
 {
   if (prop == "CACHE_VARIABLES") {
     std::vector<std::string> cacheKeys = this->GetCacheEntryKeys();
-    this->SetGlobalProperty("CACHE_VARIABLES", cmJoin(cacheKeys, ";"));
+    this->SetGlobalProperty("CACHE_VARIABLES", cmList::to_string(cacheKeys));
   } else if (prop == "COMMANDS") {
     std::vector<std::string> commands = this->GetCommandNames();
-    this->SetGlobalProperty("COMMANDS", cmJoin(commands, ";"));
+    this->SetGlobalProperty("COMMANDS", cmList::to_string(commands));
   } else if (prop == "IN_TRY_COMPILE") {
     this->SetGlobalProperty(
       "IN_TRY_COMPILE",
@@ -596,8 +597,7 @@
     this->SetGlobalProperty("GENERATOR_IS_MULTI_CONFIG",
                             this->IsGeneratorMultiConfig ? "1" : "0");
   } else if (prop == "ENABLED_LANGUAGES") {
-    std::string langs;
-    langs = cmJoin(this->EnabledLanguages, ";");
+    auto langs = cmList::to_string(this->EnabledLanguages);
     this->SetGlobalProperty("ENABLED_LANGUAGES", langs);
   } else if (prop == "CMAKE_ROLE") {
     std::string mode = this->GetModeString();
diff --git a/Source/cmState.h b/Source/cmState.h
index 1ebd79a..b79f3e6 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -8,7 +8,6 @@
 #include <memory>
 #include <set>
 #include <string>
-#include <type_traits>
 #include <unordered_map>
 #include <unordered_set>
 #include <utility>
diff --git a/Source/cmStateDirectory.cxx b/Source/cmStateDirectory.cxx
index 6e6fcbd..39353f3 100644
--- a/Source/cmStateDirectory.cxx
+++ b/Source/cmStateDirectory.cxx
@@ -13,6 +13,7 @@
 #include <cmext/string_view>
 
 #include "cmAlgorithms.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmProperty.h"
 #include "cmPropertyMap.h"
@@ -20,7 +21,6 @@
 #include "cmState.h"
 #include "cmStatePrivate.h"
 #include "cmStateTypes.h"
-#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmValue.h"
 
@@ -381,15 +381,15 @@
     for (cmStateSnapshot const& ci : children) {
       child_dirs.push_back(ci.GetDirectory().GetCurrentSource());
     }
-    output = cmJoin(child_dirs, ";");
+    output = cmList::to_string(child_dirs);
     return cmValue(output);
   }
   if (prop == kBUILDSYSTEM_TARGETS) {
-    output = cmJoin(this->DirectoryState->NormalTargetNames, ";");
+    output = cmList::to_string(this->DirectoryState->NormalTargetNames);
     return cmValue(output);
   }
   if (prop == "IMPORTED_TARGETS"_s) {
-    output = cmJoin(this->DirectoryState->ImportedTargetNames, ";");
+    output = cmList::to_string(this->DirectoryState->ImportedTargetNames);
     return cmValue(output);
   }
 
@@ -401,38 +401,38 @@
       snp = snp.GetCallStackParent();
     }
     std::reverse(listFiles.begin(), listFiles.end());
-    output = cmJoin(listFiles, ";");
+    output = cmList::to_string(listFiles);
     return cmValue(output);
   }
   if (prop == "CACHE_VARIABLES") {
-    output = cmJoin(this->Snapshot_.State->GetCacheEntryKeys(), ";");
+    output = cmList::to_string(this->Snapshot_.State->GetCacheEntryKeys());
     return cmValue(output);
   }
   if (prop == "VARIABLES") {
     std::vector<std::string> res = this->Snapshot_.ClosureKeys();
     cm::append(res, this->Snapshot_.State->GetCacheEntryKeys());
     std::sort(res.begin(), res.end());
-    output = cmJoin(res, ";");
+    output = cmList::to_string(res);
     return cmValue(output);
   }
   if (prop == "INCLUDE_DIRECTORIES") {
-    output = cmJoin(this->GetIncludeDirectoriesEntries(), ";");
+    output = cmList::to_string(this->GetIncludeDirectoriesEntries());
     return cmValue(output);
   }
   if (prop == "COMPILE_OPTIONS") {
-    output = cmJoin(this->GetCompileOptionsEntries(), ";");
+    output = cmList::to_string(this->GetCompileOptionsEntries());
     return cmValue(output);
   }
   if (prop == "COMPILE_DEFINITIONS") {
-    output = cmJoin(this->GetCompileDefinitionsEntries(), ";");
+    output = cmList::to_string(this->GetCompileDefinitionsEntries());
     return cmValue(output);
   }
   if (prop == "LINK_OPTIONS") {
-    output = cmJoin(this->GetLinkOptionsEntries(), ";");
+    output = cmList::to_string(this->GetLinkOptionsEntries());
     return cmValue(output);
   }
   if (prop == "LINK_DIRECTORIES") {
-    output = cmJoin(this->GetLinkDirectoriesEntries(), ";");
+    output = cmList::to_string(this->GetLinkDirectoriesEntries());
     return cmValue(output);
   }
 
diff --git a/Source/cmStateSnapshot.cxx b/Source/cmStateSnapshot.cxx
index e230702..8217a9c 100644
--- a/Source/cmStateSnapshot.cxx
+++ b/Source/cmStateSnapshot.cxx
@@ -10,6 +10,7 @@
 #include <cm/iterator>
 
 #include "cmDefinitions.h"
+#include "cmLinkedTree.h"
 #include "cmListFileCache.h"
 #include "cmPropertyMap.h"
 #include "cmState.h"
diff --git a/Source/cmStateSnapshot.h b/Source/cmStateSnapshot.h
index a61ec83..1796d76 100644
--- a/Source/cmStateSnapshot.h
+++ b/Source/cmStateSnapshot.h
@@ -10,7 +10,6 @@
 
 #include <cm/string_view>
 
-#include "cmLinkedTree.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmValue.h"
diff --git a/Source/cmString.hxx b/Source/cmString.hxx
index f1e462b..1994c2c 100644
--- a/Source/cmString.hxx
+++ b/Source/cmString.hxx
@@ -6,7 +6,6 @@
 
 #include <algorithm>
 #include <cstddef>
-#include <functional>
 #include <initializer_list>
 #include <memory>
 #include <ostream>
@@ -494,8 +493,8 @@
                   char ch)
   {
     std::string out;
-    out.reserve((first - this->view_.begin()) + count2 +
-                (this->view_.end() - last));
+    out.reserve(static_cast<size_type>(first - this->view_.begin()) + count2 +
+                static_cast<size_type>(this->view_.end() - last));
     out.append(this->view_.begin(), first);
     out.append(count2, ch);
     out.append(last, this->view_.end());
diff --git a/Source/cmSyntheticTargetCache.h b/Source/cmSyntheticTargetCache.h
new file mode 100644
index 0000000..22d1533
--- /dev/null
+++ b/Source/cmSyntheticTargetCache.h
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <map>
+#include <string>
+
+class cmGeneratorTarget;
+
+struct cmSyntheticTargetCache
+{
+  std::map<std::string, cmGeneratorTarget const*> CxxModuleTargets;
+};
diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx
index 1fb0079..fe421ba 100644
--- a/Source/cmSystemTools.cxx
+++ b/Source/cmSystemTools.cxx
@@ -25,12 +25,17 @@
 
 #include <cm3p/uv.h>
 
+#include "cm_fileno.hxx"
+
 #include "cmDuration.h"
 #include "cmELF.h"
 #include "cmMessageMetadata.h"
 #include "cmProcessOutput.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
@@ -47,13 +52,6 @@
 #  endif
 #endif
 
-#if !defined(CMAKE_BOOTSTRAP)
-#  if defined(_WIN32)
-#    include <cm/memory>
-#  endif
-#  include "cmCryptoHash.h"
-#endif
-
 #if defined(CMake_USE_MACH_PARSER)
 #  include "cmMachO.h"
 #endif
@@ -66,12 +64,14 @@
 #include <cassert>
 #include <cctype>
 #include <cerrno>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
 #include <functional>
 #include <iostream>
+#include <memory>
 #include <sstream>
 #include <utility>
 #include <vector>
@@ -529,7 +529,7 @@
   return !in_single && !in_escape && !in_double;
 }
 
-size_t cmSystemTools::CalculateCommandLineLengthLimit()
+std::size_t cmSystemTools::CalculateCommandLineLengthLimit()
 {
   size_t sz =
 #ifdef _WIN32
@@ -575,85 +575,113 @@
                                      const char* dir, OutputOption outputflag,
                                      cmDuration timeout, Encoding encoding)
 {
-  std::vector<const char*> argv;
-  argv.reserve(command.size() + 1);
-  for (std::string const& cmd : command) {
-    argv.push_back(cmd.c_str());
-  }
-  argv.push_back(nullptr);
-
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  cmsysProcess_SetWorkingDirectory(cp, dir);
-  if (cmSystemTools::GetRunCommandHideConsole()) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(command);
+  if (dir) {
+    builder.SetWorkingDirectory(dir);
   }
 
   if (outputflag == OUTPUT_PASSTHROUGH) {
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
-    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
     captureStdOut = nullptr;
     captureStdErr = nullptr;
+    builder
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                         cm_fileno(stdout))
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                         cm_fileno(stderr));
   } else if (outputflag == OUTPUT_MERGE ||
              (captureStdErr && captureStdErr == captureStdOut)) {
-    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
+    builder.SetMergedBuiltinStreams();
     captureStdErr = nullptr;
+  } else {
+    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
   }
   assert(!captureStdErr || captureStdErr != captureStdOut);
 
-  cmsysProcess_SetTimeout(cp, timeout.count());
-  cmsysProcess_Execute(cp);
+  auto chain = builder.Start();
+  bool timedOut = false;
+  cm::uv_timer_ptr timer;
+  if (timeout.count()) {
+    timer.init(chain.GetLoop(), &timedOut);
+    timer.start(
+      [](uv_timer_t* t) {
+        auto* timedOutPtr = static_cast<bool*>(t->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+  }
 
   std::vector<char> tempStdOut;
   std::vector<char> tempStdErr;
-  char* data;
-  int length;
-  int pipe;
+  cm::uv_pipe_ptr outStream;
+  bool outFinished = true;
+  cm::uv_pipe_ptr errStream;
+  bool errFinished = true;
   cmProcessOutput processOutput(encoding);
-  std::string strdata;
+  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
+  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
   if (outputflag != OUTPUT_PASSTHROUGH &&
       (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
-    while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
-           0) {
-      // Translate NULL characters in the output into valid text.
-      for (int i = 0; i < length; ++i) {
-        if (data[i] == '\0') {
-          data[i] = ' ';
-        }
+    auto startRead =
+      [&outputflag, &processOutput,
+       &chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd,
+               std::vector<char>& tempStd, int id,
+               void (*outputFunc)(const std::string&),
+               bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
+      if (stream < 0) {
+        return nullptr;
       }
 
-      if (pipe == cmsysProcess_Pipe_STDOUT) {
-        if (outputflag != OUTPUT_NONE) {
-          processOutput.DecodeText(data, length, strdata, 1);
-          cmSystemTools::Stdout(strdata);
-        }
-        if (captureStdOut) {
-          cm::append(tempStdOut, data, data + length);
-        }
-      } else if (pipe == cmsysProcess_Pipe_STDERR) {
-        if (outputflag != OUTPUT_NONE) {
-          processOutput.DecodeText(data, length, strdata, 2);
-          cmSystemTools::Stderr(strdata);
-        }
-        if (captureStdErr) {
-          cm::append(tempStdErr, data, data + length);
-        }
-      }
-    }
+      pipe.init(chain.GetLoop(), 0);
+      uv_pipe_open(pipe, stream);
 
-    if (outputflag != OUTPUT_NONE) {
-      processOutput.DecodeText(std::string(), strdata, 1);
-      if (!strdata.empty()) {
-        cmSystemTools::Stdout(strdata);
-      }
-      processOutput.DecodeText(std::string(), strdata, 2);
-      if (!strdata.empty()) {
-        cmSystemTools::Stderr(strdata);
-      }
+      finished = false;
+      return cmUVStreamRead(
+        pipe,
+        [outputflag, &processOutput, captureStd, &tempStd, id,
+         outputFunc](std::vector<char> data) {
+          // Translate NULL characters in the output into valid text.
+          for (auto& c : data) {
+            if (c == '\0') {
+              c = ' ';
+            }
+          }
+
+          if (outputflag != OUTPUT_NONE) {
+            std::string strdata;
+            processOutput.DecodeText(data.data(), data.size(), strdata, id);
+            outputFunc(strdata);
+          }
+          if (captureStd) {
+            cm::append(tempStd, data.data(), data.data() + data.size());
+          }
+        },
+        [&finished, outputflag, &processOutput, id, outputFunc]() {
+          finished = true;
+          if (outputflag != OUTPUT_NONE) {
+            std::string strdata;
+            processOutput.DecodeText(std::string(), strdata, id);
+            if (!strdata.empty()) {
+              outputFunc(strdata);
+            }
+          }
+        });
+    };
+
+    outputHandle =
+      startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1,
+                cmSystemTools::Stdout, outFinished);
+    if (chain.OutputStream() != chain.ErrorStream()) {
+      errorHandle =
+        startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2,
+                  cmSystemTools::Stderr, errFinished);
     }
   }
 
-  cmsysProcess_WaitForExit(cp, nullptr);
+  while (!timedOut && !(chain.Finished() && outFinished && errFinished)) {
+    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  }
 
   if (captureStdOut) {
     captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
@@ -665,37 +693,7 @@
   }
 
   bool result = true;
-  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
-    if (retVal) {
-      *retVal = cmsysProcess_GetExitValue(cp);
-    } else {
-      if (cmsysProcess_GetExitValue(cp) != 0) {
-        result = false;
-      }
-    }
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
-    const char* exception_str = cmsysProcess_GetExceptionString(cp);
-    if (outputflag != OUTPUT_NONE) {
-      std::cerr << exception_str << std::endl;
-    }
-    if (captureStdErr) {
-      captureStdErr->append(exception_str, strlen(exception_str));
-    } else if (captureStdOut) {
-      captureStdOut->append(exception_str, strlen(exception_str));
-    }
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
-    const char* error_str = cmsysProcess_GetErrorString(cp);
-    if (outputflag != OUTPUT_NONE) {
-      std::cerr << error_str << std::endl;
-    }
-    if (captureStdErr) {
-      captureStdErr->append(error_str, strlen(error_str));
-    } else if (captureStdOut) {
-      captureStdOut->append(error_str, strlen(error_str));
-    }
-    result = false;
-  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
+  if (timedOut) {
     const char* error_str = "Process terminated due to timeout\n";
     if (outputflag != OUTPUT_NONE) {
       std::cerr << error_str << std::endl;
@@ -704,9 +702,34 @@
       captureStdErr->append(error_str, strlen(error_str));
     }
     result = false;
+  } else {
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (retVal) {
+          *retVal = static_cast<int>(status.ExitStatus);
+        } else {
+          if (status.ExitStatus != 0) {
+            result = false;
+          }
+        }
+        break;
+      default: {
+        if (outputflag != OUTPUT_NONE) {
+          std::cerr << exception.second << std::endl;
+        }
+        if (captureStdErr) {
+          captureStdErr->append(exception.second);
+        } else if (captureStdOut) {
+          captureStdOut->append(exception.second);
+        }
+        result = false;
+      } break;
+    }
   }
 
-  cmsysProcess_Delete(cp);
   return result;
 }
 
@@ -1308,89 +1331,6 @@
   RemoveFile(source);
 }
 
-#ifndef CMAKE_BOOTSTRAP
-std::string cmSystemTools::ComputeFileHash(const std::string& source,
-                                           cmCryptoHash::Algo algo)
-{
-  cmCryptoHash hash(algo);
-  return hash.HashFile(source);
-}
-
-std::string cmSystemTools::ComputeStringMD5(const std::string& input)
-{
-  cmCryptoHash md5(cmCryptoHash::AlgoMD5);
-  return md5.HashString(input);
-}
-
-#  ifdef _WIN32
-std::string cmSystemTools::ComputeCertificateThumbprint(
-  const std::string& source)
-{
-  std::string thumbprint;
-
-  CRYPT_INTEGER_BLOB cryptBlob;
-  HCERTSTORE certStore = nullptr;
-  PCCERT_CONTEXT certContext = nullptr;
-
-  HANDLE certFile = CreateFileW(
-    cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ,
-    FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
-
-  if (certFile != INVALID_HANDLE_VALUE && certFile != nullptr) {
-    DWORD fileSize = GetFileSize(certFile, nullptr);
-    if (fileSize != INVALID_FILE_SIZE) {
-      auto certData = cm::make_unique<BYTE[]>(fileSize);
-      if (certData != nullptr) {
-        DWORD dwRead = 0;
-        if (ReadFile(certFile, certData.get(), fileSize, &dwRead, nullptr)) {
-          cryptBlob.cbData = fileSize;
-          cryptBlob.pbData = certData.get();
-
-          // Verify that this is a valid cert
-          if (PFXIsPFXBlob(&cryptBlob)) {
-            // Open the certificate as a store
-            certStore =
-              PFXImportCertStore(&cryptBlob, nullptr, CRYPT_EXPORTABLE);
-            if (certStore != nullptr) {
-              // There should only be 1 cert.
-              certContext =
-                CertEnumCertificatesInStore(certStore, certContext);
-              if (certContext != nullptr) {
-                // The hash is 20 bytes
-                BYTE hashData[20];
-                DWORD hashLength = 20;
-
-                // Buffer to print the hash. Each byte takes 2 chars +
-                // terminating character
-                char hashPrint[41];
-                char* pHashPrint = hashPrint;
-                // Get the hash property from the certificate
-                if (CertGetCertificateContextProperty(
-                      certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) {
-                  for (DWORD i = 0; i < hashLength; i++) {
-                    // Convert each byte to hexadecimal
-                    snprintf(pHashPrint, 3, "%02X", hashData[i]);
-                    pHashPrint += 2;
-                  }
-                  *pHashPrint = '\0';
-                  thumbprint = hashPrint;
-                }
-                CertFreeCertificateContext(certContext);
-              }
-              CertCloseStore(certStore, 0);
-            }
-          }
-        }
-      }
-    }
-    CloseHandle(certFile);
-  }
-
-  return thumbprint;
-}
-#  endif
-#endif
-
 void cmSystemTools::Glob(const std::string& directory,
                          const std::string& regexp,
                          std::vector<std::string>& files)
@@ -1900,6 +1840,12 @@
           cmHasLiteralSuffix(path, ".framework"));
 }
 
+bool cmSystemTools::IsPathToXcFramework(const std::string& path)
+{
+  return (cmSystemTools::FileIsFullPath(path) &&
+          cmHasLiteralSuffix(path, ".xcframework"));
+}
+
 bool cmSystemTools::IsPathToMacOSSharedLibrary(const std::string& path)
 {
   return (cmSystemTools::FileIsFullPath(path) &&
@@ -2297,9 +2243,10 @@
 #endif
 }
 
-int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
-                               cmDuration timeout, std::vector<char>& out,
-                               std::vector<char>& err)
+cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
+  uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe,
+  std::string& line, cmDuration timeout, std::vector<char>& out,
+  std::vector<char>& err)
 {
   line.clear();
   auto outiter = out.begin();
@@ -2321,7 +2268,7 @@
           line.append(out.data(), length);
         }
         out.erase(out.begin(), outiter + 1);
-        return cmsysProcess_Pipe_STDOUT;
+        return WaitForLineResult::STDOUT;
       }
     }
 
@@ -2339,33 +2286,66 @@
           line.append(err.data(), length);
         }
         err.erase(err.begin(), erriter + 1);
-        return cmsysProcess_Pipe_STDERR;
+        return WaitForLineResult::STDERR;
       }
     }
 
     // No newlines found.  Wait for more data from the process.
-    int length;
-    char* data;
-    double timeoutAsDbl = timeout.count();
-    int pipe =
-      cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
-    if (pipe == cmsysProcess_Pipe_Timeout) {
+    struct ReadData
+    {
+      uv_stream_t* Stream;
+      std::vector<char> Buffer;
+      bool Read = false;
+      bool Finished = false;
+    };
+    auto startRead =
+      [](uv_stream_t* stream,
+         ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
+      data.Stream = stream;
+      return cmUVStreamRead(
+        stream,
+        [&data](std::vector<char> buf) {
+          data.Buffer = std::move(buf);
+          data.Read = true;
+          uv_read_stop(data.Stream);
+        },
+        [&data]() { data.Finished = true; });
+    };
+    ReadData outData;
+    auto outHandle = startRead(outPipe, outData);
+    ReadData errData;
+    auto errHandle = startRead(errPipe, errData);
+
+    cm::uv_timer_ptr timer;
+    bool timedOut = false;
+    timer.init(*loop, &timedOut);
+    timer.start(
+      [](uv_timer_t* handle) {
+        auto* timedOutPtr = static_cast<bool*>(handle->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+
+    uv_run(loop, UV_RUN_ONCE);
+    if (timedOut) {
       // Timeout has been exceeded.
-      return pipe;
+      return WaitForLineResult::Timeout;
     }
-    if (pipe == cmsysProcess_Pipe_STDOUT) {
-      processOutput.DecodeText(data, length, strdata, 1);
+    if (outData.Read) {
+      processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
+                               strdata, 1);
       // Append to the stdout buffer.
       std::vector<char>::size_type size = out.size();
       cm::append(out, strdata);
       outiter = out.begin() + size;
-    } else if (pipe == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 2);
+    } else if (errData.Read) {
+      processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
+                               strdata, 2);
       // Append to the stderr buffer.
       std::vector<char>::size_type size = err.size();
       cm::append(err, strdata);
       erriter = err.begin() + size;
-    } else if (pipe == cmsysProcess_Pipe_None) {
+    } else if (outData.Finished && errData.Finished) {
       // Both stdout and stderr pipes have broken.  Return leftover data.
       processOutput.DecodeText(std::string(), strdata, 1);
       if (!strdata.empty()) {
@@ -2382,14 +2362,20 @@
       if (!out.empty()) {
         line.append(out.data(), outiter - out.begin());
         out.erase(out.begin(), out.end());
-        return cmsysProcess_Pipe_STDOUT;
+        return WaitForLineResult::STDOUT;
       }
       if (!err.empty()) {
         line.append(err.data(), erriter - err.begin());
         err.erase(err.begin(), err.end());
-        return cmsysProcess_Pipe_STDERR;
+        return WaitForLineResult::STDERR;
       }
-      return cmsysProcess_Pipe_None;
+      return WaitForLineResult::None;
+    }
+    if (!outData.Finished) {
+      uv_read_stop(outPipe);
+    }
+    if (!errData.Finished) {
+      uv_read_stop(errPipe);
     }
   }
 }
diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h
index 7d55d4b..7e33e58 100644
--- a/Source/cmSystemTools.h
+++ b/Source/cmSystemTools.h
@@ -18,11 +18,11 @@
 #include <cm/optional>
 #include <cm/string_view>
 
-#include "cmsys/Process.h"
+#include <cm3p/uv.h>
+
 #include "cmsys/Status.hxx"      // IWYU pragma: export
 #include "cmsys/SystemTools.hxx" // IWYU pragma: export
 
-#include "cmCryptoHash.h"
 #include "cmDuration.h"
 #include "cmProcessOutput.h"
 
@@ -111,6 +111,9 @@
   //! Return true if the path is a framework
   static bool IsPathToFramework(const std::string& path);
 
+  //! Return true if the path is a xcframework
+  static bool IsPathToXcFramework(const std::string& path);
+
   //! Return true if the path is a macOS non-framework shared library (aka
   //! .dylib)
   static bool IsPathToMacOSSharedLibrary(const std::string& path);
@@ -211,20 +214,6 @@
   static void MoveFileIfDifferent(const std::string& source,
                                   const std::string& destination);
 
-#ifndef CMAKE_BOOTSTRAP
-  //! Compute the hash of a file
-  static std::string ComputeFileHash(const std::string& source,
-                                     cmCryptoHash::Algo algo);
-
-  /** Compute the md5sum of a string.  */
-  static std::string ComputeStringMD5(const std::string& input);
-
-#  ifdef _WIN32
-  //! Get the SHA thumbprint for a certificate file
-  static std::string ComputeCertificateThumbprint(const std::string& source);
-#  endif
-#endif
-
   /**
    * Run a single executable command
    *
@@ -302,7 +291,7 @@
     std::vector<std::string>::const_iterator argBeg,
     std::vector<std::string>::const_iterator argEnd);
 
-  static size_t CalculateCommandLineLengthLimit();
+  static std::size_t CalculateCommandLineLengthLimit();
 
   static void DisableRunCommandOutput() { s_DisableRunCommandOutput = true; }
   static void EnableRunCommandOutput() { s_DisableRunCommandOutput = false; }
@@ -351,10 +340,20 @@
    */
   static void ReportLastSystemError(const char* m);
 
-  /** a general output handler for cmsysProcess  */
-  static int WaitForLine(cmsysProcess* process, std::string& line,
-                         cmDuration timeout, std::vector<char>& out,
-                         std::vector<char>& err);
+  enum class WaitForLineResult
+  {
+    None,
+    STDOUT,
+    STDERR,
+    Timeout,
+  };
+
+  /** a general output handler for libuv  */
+  static WaitForLineResult WaitForLine(uv_loop_t* loop, uv_stream_t* outPipe,
+                                       uv_stream_t* errPipe, std::string& line,
+                                       cmDuration timeout,
+                                       std::vector<char>& out,
+                                       std::vector<char>& err);
 
   static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; }
   static bool GetForceUnixPaths() { return s_ForceUnixPaths; }
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index b53adf8..abbf29e 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -40,6 +40,7 @@
 #include "cmSystemTools.h"
 #include "cmTargetPropertyComputer.h"
 #include "cmValue.h"
+#include "cmXcFramework.h"
 #include "cmake.h"
 
 template <>
@@ -245,9 +246,9 @@
   {
   }
 
-  void CopyFromDirectory(cmBTStringRange directoryEntries)
+  void CopyFromEntries(cmBTStringRange entries)
   {
-    return cm::append(this->Entries, directoryEntries);
+    return cm::append(this->Entries, entries);
   }
 
   enum class Action
@@ -672,6 +673,11 @@
   UsageRequirementProperty InterfaceLinkLibraries;
   UsageRequirementProperty InterfaceLinkLibrariesDirect;
   UsageRequirementProperty InterfaceLinkLibrariesDirectExclude;
+  UsageRequirementProperty ImportedCxxModulesIncludeDirectories;
+  UsageRequirementProperty ImportedCxxModulesCompileDefinitions;
+  UsageRequirementProperty ImportedCxxModulesCompileFeatures;
+  UsageRequirementProperty ImportedCxxModulesCompileOptions;
+  UsageRequirementProperty ImportedCxxModulesLinkLibraries;
 
   FileSetType HeadersFileSets;
   FileSetType CxxModulesFileSets;
@@ -722,6 +728,14 @@
   , InterfaceLinkLibrariesDirect("INTERFACE_LINK_LIBRARIES_DIRECT"_s)
   , InterfaceLinkLibrariesDirectExclude(
       "INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE"_s)
+  , ImportedCxxModulesIncludeDirectories(
+      "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES"_s)
+  , ImportedCxxModulesCompileDefinitions(
+      "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS"_s)
+  , ImportedCxxModulesCompileFeatures(
+      "IMPORTED_CXX_MODULES_COMPILE_FEATURES"_s)
+  , ImportedCxxModulesCompileOptions("IMPORTED_CXX_MODULES_COMPILE_OPTIONS"_s)
+  , ImportedCxxModulesLinkLibraries("IMPORTED_CXX_MODULES_LINK_LIBRARIES"_s)
   , HeadersFileSets("HEADERS"_s, "HEADER_DIRS"_s, "HEADER_SET"_s,
                     "HEADER_DIRS_"_s, "HEADER_SET_"_s, "Header"_s,
                     "The default header set"_s, "Header set"_s,
@@ -811,12 +825,12 @@
     did_read = true;
   } else if (prop == this->SelfEntries.PropertyName) {
     static std::string output;
-    output = cmJoin(this->SelfEntries.Entries, ";"_s);
+    output = cmList::to_string(this->SelfEntries.Entries);
     value = cmValue(output);
     did_read = true;
   } else if (prop == this->InterfaceEntries.PropertyName) {
     static std::string output;
-    output = cmJoin(this->InterfaceEntries.Entries, ";"_s);
+    output = cmList::to_string(this->InterfaceEntries.Entries);
     value = cmValue(output);
     did_read = true;
   } else if (cmHasPrefix(prop, this->DirectoryPrefix)) {
@@ -899,7 +913,7 @@
     if (!this->Entries.empty()) {
       // Storage to back the returned `cmValue`.
       static std::string output;
-      output = cmJoin(this->Entries, ";");
+      output = cmList::to_string(this->Entries);
       value = cmValue(output);
     }
     did_read = true;
@@ -950,7 +964,7 @@
   if (this->IsNormal()) {
     // Initialize the INCLUDE_DIRECTORIES property based on the current value
     // of the same directory property:
-    this->impl->IncludeDirectories.CopyFromDirectory(
+    this->impl->IncludeDirectories.CopyFromEntries(
       this->impl->Makefile->GetIncludeDirectoriesEntries());
 
     {
@@ -959,11 +973,11 @@
                                                   sysInc.end());
     }
 
-    this->impl->CompileOptions.CopyFromDirectory(
+    this->impl->CompileOptions.CopyFromEntries(
       this->impl->Makefile->GetCompileOptionsEntries());
-    this->impl->LinkOptions.CopyFromDirectory(
+    this->impl->LinkOptions.CopyFromEntries(
       this->impl->Makefile->GetLinkOptionsEntries());
-    this->impl->LinkDirectories.CopyFromDirectory(
+    this->impl->LinkDirectories.CopyFromEntries(
       this->impl->Makefile->GetLinkDirectoriesEntries());
   }
 
@@ -1734,6 +1748,186 @@
   return cmMakeRange(this->impl->InterfaceLinkLibrariesDirectExclude.Entries);
 }
 
+void cmTarget::CopyPolicyStatuses(cmTarget const* tgt)
+{
+  // Normal targets cannot be the target of a copy.
+  assert(!this->IsNormal());
+  // Imported targets cannot be the target of a copy.
+  assert(!this->IsImported());
+  // Only imported targets can be the source of a copy.
+  assert(tgt->IsImported());
+
+  this->impl->PolicyMap = tgt->impl->PolicyMap;
+}
+
+void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt)
+{
+  // Normal targets cannot be the target of a copy.
+  assert(!this->IsNormal());
+  // Imported targets cannot be the target of a copy.
+  assert(!this->IsImported());
+  // Only imported targets can be the source of a copy.
+  assert(tgt->IsImported());
+
+  this->impl->IncludeDirectories.Entries.clear();
+  this->impl->IncludeDirectories.CopyFromEntries(
+    cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries));
+  this->impl->CompileDefinitions.Entries.clear();
+  this->impl->CompileDefinitions.CopyFromEntries(
+    cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries));
+  this->impl->CompileFeatures.Entries.clear();
+  this->impl->CompileFeatures.CopyFromEntries(
+    cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries));
+  this->impl->CompileOptions.Entries.clear();
+  this->impl->CompileOptions.CopyFromEntries(
+    cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries));
+  this->impl->LinkLibraries.Entries.clear();
+  this->impl->LinkLibraries.CopyFromEntries(
+    cmMakeRange(tgt->impl->LinkLibraries.Entries));
+
+  // Copy the C++ module fileset entries from `tgt`'s `INTERFACE` to this
+  // target's `PRIVATE`.
+  this->impl->CxxModulesFileSets.SelfEntries.Entries.clear();
+  this->impl->CxxModulesFileSets.SelfEntries.Entries =
+    tgt->impl->CxxModulesFileSets.InterfaceEntries.Entries;
+}
+
+void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt)
+{
+  // Normal targets cannot be the target of a copy.
+  assert(!this->IsNormal());
+  // Imported targets cannot be the target of a copy.
+  assert(!this->IsImported());
+  // Only imported targets can be the source of a copy.
+  assert(tgt->IsImported());
+
+  // The list of properties that are relevant here include:
+  // - compilation-specific properties for any language or platform
+  // - compilation-specific properties for C++
+  // - build graph-specific properties that affect compilation
+  // - IDE metadata properties
+  // - static analysis properties
+
+  static const std::string propertiesToCopy[] = {
+    // Compilation properties
+    "DEFINE_SYMBOL",
+    "DEPRECATION",
+    "NO_SYSTEM_FROM_IMPORTED",
+    "POSITION_INDEPENDENT_CODE",
+    "VISIBILITY_INLINES_HIDDEN",
+    // -- Platforms
+    // ---- Android
+    "ANDROID_API",
+    "ANDROID_API_MIN",
+    "ANDROID_ARCH",
+    "ANDROID_STL_TYPE",
+    // ---- macOS
+    "OSX_ARCHITECTURES",
+    // ---- Windows
+    "MSVC_DEBUG_INFORMATION_FORMAT",
+    "MSVC_RUNTIME_LIBRARY",
+    "VS_PLATFORM_TOOLSET",
+    // ---- OpenWatcom
+    "WATCOM_RUNTIME_LIBRARY",
+    // -- Language
+    // ---- C++
+    "CXX_COMPILER_LAUNCHER",
+    "CXX_STANDARD",
+    "CXX_STANDARD_REQUIRED",
+    "CXX_EXTENSIONS",
+    "CXX_VISIBILITY_PRESET",
+
+    // Static analysis
+    "CXX_CLANG_TIDY",
+    "CXX_CLANG_TIDY_EXPORT_FIXES_DIR",
+    "CXX_CPPLINT",
+    "CXX_CPPCHECK",
+    "CXX_INCLUDE_WHAT_YOU_USE",
+
+    // Build graph properties
+    "EXCLUDE_FROM_ALL",
+    "EXCLUDE_FROM_DEFAULT_BUILD",
+    "OPTIMIZE_DEPENDENCIES",
+    // -- Ninja
+    "JOB_POOL_COMPILE",
+    // -- Visual Studio
+    "VS_NO_COMPILE_BATCHING",
+    "VS_PROJECT_IMPORT",
+
+    // Metadata
+    "EchoString",
+    "EXPORT_COMPILE_COMMANDS",
+    "FOLDER",
+    "LABELS",
+    "PROJECT_LABEL",
+    "SYSTEM",
+  };
+
+  auto copyProperty = [this, tgt](std::string const& prop) -> cmValue {
+    cmValue value = tgt->GetProperty(prop);
+    // Always set the property; it may have been explicitly unset.
+    this->SetProperty(prop, value);
+    return value;
+  };
+
+  for (auto const& prop : propertiesToCopy) {
+    copyProperty(prop);
+  }
+
+  static const cm::static_string_view perConfigPropertiesToCopy[] = {
+    "EXCLUDE_FROM_DEFAULT_BUILD_"_s,
+    "IMPORTED_CXX_MODULES_"_s,
+    "MAP_IMPORTED_CONFIG_"_s,
+    "OSX_ARCHITECTURES_"_s,
+  };
+
+  std::vector<std::string> configNames =
+    this->impl->Makefile->GetGeneratorConfigs(cmMakefile::ExcludeEmptyConfig);
+  for (std::string const& configName : configNames) {
+    std::string configUpper = cmSystemTools::UpperCase(configName);
+    for (auto const& perConfigProp : perConfigPropertiesToCopy) {
+      copyProperty(cmStrCat(perConfigProp, configUpper));
+    }
+  }
+
+  if (this->GetGlobalGenerator()->IsXcode()) {
+    cmValue xcodeGenerateScheme = copyProperty("XCODE_GENERATE_SCHEME");
+
+    // TODO: Make sure these show up on the imported target in the first place
+    // XCODE_ATTRIBUTE_???
+
+    if (xcodeGenerateScheme.IsOn()) {
+#ifdef __APPLE__
+      static const std::string xcodeSchemePropertiesToCopy[] = {
+        // FIXME: Do all of these apply? Do they matter?
+        "XCODE_SCHEME_ADDRESS_SANITIZER",
+        "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN",
+        "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER",
+        "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS",
+        "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE",
+        "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION",
+        "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION",
+        "XCODE_SCHEME_GUARD_MALLOC",
+        "XCODE_SCHEME_LAUNCH_CONFIGURATION",
+        "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP",
+        "XCODE_SCHEME_MALLOC_GUARD_EDGES",
+        "XCODE_SCHEME_MALLOC_SCRIBBLE",
+        "XCODE_SCHEME_MALLOC_STACK",
+        "XCODE_SCHEME_THREAD_SANITIZER",
+        "XCODE_SCHEME_THREAD_SANITIZER_STOP",
+        "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER",
+        "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP",
+        "XCODE_SCHEME_ZOMBIE_OBJECTS",
+      };
+
+      for (auto const& xcodeProperty : xcodeSchemePropertiesToCopy) {
+        copyProperty(xcodeProperty);
+      }
+#endif
+    }
+  }
+}
+
 cmBTStringRange cmTarget::GetHeaderSetsEntries() const
 {
   return cmMakeRange(this->impl->HeadersFileSets.SelfEntries.Entries);
@@ -1776,6 +1970,11 @@
 MAKE_PROP(IMPORTED_GLOBAL);
 MAKE_PROP(INCLUDE_DIRECTORIES);
 MAKE_PROP(LINK_OPTIONS);
+MAKE_PROP(IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES);
+MAKE_PROP(IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS);
+MAKE_PROP(IMPORTED_CXX_MODULES_COMPILE_FEATURES);
+MAKE_PROP(IMPORTED_CXX_MODULES_COMPILE_OPTIONS);
+MAKE_PROP(IMPORTED_CXX_MODULES_LINK_LIBRARIES);
 MAKE_PROP(LINK_DIRECTORIES);
 MAKE_PROP(LINK_LIBRARIES);
 MAKE_PROP(MANUALLY_ADDED_DEPENDENCIES);
@@ -1845,6 +2044,11 @@
     &this->impl->InterfaceLinkLibraries,
     &this->impl->InterfaceLinkLibrariesDirect,
     &this->impl->InterfaceLinkLibrariesDirectExclude,
+    &this->impl->ImportedCxxModulesIncludeDirectories,
+    &this->impl->ImportedCxxModulesCompileDefinitions,
+    &this->impl->ImportedCxxModulesCompileFeatures,
+    &this->impl->ImportedCxxModulesCompileOptions,
+    &this->impl->ImportedCxxModulesLinkLibraries,
   };
 
   for (auto* usageRequirement : usageRequirements) {
@@ -2018,6 +2222,11 @@
     &this->impl->InterfaceLinkLibraries,
     &this->impl->InterfaceLinkLibrariesDirect,
     &this->impl->InterfaceLinkLibrariesDirectExclude,
+    &this->impl->ImportedCxxModulesIncludeDirectories,
+    &this->impl->ImportedCxxModulesCompileDefinitions,
+    &this->impl->ImportedCxxModulesCompileFeatures,
+    &this->impl->ImportedCxxModulesCompileOptions,
+    &this->impl->ImportedCxxModulesLinkLibraries,
   };
 
   for (auto* usageRequirement : usageRequirements) {
@@ -2130,7 +2339,7 @@
     return nullptr;
   }
   static std::string output;
-  output = cmJoin(fileSet->GetDirectoryEntries(), ";"_s);
+  output = cmList::to_string(fileSet->GetDirectoryEntries());
   return cmValue(output);
 }
 
@@ -2150,7 +2359,7 @@
     return nullptr;
   }
   static std::string output;
-  output = cmJoin(fileSet->GetFileEntries(), ";"_s);
+  output = cmList::to_string(fileSet->GetFileEntries());
   return cmValue(output);
 }
 
@@ -2422,6 +2631,7 @@
     propC_STANDARD,
     propCXX_STANDARD,
     propCUDA_STANDARD,
+    propHIP_STANDARD,
     propOBJC_STANDARD,
     propOBJCXX_STANDARD,
     propLINK_LIBRARIES,
@@ -2443,11 +2653,16 @@
     propINTERFACE_LINK_LIBRARIES,
     propINTERFACE_LINK_LIBRARIES_DIRECT,
     propINTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE,
+    propIMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES,
+    propIMPORTED_CXX_MODULES_COMPILE_DEFINITIONS,
+    propIMPORTED_CXX_MODULES_COMPILE_FEATURES,
+    propIMPORTED_CXX_MODULES_COMPILE_OPTIONS,
+    propIMPORTED_CXX_MODULES_LINK_LIBRARIES,
   };
   if (specialProps.count(prop)) {
     if (prop == propC_STANDARD || prop == propCXX_STANDARD ||
-        prop == propCUDA_STANDARD || prop == propOBJC_STANDARD ||
-        prop == propOBJCXX_STANDARD) {
+        prop == propCUDA_STANDARD || prop == propHIP_STANDARD ||
+        prop == propOBJC_STANDARD || prop == propOBJCXX_STANDARD) {
       auto propertyIter = this->impl->LanguageStandardProperties.find(prop);
       if (propertyIter == this->impl->LanguageStandardProperties.end()) {
         return nullptr;
@@ -2468,6 +2683,11 @@
       &this->impl->InterfaceLinkLibraries,
       &this->impl->InterfaceLinkLibrariesDirect,
       &this->impl->InterfaceLinkLibrariesDirectExclude,
+      &this->impl->ImportedCxxModulesIncludeDirectories,
+      &this->impl->ImportedCxxModulesCompileDefinitions,
+      &this->impl->ImportedCxxModulesCompileFeatures,
+      &this->impl->ImportedCxxModulesCompileOptions,
+      &this->impl->ImportedCxxModulesLinkLibraries,
     };
 
     for (auto const* usageRequirement : usageRequirements) {
@@ -2495,7 +2715,7 @@
         [](const BT<std::pair<std::string, bool>>& item) -> std::string {
           return item.Value.first;
         });
-      output = cmJoin(utilities, ";");
+      output = cmList::to_string(utilities);
       return cmValue(output);
     }
     if (prop == propIMPORTED) {
@@ -2650,11 +2870,32 @@
   return this->impl->PerConfig;
 }
 
+bool cmTarget::IsRuntimeBinary() const
+{
+  switch (this->GetType()) {
+    case cmStateEnums::EXECUTABLE:
+    case cmStateEnums::SHARED_LIBRARY:
+    case cmStateEnums::MODULE_LIBRARY:
+      return true;
+    case cmStateEnums::OBJECT_LIBRARY:
+    case cmStateEnums::STATIC_LIBRARY:
+    case cmStateEnums::UTILITY:
+    case cmStateEnums::INTERFACE_LIBRARY:
+    case cmStateEnums::GLOBAL_TARGET:
+    case cmStateEnums::UNKNOWN_LIBRARY:
+      break;
+  }
+  return false;
+}
+
 bool cmTarget::CanCompileSources() const
 {
   if (this->IsImported()) {
     return false;
   }
+  if (this->IsSynthetic()) {
+    return true;
+  }
   switch (this->GetType()) {
     case cmStateEnums::EXECUTABLE:
     case cmStateEnums::STATIC_LIBRARY:
@@ -2779,6 +3020,8 @@
       case cmStateEnums::RuntimeBinaryArtifact:
         if (loc) {
           result = *loc;
+        } else if (imp) {
+          result = *imp;
         } else {
           std::string impProp = cmStrCat("IMPORTED_LOCATION", suffix);
           if (cmValue config_location = this->GetProperty(impProp)) {
@@ -2787,6 +3030,35 @@
                        this->GetProperty("IMPORTED_LOCATION")) {
             result = *location;
           }
+          if (result.empty() &&
+              (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+               this->IsExecutableWithExports())) {
+            impProp = cmStrCat("IMPORTED_IMPLIB", suffix);
+            if (cmValue config_implib = this->GetProperty(impProp)) {
+              result = *config_implib;
+            } else if (cmValue implib = this->GetProperty("IMPORTED_IMPLIB")) {
+              result = *implib;
+            }
+          }
+        }
+        if (this->IsApple() &&
+            (this->impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
+             this->impl->TargetType == cmStateEnums::STATIC_LIBRARY ||
+             this->impl->TargetType == cmStateEnums::UNKNOWN_LIBRARY) &&
+            cmSystemTools::IsPathToXcFramework(result)) {
+          auto plist = cmParseXcFrameworkPlist(result, *this->impl->Makefile,
+                                               this->impl->Backtrace);
+          if (!plist) {
+            return "";
+          }
+          auto const* library = plist->SelectSuitableLibrary(
+            *this->impl->Makefile, this->impl->Backtrace);
+          if (library) {
+            result = cmStrCat(result, '/', library->LibraryIdentifier, '/',
+                              library->LibraryPath);
+          } else {
+            return "";
+          }
         }
         break;
 
@@ -2812,7 +3084,10 @@
         std::string unset;
         std::string configuration;
 
-        if (artifact == cmStateEnums::RuntimeBinaryArtifact) {
+        if (this->GetType() == cmStateEnums::SHARED_LIBRARY &&
+            artifact == cmStateEnums::RuntimeBinaryArtifact) {
+          unset = "IMPORTED_LOCATION or IMPORTED_IMPLIB";
+        } else if (artifact == cmStateEnums::RuntimeBinaryArtifact) {
           unset = "IMPORTED_LOCATION";
         } else if (artifact == cmStateEnums::ImportLibraryArtifact) {
           unset = "IMPORTED_IMPLIB";
@@ -2926,6 +3201,11 @@
   return result;
 }
 
+bool cmTarget::HasFileSets() const
+{
+  return !this->impl->FileSets.empty();
+}
+
 bool cmTargetInternals::CheckImportedLibName(std::string const& prop,
                                              std::string const& value) const
 {
@@ -2985,11 +3265,10 @@
   }
 
   // If we needed to find one of the mapped configurations but did not
-  // On a DLL platform there may be only IMPORTED_IMPLIB for a shared
-  // library or an executable with exports.
-  bool allowImp = (this->IsDLLPlatform() &&
-                   (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
-                    this->IsExecutableWithExports())) ||
+  // There may be only IMPORTED_IMPLIB for a shared library or an executable
+  // with exports.
+  bool allowImp = (this->GetType() == cmStateEnums::SHARED_LIBRARY ||
+                   this->IsExecutableWithExports()) ||
     (this->IsAIX() && this->IsExecutableWithExports()) ||
     (this->GetMakefile()->PlatformSupportsAppleTextStubs() &&
      this->IsSharedLibraryWithExports());
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 2d12a70..584856a 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -15,7 +15,7 @@
 #include <cm/optional>
 
 #include "cmAlgorithms.h"
-#include "cmFileSet.h"
+#include "cmListFileCache.h"
 #include "cmPolicies.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
@@ -23,20 +23,16 @@
 #include "cmValue.h"
 
 class cmCustomCommand;
+class cmFileSet;
 class cmGlobalGenerator;
 class cmInstallTargetGenerator;
-class cmListFileBacktrace;
-class cmListFileContext;
 class cmMakefile;
 class cmPropertyMap;
 class cmSourceFile;
 class cmTargetExport;
 class cmTargetInternals;
 
-template <typename T>
-class BT;
-template <typename T>
-class BTs;
+enum class cmFileSetVisibility;
 
 /** \class cmTarget
  * \brief Represent a library or executable target loaded from a makefile.
@@ -216,6 +212,7 @@
   bool IsImported() const;
   bool IsImportedGloballyVisible() const;
   bool IsPerConfig() const;
+  bool IsRuntimeBinary() const;
   bool CanCompileSources() const;
 
   bool GetMappedConfig(std::string const& desired_config, cmValue& loc,
@@ -294,6 +291,10 @@
   cmBTStringRange GetLinkInterfaceDirectEntries() const;
   cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
 
+  void CopyPolicyStatuses(cmTarget const* tgt);
+  void CopyImportedCxxModulesEntries(cmTarget const* tgt);
+  void CopyImportedCxxModulesProperties(cmTarget const* tgt);
+
   cmBTStringRange GetHeaderSetsEntries() const;
   cmBTStringRange GetCxxModuleSetsEntries() const;
 
@@ -320,6 +321,8 @@
   static std::string GetFileSetsPropertyName(const std::string& type);
   static std::string GetInterfaceFileSetsPropertyName(const std::string& type);
 
+  bool HasFileSets() const;
+
 private:
   template <typename ValueType>
   void StoreProperty(const std::string& prop, ValueType value);
diff --git a/Source/cmTargetCompileFeaturesCommand.cxx b/Source/cmTargetCompileFeaturesCommand.cxx
index aa1abdd..37c125b 100644
--- a/Source/cmTargetCompileFeaturesCommand.cxx
+++ b/Source/cmTargetCompileFeaturesCommand.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetCompileFeaturesCommand.h"
 
+#include "cmList.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
 #include "cmStandardLevelResolver.h"
@@ -43,7 +44,7 @@
 
   std::string Join(const std::vector<std::string>& content) override
   {
-    return cmJoin(content, ";");
+    return cmList::to_string(content);
   }
 };
 
diff --git a/Source/cmTargetCompileOptionsCommand.cxx b/Source/cmTargetCompileOptionsCommand.cxx
index 8ca3842..e73a75f 100644
--- a/Source/cmTargetCompileOptionsCommand.cxx
+++ b/Source/cmTargetCompileOptionsCommand.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetCompileOptionsCommand.h"
 
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -44,7 +45,7 @@
 
   std::string Join(const std::vector<std::string>& content) override
   {
-    return cmJoin(content, ";");
+    return cmList::to_string(content);
   }
 };
 
diff --git a/Source/cmTargetLinkDirectoriesCommand.cxx b/Source/cmTargetLinkDirectoriesCommand.cxx
index 3ba27a8..dddb348 100644
--- a/Source/cmTargetLinkDirectoriesCommand.cxx
+++ b/Source/cmTargetLinkDirectoriesCommand.cxx
@@ -3,6 +3,7 @@
 #include "cmTargetLinkDirectoriesCommand.h"
 
 #include "cmGeneratorExpression.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -58,7 +59,7 @@
     directories.push_back(unixPath);
   }
 
-  return cmJoin(directories, ";");
+  return cmList::to_string(directories);
 }
 
 } // namespace
diff --git a/Source/cmTargetLinkLibrariesCommand.cxx b/Source/cmTargetLinkLibrariesCommand.cxx
index 03d7c9f..bfd3972 100644
--- a/Source/cmTargetLinkLibrariesCommand.cxx
+++ b/Source/cmTargetLinkLibrariesCommand.cxx
@@ -3,6 +3,7 @@
 #include "cmTargetLinkLibrariesCommand.h"
 
 #include <cassert>
+#include <cstddef>
 #include <memory>
 #include <sstream>
 #include <unordered_set>
diff --git a/Source/cmTargetLinkOptionsCommand.cxx b/Source/cmTargetLinkOptionsCommand.cxx
index 3ea2d71..cd93835 100644
--- a/Source/cmTargetLinkOptionsCommand.cxx
+++ b/Source/cmTargetLinkOptionsCommand.cxx
@@ -2,6 +2,7 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmTargetLinkOptionsCommand.h"
 
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -36,7 +37,7 @@
 
   std::string Join(const std::vector<std::string>& content) override
   {
-    return cmJoin(content, ";");
+    return cmList::to_string(content);
   }
 };
 
diff --git a/Source/cmTargetPrecompileHeadersCommand.cxx b/Source/cmTargetPrecompileHeadersCommand.cxx
index 4dd158d..0173a92 100644
--- a/Source/cmTargetPrecompileHeadersCommand.cxx
+++ b/Source/cmTargetPrecompileHeadersCommand.cxx
@@ -5,6 +5,7 @@
 #include <utility>
 
 #include "cmGeneratorExpression.h"
+#include "cmList.h"
 #include "cmListFileCache.h"
 #include "cmMakefile.h"
 #include "cmMessageType.h"
@@ -73,7 +74,7 @@
 
   std::string Join(const std::vector<std::string>& content) override
   {
-    return cmJoin(content, ";");
+    return cmList::to_string(content);
   }
 };
 
diff --git a/Source/cmTargetSourcesCommand.cxx b/Source/cmTargetSourcesCommand.cxx
index 12328b1..babbaa5 100644
--- a/Source/cmTargetSourcesCommand.cxx
+++ b/Source/cmTargetSourcesCommand.cxx
@@ -10,7 +10,6 @@
 
 #include "cmArgumentParser.h"
 #include "cmArgumentParserTypes.h"
-#include "cmExperimental.h"
 #include "cmFileSet.h"
 #include "cmGeneratorExpression.h"
 #include "cmList.h"
@@ -98,7 +97,7 @@
 
   std::string Join(const std::vector<std::string>& content) override
   {
-    return cmJoin(content, ";");
+    return cmList::to_string(content);
   }
 
   enum class IsInterface
@@ -260,28 +259,18 @@
       this->SetError("Must specify a TYPE when creating file set");
       return false;
     }
-    bool const supportCxx20FileSetTypes = cmExperimental::HasSupportEnabled(
-      *this->Makefile, cmExperimental::Feature::CxxModuleCMakeApi);
+    if (type != "HEADERS"_s && type != "CXX_MODULES"_s) {
+      this->SetError(
+        R"(File set TYPE may only be "HEADERS" or "CXX_MODULES")");
+      return false;
+    }
 
-    if (supportCxx20FileSetTypes) {
-      if (type != "HEADERS"_s && type != "CXX_MODULES"_s) {
+    if (cmFileSetVisibilityIsForInterface(visibility) &&
+        !cmFileSetVisibilityIsForSelf(visibility) &&
+        !this->Target->IsImported()) {
+      if (type == "CXX_MODULES"_s) {
         this->SetError(
-          R"(File set TYPE may only be "HEADERS" or "CXX_MODULES")");
-        return false;
-      }
-
-      if (cmFileSetVisibilityIsForInterface(visibility) &&
-          !cmFileSetVisibilityIsForSelf(visibility) &&
-          !this->Target->IsImported()) {
-        if (type == "CXX_MODULES"_s) {
-          this->SetError(
-            R"(File set TYPE "CXX_MODULES" may not have "INTERFACE" visibility)");
-          return false;
-        }
-      }
-    } else {
-      if (type != "HEADERS"_s) {
-        this->SetError("File set TYPE may only be \"HEADERS\"");
+          R"(File set TYPE "CXX_MODULES" may not have "INTERFACE" visibility)");
         return false;
       }
     }
diff --git a/Source/cmTransformDepfile.cxx b/Source/cmTransformDepfile.cxx
index 12c121f..914172b 100644
--- a/Source/cmTransformDepfile.cxx
+++ b/Source/cmTransformDepfile.cxx
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <functional>
 #include <string>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
index e05b2d52..34e6a70 100644
--- a/Source/cmUVHandlePtr.cxx
+++ b/Source/cmUVHandlePtr.cxx
@@ -11,6 +11,9 @@
 
 namespace cm {
 
+template <typename T>
+struct uv_handle_deleter;
+
 struct uv_loop_deleter
 {
   void operator()(uv_loop_t* loop) const;
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx
index ed5f38b..655e52a 100644
--- a/Source/cmUVProcessChain.cxx
+++ b/Source/cmUVProcessChain.cxx
@@ -1,16 +1,11 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
-#include "cmConfigure.h"
-
 #include "cmUVProcessChain.h"
 
 #include <array>
-#include <cassert>
 #include <csignal>
 #include <cstdio>
 #include <istream> // IWYU pragma: keep
-#include <iterator>
-#include <type_traits>
 #include <utility>
 
 #include <cm/memory>
@@ -19,43 +14,24 @@
 
 #include "cmGetPipes.h"
 #include "cmUVHandlePtr.h"
-#include "cmUVStreambuf.h"
 
 struct cmUVProcessChain::InternalData
 {
-  struct BasicStreamData
+  struct StreamData
   {
-    cmUVStreambuf Streambuf;
-    cm::uv_pipe_ptr BuiltinStream;
+    int BuiltinStream = -1;
     uv_stdio_container_t Stdio;
   };
 
-  template <typename IOStream>
-  struct StreamData : public BasicStreamData
-  {
-    StreamData()
-      : BuiltinIOStream(&this->Streambuf)
-    {
-    }
-
-    IOStream BuiltinIOStream;
-
-    IOStream* GetBuiltinStream()
-    {
-      if (this->BuiltinStream.get()) {
-        return &this->BuiltinIOStream;
-      }
-      return nullptr;
-    }
-  };
-
   struct ProcessData
   {
     cmUVProcessChain::InternalData* Data;
     cm::uv_process_ptr Process;
+    cm::uv_pipe_ptr InputPipe;
     cm::uv_pipe_ptr OutputPipe;
-    bool Finished = false;
     Status ProcessStatus;
+
+    void Finish();
   };
 
   const cmUVProcessChainBuilder* Builder = nullptr;
@@ -64,18 +40,21 @@
 
   cm::uv_loop_ptr Loop;
 
-  StreamData<std::istream> OutputStreamData;
-  StreamData<std::istream> ErrorStreamData;
+  StreamData InputStreamData;
+  StreamData OutputStreamData;
+  StreamData ErrorStreamData;
+  cm::uv_pipe_ptr TempOutputPipe;
+  cm::uv_pipe_ptr TempErrorPipe;
 
   unsigned int ProcessesCompleted = 0;
   std::vector<std::unique_ptr<ProcessData>> Processes;
 
   bool Prepare(const cmUVProcessChainBuilder* builder);
-  bool AddCommand(const cmUVProcessChainBuilder::ProcessConfiguration& config,
-                  bool first, bool last);
-  bool Finish();
-
-  static const Status* GetStatus(const ProcessData& data);
+  void SpawnProcess(
+    std::size_t index,
+    const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
+    bool last);
+  void Finish();
 };
 
 cmUVProcessChainBuilder::cmUVProcessChainBuilder()
@@ -132,9 +111,6 @@
 {
   switch (stdio) {
     case Stream_INPUT:
-      // FIXME
-      break;
-
     case Stream_OUTPUT:
     case Stream_ERROR: {
       auto& streamData = this->Stdio[stdio];
@@ -167,11 +143,9 @@
     return chain;
   }
 
-  for (auto it = this->Processes.begin(); it != this->Processes.end(); ++it) {
-    if (!chain.Data->AddCommand(*it, it == this->Processes.begin(),
-                                it == std::prev(this->Processes.end()))) {
-      return chain;
-    }
+  for (std::size_t i = 0; i < this->Processes.size(); i++) {
+    chain.Data->SpawnProcess(i, this->Processes[i], i == 0,
+                             i == this->Processes.size() - 1);
   }
 
   chain.Data->Finish();
@@ -179,20 +153,30 @@
   return chain;
 }
 
-const cmUVProcessChain::Status* cmUVProcessChain::InternalData::GetStatus(
-  const cmUVProcessChain::InternalData::ProcessData& data)
-{
-  if (data.Finished) {
-    return &data.ProcessStatus;
-  }
-  return nullptr;
-}
-
 bool cmUVProcessChain::InternalData::Prepare(
   const cmUVProcessChainBuilder* builder)
 {
   this->Builder = builder;
 
+  auto const& input =
+    this->Builder->Stdio[cmUVProcessChainBuilder::Stream_INPUT];
+  auto& inputData = this->InputStreamData;
+  switch (input.Type) {
+    case cmUVProcessChainBuilder::None:
+      inputData.Stdio.flags = UV_IGNORE;
+      break;
+
+    case cmUVProcessChainBuilder::Builtin: {
+      // FIXME
+      break;
+    }
+
+    case cmUVProcessChainBuilder::External:
+      inputData.Stdio.flags = UV_INHERIT_FD;
+      inputData.Stdio.data.fd = input.FileDescriptor;
+      break;
+  }
+
   auto const& error =
     this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR];
   auto& errorData = this->ErrorStreamData;
@@ -207,12 +191,17 @@
         return false;
       }
 
-      errorData.BuiltinStream.init(*this->Loop, 0);
-      if (uv_pipe_open(errorData.BuiltinStream, pipeFd[0]) < 0) {
-        return false;
-      }
+      errorData.BuiltinStream = pipeFd[0];
       errorData.Stdio.flags = UV_INHERIT_FD;
       errorData.Stdio.data.fd = pipeFd[1];
+
+      if (this->TempErrorPipe.init(*this->Loop, 0) < 0) {
+        return false;
+      }
+      if (uv_pipe_open(this->TempErrorPipe, errorData.Stdio.data.fd) < 0) {
+        return false;
+      }
+
       break;
     }
 
@@ -232,13 +221,25 @@
 
     case cmUVProcessChainBuilder::Builtin:
       if (this->Builder->MergedBuiltinStreams) {
+        outputData.BuiltinStream = errorData.BuiltinStream;
         outputData.Stdio.flags = UV_INHERIT_FD;
         outputData.Stdio.data.fd = errorData.Stdio.data.fd;
       } else {
-        outputData.BuiltinStream.init(*this->Loop, 0);
-        outputData.Stdio.flags =
-          static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
-        outputData.Stdio.data.stream = outputData.BuiltinStream;
+        int pipeFd[2];
+        if (cmGetPipes(pipeFd) < 0) {
+          return false;
+        }
+
+        outputData.BuiltinStream = pipeFd[0];
+        outputData.Stdio.flags = UV_INHERIT_FD;
+        outputData.Stdio.data.fd = pipeFd[1];
+
+        if (this->TempOutputPipe.init(*this->Loop, 0) < 0) {
+          return false;
+        }
+        if (uv_pipe_open(this->TempOutputPipe, outputData.Stdio.data.fd) < 0) {
+          return false;
+        }
       }
       break;
 
@@ -248,16 +249,47 @@
       break;
   }
 
+  bool first = true;
+  for (std::size_t i = 0; i < this->Builder->Processes.size(); i++) {
+    this->Processes.emplace_back(cm::make_unique<ProcessData>());
+    auto& process = *this->Processes.back();
+    process.Data = this;
+    process.ProcessStatus.Finished = false;
+
+    if (!first) {
+      auto& prevProcess = *this->Processes[i - 1];
+
+      int pipeFd[2];
+      if (cmGetPipes(pipeFd) < 0) {
+        return false;
+      }
+
+      if (prevProcess.OutputPipe.init(*this->Loop, 0) < 0) {
+        return false;
+      }
+      if (uv_pipe_open(prevProcess.OutputPipe, pipeFd[1]) < 0) {
+        return false;
+      }
+      if (process.InputPipe.init(*this->Loop, 0) < 0) {
+        return false;
+      }
+      if (uv_pipe_open(process.InputPipe, pipeFd[0]) < 0) {
+        return false;
+      }
+    }
+
+    first = false;
+  }
+
   return true;
 }
 
-bool cmUVProcessChain::InternalData::AddCommand(
+void cmUVProcessChain::InternalData::SpawnProcess(
+  std::size_t index,
   const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
   bool last)
 {
-  this->Processes.emplace_back(cm::make_unique<ProcessData>());
-  auto& process = *this->Processes.back();
-  process.Data = this;
+  auto& process = *this->Processes[index];
 
   auto options = uv_process_options_t();
 
@@ -277,24 +309,18 @@
   }
 
   std::array<uv_stdio_container_t, 3> stdio;
-  stdio[0] = uv_stdio_container_t();
   if (first) {
-    stdio[0].flags = UV_IGNORE;
+    stdio[0] = this->InputStreamData.Stdio;
   } else {
-    assert(this->Processes.size() >= 2);
-    auto& prev = *this->Processes[this->Processes.size() - 2];
+    stdio[0] = uv_stdio_container_t();
     stdio[0].flags = UV_INHERIT_STREAM;
-    stdio[0].data.stream = prev.OutputPipe;
+    stdio[0].data.stream = process.InputPipe;
   }
   if (last) {
     stdio[1] = this->OutputStreamData.Stdio;
   } else {
-    if (process.OutputPipe.init(*this->Loop, 0) < 0) {
-      return false;
-    }
     stdio[1] = uv_stdio_container_t();
-    stdio[1].flags =
-      static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+    stdio[1].flags = UV_INHERIT_STREAM;
     stdio[1].data.stream = process.OutputPipe;
   }
   stdio[2] = this->ErrorStreamData.Stdio;
@@ -304,40 +330,24 @@
   options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
                        int termSignal) {
     auto* processData = static_cast<ProcessData*>(handle->data);
-    processData->Finished = true;
     processData->ProcessStatus.ExitStatus = exitStatus;
     processData->ProcessStatus.TermSignal = termSignal;
-    processData->Data->ProcessesCompleted++;
+    processData->Finish();
   };
 
-  return process.Process.spawn(*this->Loop, options, &process) >= 0;
+  if ((process.ProcessStatus.SpawnResult =
+         process.Process.spawn(*this->Loop, options, &process)) < 0) {
+    process.Finish();
+  }
+  process.InputPipe.reset();
+  process.OutputPipe.reset();
 }
 
-bool cmUVProcessChain::InternalData::Finish()
+void cmUVProcessChain::InternalData::Finish()
 {
-  if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT].Type ==
-        cmUVProcessChainBuilder::Builtin &&
-      !this->Builder->MergedBuiltinStreams) {
-    this->OutputStreamData.Streambuf.open(
-      this->OutputStreamData.BuiltinStream);
-  }
-
-  if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR].Type ==
-      cmUVProcessChainBuilder::Builtin) {
-    cm::uv_pipe_ptr tmpPipe;
-    if (tmpPipe.init(*this->Loop, 0) < 0) {
-      return false;
-    }
-    if (uv_pipe_open(tmpPipe, this->ErrorStreamData.Stdio.data.fd) < 0) {
-      return false;
-    }
-    tmpPipe.reset();
-
-    this->ErrorStreamData.Streambuf.open(this->ErrorStreamData.BuiltinStream);
-  }
-
+  this->TempOutputPipe.reset();
+  this->TempErrorPipe.reset();
   this->Valid = true;
-  return true;
 }
 
 cmUVProcessChain::cmUVProcessChain()
@@ -365,17 +375,14 @@
   return *this->Data->Loop;
 }
 
-std::istream* cmUVProcessChain::OutputStream()
+int cmUVProcessChain::OutputStream()
 {
-  if (this->Data->Builder->MergedBuiltinStreams) {
-    return this->Data->ErrorStreamData.GetBuiltinStream();
-  }
-  return this->Data->OutputStreamData.GetBuiltinStream();
+  return this->Data->OutputStreamData.BuiltinStream;
 }
 
-std::istream* cmUVProcessChain::ErrorStream()
+int cmUVProcessChain::ErrorStream()
 {
-  return this->Data->ErrorStreamData.GetBuiltinStream();
+  return this->Data->ErrorStreamData.BuiltinStream;
 }
 
 bool cmUVProcessChain::Valid() const
@@ -383,12 +390,12 @@
   return this->Data->Valid;
 }
 
-bool cmUVProcessChain::Wait(int64_t milliseconds)
+bool cmUVProcessChain::Wait(uint64_t milliseconds)
 {
   bool timeout = false;
   cm::uv_timer_ptr timer;
 
-  if (milliseconds >= 0) {
+  if (milliseconds > 0) {
     timer.init(*this->Data->Loop, &timeout);
     timer.start(
       [](uv_timer_t* handle) {
@@ -412,19 +419,15 @@
   std::vector<const cmUVProcessChain::Status*> statuses(
     this->Data->Processes.size(), nullptr);
   for (std::size_t i = 0; i < statuses.size(); i++) {
-    statuses[i] = this->GetStatus(i);
+    statuses[i] = &this->GetStatus(i);
   }
   return statuses;
 }
 
-const cmUVProcessChain::Status* cmUVProcessChain::GetStatus(
+const cmUVProcessChain::Status& cmUVProcessChain::GetStatus(
   std::size_t index) const
 {
-  auto const& process = *this->Data->Processes[index];
-  if (process.Finished) {
-    return &process.ProcessStatus;
-  }
-  return nullptr;
+  return this->Data->Processes[index]->ProcessStatus;
 }
 
 bool cmUVProcessChain::Finished() const
@@ -435,8 +438,12 @@
 std::pair<cmUVProcessChain::ExceptionCode, std::string>
 cmUVProcessChain::Status::GetException() const
 {
+  if (this->SpawnResult) {
+    return std::make_pair(ExceptionCode::Spawn,
+                          uv_strerror(this->SpawnResult));
+  }
 #ifdef _WIN32
-  if ((this->ExitStatus & 0xF0000000) == 0xC0000000) {
+  if (this->Finished && (this->ExitStatus & 0xF0000000) == 0xC0000000) {
     // Child terminated due to exceptional behavior.
     switch (this->ExitStatus) {
       case STATUS_CONTROL_C_EXIT:
@@ -511,9 +518,8 @@
       }
     }
   }
-  return std::make_pair(ExceptionCode::None, "");
 #else
-  if (this->TermSignal) {
+  if (this->Finished && this->TermSignal) {
     switch (this->TermSignal) {
 #  ifdef SIGSEGV
       case SIGSEGV:
@@ -670,6 +676,12 @@
       }
     }
   }
-  return std::make_pair(ExceptionCode::None, "");
 #endif
+  return std::make_pair(ExceptionCode::None, "");
+}
+
+void cmUVProcessChain::InternalData::ProcessData::Finish()
+{
+  this->ProcessStatus.Finished = true;
+  this->Data->ProcessesCompleted++;
 }
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h
index f92742f..0f37e7d 100644
--- a/Source/cmUVProcessChain.h
+++ b/Source/cmUVProcessChain.h
@@ -2,10 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include "cmConfigure.h" // IWYU pragma: keep
+
 #include <array>
 #include <cstddef> // IWYU pragma: keep
 #include <cstdint>
-#include <iosfwd>
 #include <memory>
 #include <string>
 #include <utility>
@@ -74,11 +75,14 @@
     Illegal,
     Interrupt,
     Numerical,
+    Spawn,
     Other,
   };
 
   struct Status
   {
+    int SpawnResult;
+    bool Finished;
     int64_t ExitStatus;
     int TermSignal;
 
@@ -96,13 +100,13 @@
   uv_loop_t& GetLoop();
 
   // FIXME: Add stdin support
-  std::istream* OutputStream();
-  std::istream* ErrorStream();
+  int OutputStream();
+  int ErrorStream();
 
   bool Valid() const;
-  bool Wait(int64_t milliseconds = -1);
+  bool Wait(uint64_t milliseconds = 0);
   std::vector<const Status*> GetStatus() const;
-  const Status* GetStatus(std::size_t index) const;
+  const Status& GetStatus(std::size_t index) const;
   bool Finished() const;
 
 private:
diff --git a/Source/cmUVStream.h b/Source/cmUVStream.h
new file mode 100644
index 0000000..db051b8
--- /dev/null
+++ b/Source/cmUVStream.h
@@ -0,0 +1,154 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <cassert>
+#include <functional>
+#include <istream>
+
+#include <cm/memory>
+
+#include <cm3p/uv.h>
+
+#include "cmUVHandlePtr.h"
+#include "cmUVStreambuf.h"
+
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+class cmBasicUVIStream : public std::basic_istream<CharT>
+{
+public:
+  cmBasicUVIStream();
+  cmBasicUVIStream(uv_stream_t* stream);
+
+  bool is_open() const;
+
+  void open(uv_stream_t* stream);
+
+  void close();
+
+private:
+  cmBasicUVStreambuf<CharT, Traits> Buffer;
+};
+
+template <typename CharT, typename Traits>
+cmBasicUVIStream<CharT, Traits>::cmBasicUVIStream()
+  : std::basic_istream<CharT, Traits>(&this->Buffer)
+{
+}
+
+template <typename CharT, typename Traits>
+cmBasicUVIStream<CharT, Traits>::cmBasicUVIStream(uv_stream_t* stream)
+  : cmBasicUVIStream()
+{
+  this->open(stream);
+}
+
+template <typename CharT, typename Traits>
+bool cmBasicUVIStream<CharT, Traits>::is_open() const
+{
+  return this->Buffer.is_open();
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVIStream<CharT, Traits>::open(uv_stream_t* stream)
+{
+  this->Buffer.open(stream);
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVIStream<CharT, Traits>::close()
+{
+  this->Buffer.close();
+}
+
+using cmUVIStream = cmBasicUVIStream<char>;
+
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+class cmBasicUVPipeIStream : public cmBasicUVIStream<CharT, Traits>
+{
+public:
+  cmBasicUVPipeIStream();
+  cmBasicUVPipeIStream(uv_loop_t& loop, int fd);
+
+  using cmBasicUVIStream<CharT, Traits>::is_open;
+
+  void open(uv_loop_t& loop, int fd);
+
+  void close();
+
+private:
+  cm::uv_pipe_ptr Pipe;
+};
+
+template <typename CharT, typename Traits>
+cmBasicUVPipeIStream<CharT, Traits>::cmBasicUVPipeIStream() = default;
+
+template <typename CharT, typename Traits>
+cmBasicUVPipeIStream<CharT, Traits>::cmBasicUVPipeIStream(uv_loop_t& loop,
+                                                          int fd)
+{
+  this->open(loop, fd);
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVPipeIStream<CharT, Traits>::open(uv_loop_t& loop, int fd)
+{
+  this->Pipe.init(loop, 0);
+  uv_pipe_open(this->Pipe, fd);
+  this->cmBasicUVIStream<CharT, Traits>::open(this->Pipe);
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVPipeIStream<CharT, Traits>::close()
+{
+  this->cmBasicUVIStream<CharT, Traits>::close();
+  this->Pipe.reset();
+}
+
+using cmUVPipeIStream = cmBasicUVPipeIStream<char>;
+
+class cmUVStreamReadHandle
+{
+private:
+  std::vector<char> Buffer;
+  std::function<void(std::vector<char>)> OnRead;
+  std::function<void()> OnFinish;
+
+  template <typename ReadCallback, typename FinishCallback>
+  friend std::unique_ptr<cmUVStreamReadHandle> cmUVStreamRead(
+    uv_stream_t* stream, ReadCallback onRead, FinishCallback onFinish);
+};
+
+template <typename ReadCallback, typename FinishCallback>
+std::unique_ptr<cmUVStreamReadHandle> cmUVStreamRead(uv_stream_t* stream,
+                                                     ReadCallback onRead,
+                                                     FinishCallback onFinish)
+{
+  auto handle = cm::make_unique<cmUVStreamReadHandle>();
+  handle->OnRead = std::move(onRead);
+  handle->OnFinish = std::move(onFinish);
+
+  stream->data = handle.get();
+  uv_read_start(
+    stream,
+    [](uv_handle_t* s, std::size_t suggestedSize, uv_buf_t* buffer) {
+      auto* data = static_cast<cmUVStreamReadHandle*>(s->data);
+      data->Buffer.resize(suggestedSize);
+      buffer->base = data->Buffer.data();
+      buffer->len = suggestedSize;
+    },
+    [](uv_stream_t* s, ssize_t nread, const uv_buf_t* buffer) {
+      auto* data = static_cast<cmUVStreamReadHandle*>(s->data);
+      if (nread > 0) {
+        (void)buffer;
+        assert(buffer->base == data->Buffer.data());
+        data->Buffer.resize(nread);
+        data->OnRead(std::move(data->Buffer));
+      } else if (nread < 0 /*|| nread == UV_EOF*/) {
+        data->OnFinish();
+        uv_read_stop(s);
+      }
+    });
+
+  return handle;
+}
diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h
index efe45de..4f7b209 100644
--- a/Source/cmUVStreambuf.h
+++ b/Source/cmUVStreambuf.h
@@ -14,7 +14,8 @@
 /*
  * This file is based on example code from:
  *
- * http://www.voidcn.com/article/p-vjnlygmc-gy.html
+ * https://web.archive.org/web/20170515211805/
+ *     http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
  *
  * The example code was distributed under the following license:
  *
diff --git a/Source/cmUuid.cxx b/Source/cmUuid.cxx
index 6688668..5f5d3e4 100644
--- a/Source/cmUuid.cxx
+++ b/Source/cmUuid.cxx
@@ -104,20 +104,20 @@
 
     size_t bytes = kUuidGroups[i];
     for (size_t j = 0; j < bytes; ++j) {
-      unsigned char byte = input[inputIndex++];
-      output += this->ByteToHex(byte);
+      unsigned char inputByte = input[inputIndex++];
+      output += this->ByteToHex(inputByte);
     }
   }
 
   return output;
 }
 
-std::string cmUuid::ByteToHex(unsigned char byte) const
+std::string cmUuid::ByteToHex(unsigned char inputByte) const
 {
   std::string result("  ");
   for (int i = 0; i < 2; ++i) {
-    unsigned char rest = byte % 16;
-    byte /= 16;
+    unsigned char rest = inputByte % 16;
+    inputByte /= 16;
     char c = (rest < 0xA) ? static_cast<char>('0' + rest)
                           : static_cast<char>('a' + (rest - 0xA));
     result.at(1 - i) = c;
diff --git a/Source/cmVSSetupHelper.cxx b/Source/cmVSSetupHelper.cxx
index 6702b7b..ccf24a0 100644
--- a/Source/cmVSSetupHelper.cxx
+++ b/Source/cmVSSetupHelper.cxx
@@ -63,15 +63,16 @@
 bool LoadVSInstanceVCToolsetVersion(VSInstanceInfo& vsInstanceInfo)
 {
   std::string const vcRoot = vsInstanceInfo.GetInstallLocation();
-  std::string vcToolsVersionFile =
-    vcRoot + "/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt";
+  std::string vcToolsVersionFile = cmStrCat(
+    vcRoot, "/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt");
   std::string vcToolsVersion;
   cmsys::ifstream fin(vcToolsVersionFile.c_str());
   if (!fin || !cmSystemTools::GetLineFromStream(fin, vcToolsVersion)) {
     return false;
   }
   vcToolsVersion = cmTrimWhitespace(vcToolsVersion);
-  std::string const vcToolsDir = vcRoot + "/VC/Tools/MSVC/" + vcToolsVersion;
+  std::string const vcToolsDir =
+    cmStrCat(vcRoot, "/VC/Tools/MSVC/", vcToolsVersion);
   if (!cmSystemTools::FileIsDirectory(vcToolsDir)) {
     return false;
   }
@@ -434,14 +435,14 @@
 
   std::string envVSCommonToolsDir;
   std::string envVSCommonToolsDirEnvName =
-    "VS" + std::to_string(this->Version) + "0COMNTOOLS";
+    cmStrCat("VS", std::to_string(this->Version), "0COMNTOOLS");
 
   if (cmSystemTools::GetEnv(envVSCommonToolsDirEnvName.c_str(),
                             envVSCommonToolsDir)) {
     cmSystemTools::ConvertToUnixSlashes(envVSCommonToolsDir);
   }
 
-  std::string const wantVersion = std::to_string(this->Version) + '.';
+  std::string const wantVersion = cmStrCat(std::to_string(this->Version), '.');
 
   bool specifiedLocationNotSpecifiedVersion = false;
 
diff --git a/Source/cmValue.h b/Source/cmValue.h
index f96d2f5..c924dda 100644
--- a/Source/cmValue.h
+++ b/Source/cmValue.h
@@ -4,7 +4,6 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
-#include <cstddef>
 #include <iosfwd>
 #include <string>
 
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 949d749..ce94fe1 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -14,15 +14,20 @@
 #include <cm/string_view>
 #include <cm/vector>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "windows.h"
+// include wincrypt.h after windows.h
+#include <wincrypt.h>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmComputeLinkInformation.h"
+#include "cmCryptoHash.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmFileSet.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorTarget.h"
@@ -100,7 +105,7 @@
   void SetHasElements()
   {
     if (!HasElements) {
-      this->S << ">";
+      this->S << '>';
       HasElements = true;
     }
   }
@@ -112,13 +117,13 @@
   }
   Elem& Attribute(const char* an, std::string av)
   {
-    this->S << " " << an << "=\"" << cmVS10EscapeAttr(std::move(av)) << "\"";
+    this->S << ' ' << an << "=\"" << cmVS10EscapeAttr(std::move(av)) << '"';
     return *this;
   }
   void Content(std::string val)
   {
     if (!this->HasContent) {
-      this->S << ">";
+      this->S << '>';
       this->HasContent = true;
     }
     this->S << cmVS10EscapeXML(std::move(val));
@@ -131,9 +136,9 @@
     }
 
     if (HasElements) {
-      this->WriteString("</") << this->Tag << ">";
+      this->WriteString("</") << this->Tag << '>';
     } else if (HasContent) {
-      this->S << "</" << this->Tag << ">";
+      this->S << "</" << this->Tag << '>';
     } else {
       this->S << " />";
     }
@@ -287,8 +292,8 @@
   this->TargetCompileAsWinRT = false;
   this->IsMissingFiles = false;
   this->DefaultArtifactDir =
-    this->LocalGenerator->GetCurrentBinaryDirectory() + "/" +
-    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
+    cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
+             this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget));
   this->InSourceBuild = (this->Makefile->GetCurrentSourceDirectory() ==
                          this->Makefile->GetCurrentBinaryDirectory());
   this->ClassifyAllConfigSources();
@@ -300,16 +305,16 @@
   const std::string& config) const
 {
   std::ostringstream oss;
-  oss << "'$(Configuration)|$(Platform)'=='";
-  oss << config << "|" << this->Platform;
-  oss << "'";
+  oss << "'$(Configuration)|$(Platform)'=='" << config << '|' << this->Platform
+      << '\'';
   // handle special case for 32 bit C# targets
   if (this->ProjectType == VsProjectType::csproj &&
-      this->Platform == "Win32") {
-    oss << " Or ";
-    oss << "'$(Configuration)|$(Platform)'=='";
-    oss << config << "|x86";
-    oss << "'";
+      this->Platform == "Win32"_s) {
+    oss << " Or "
+           "'$(Configuration)|$(Platform)'=='"
+        << config
+        << "|x86"
+           "'";
   }
   return oss.str();
 }
@@ -357,15 +362,6 @@
     this->GeneratorTarget->CheckCxxModuleStatus(config);
   }
 
-  if (this->GeneratorTarget->HaveCxx20ModuleSources() &&
-      !this->GlobalGenerator->SupportsCxxModuleDyndep()) {
-    this->Makefile->IssueMessage(
-      MessageType::FATAL_ERROR,
-      cmStrCat("The \"", this->GeneratorTarget->GetName(),
-               "\" target contains C++ module sources which are not supported "
-               "by the generator"));
-  }
-
   this->ProjectType = computeProjectType(this->GeneratorTarget);
   this->Managed = this->ProjectType == VsProjectType::csproj;
   const std::string ProjectFileExtension =
@@ -373,10 +369,10 @@
 
   if (this->ProjectType == VsProjectType::csproj &&
       this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
-    std::string message = "The C# target \"" +
-      this->GeneratorTarget->GetName() +
-      "\" is of type STATIC_LIBRARY. This is discouraged (and may be "
-      "disabled in future). Make it a SHARED library instead.";
+    std::string message =
+      cmStrCat("The C# target \"", this->GeneratorTarget->GetName(),
+               "\" is of type STATIC_LIBRARY. This is discouraged (and may be "
+               "disabled in future). Make it a SHARED library instead.");
     this->Makefile->IssueMessage(MessageType::DEPRECATION_WARNING, message);
   }
 
@@ -506,7 +502,7 @@
     // Setting ResolveNugetPackages to false skips this target and the build
     // succeeds.
     cm::string_view targetName{ this->GeneratorTarget->GetName() };
-    if (targetName == "ALL_BUILD" || targetName == "PACKAGE" ||
+    if (targetName == "ALL_BUILD"_s || targetName == "PACKAGE"_s ||
         targetName == CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
       Elem e1(e0, "PropertyGroup");
       e1.Element("ResolveNugetPackages", "false");
@@ -620,8 +616,10 @@
                .empty()) {
           e1.Element(
             "CudaToolkitCustomDir",
-            this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString() +
-              this->GlobalGenerator->GetPlatformToolsetCudaNvccSubdirString());
+            cmStrCat(
+              this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString(),
+              this->GlobalGenerator
+                ->GetPlatformToolsetCudaNvccSubdirString()));
         }
       }
 
@@ -725,15 +723,15 @@
           this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString();
         std::string cudaPath = customDir.empty()
           ? "$(VCTargetsPath)\\BuildCustomizations\\"
-          : customDir +
-            this->GlobalGenerator
-              ->GetPlatformToolsetCudaVSIntegrationSubdirString() +
-            R"(extras\visual_studio_integration\MSBuildExtensions\)";
+          : cmStrCat(customDir,
+                     this->GlobalGenerator
+                       ->GetPlatformToolsetCudaVSIntegrationSubdirString(),
+                     R"(extras\visual_studio_integration\MSBuildExtensions\)");
         Elem(e1, "Import")
           .Attribute("Project",
-                     std::move(cudaPath) + "CUDA " +
-                       this->GlobalGenerator->GetPlatformToolsetCuda() +
-                       ".props");
+                     cmStrCat(std::move(cudaPath), "CUDA ",
+                              this->GlobalGenerator->GetPlatformToolsetCuda(),
+                              ".props"));
       }
       if (this->GlobalGenerator->IsMarmasmEnabled()) {
         Elem(e1, "Import")
@@ -779,7 +777,7 @@
         ConvertToWindowsSlash(props);
         Elem(e1, "Import")
           .Attribute("Project", props)
-          .Attribute("Condition", "exists('" + props + "')")
+          .Attribute("Condition", cmStrCat("exists('", props, "')"))
           .Attribute("Label", "LocalAppDataPlatform");
       }
 
@@ -829,15 +827,15 @@
           this->GlobalGenerator->GetPlatformToolsetCudaCustomDirString();
         std::string cudaPath = customDir.empty()
           ? "$(VCTargetsPath)\\BuildCustomizations\\"
-          : customDir +
-            this->GlobalGenerator
-              ->GetPlatformToolsetCudaVSIntegrationSubdirString() +
-            R"(extras\visual_studio_integration\MSBuildExtensions\)";
+          : cmStrCat(customDir,
+                     this->GlobalGenerator
+                       ->GetPlatformToolsetCudaVSIntegrationSubdirString(),
+                     R"(extras\visual_studio_integration\MSBuildExtensions\)");
         Elem(e1, "Import")
           .Attribute("Project",
-                     std::move(cudaPath) + "CUDA " +
-                       this->GlobalGenerator->GetPlatformToolsetCuda() +
-                       ".targets");
+                     cmStrCat(std::move(cudaPath), "CUDA ",
+                              this->GlobalGenerator->GetPlatformToolsetCuda(),
+                              ".targets"));
       }
       if (this->GlobalGenerator->IsMarmasmEnabled()) {
         Elem(e1, "Import")
@@ -864,7 +862,8 @@
     if (this->ProjectType == VsProjectType::csproj) {
       for (std::string const& c : this->Configurations) {
         Elem e1(e0, "PropertyGroup");
-        e1.Attribute("Condition", "'$(Configuration)' == '" + c + "'");
+        e1.Attribute("Condition",
+                     cmStrCat("'$(Configuration)' == '", c, '\''));
         e1.SetHasElements();
         this->WriteEvents(e1, c);
       }
@@ -877,7 +876,7 @@
           oss << "      " << i << ";\n";
         }
         oss << "      "
-            << "$(BuildDependsOn)\n";
+               "$(BuildDependsOn)\n";
         e1.Element("BuildDependsOn", oss.str());
       }
     }
@@ -889,17 +888,19 @@
 {
   if (this->ProjectType != VsProjectType::csproj ||
       !this->GeneratorTarget->IsDotNetSdkTarget()) {
-    std::string message = "The target \"" + this->GeneratorTarget->GetName() +
-      "\" is not eligible for .Net SDK style project.";
+    std::string message =
+      cmStrCat("The target \"", this->GeneratorTarget->GetName(),
+               "\" is not eligible for .Net SDK style project.");
     this->Makefile->IssueMessage(MessageType::INTERNAL_ERROR, message);
     return;
   }
 
   if (this->HasCustomCommands()) {
-    std::string message = "The target \"" + this->GeneratorTarget->GetName() +
+    std::string message = cmStrCat(
+      "The target \"", this->GeneratorTarget->GetName(),
       "\" does not currently support add_custom_command as the Visual Studio "
       "generators have not yet learned how to generate custom commands in "
-      ".Net SDK-style projects.";
+      ".Net SDK-style projects.");
     this->Makefile->IssueMessage(MessageType::FATAL_ERROR, message);
     return;
   }
@@ -982,11 +983,13 @@
 
   for (const std::string& config : this->Configurations) {
     Elem e1(e0, "PropertyGroup");
-    e1.Attribute("Condition", "'$(Configuration)' == '" + config + "'");
+    e1.Attribute("Condition",
+                 cmStrCat("'$(Configuration)' == '", config, '\''));
     e1.SetHasElements();
     this->WriteEvents(e1, config);
 
-    std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/";
+    std::string outDir =
+      cmStrCat(this->GeneratorTarget->GetDirectory(config), '/');
     ConvertToWindowsSlash(outDir);
     e1.Element("OutputPath", outDir);
 
@@ -1007,7 +1010,7 @@
 void cmVisualStudio10TargetGenerator::WriteCommonPropertyGroupGlobals(Elem& e1)
 {
   e1.Attribute("Label", "Globals");
-  e1.Element("ProjectGuid", "{" + this->GUID + "}");
+  e1.Element("ProjectGuid", cmStrCat('{', this->GUID, '}'));
 
   cmValue vsProjectTypes =
     this->GeneratorTarget->GetProperty("VS_GLOBAL_PROJECT_TYPES");
@@ -1045,8 +1048,8 @@
     }
     cm::string_view globalKey = cm::string_view(keyIt).substr(prefix.length());
     // Skip invalid or separately-handled properties.
-    if (globalKey.empty() || globalKey == "PROJECT_TYPES" ||
-        globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") {
+    if (globalKey.empty() || globalKey == "PROJECT_TYPES"_s ||
+        globalKey == "ROOTNAMESPACE"_s || globalKey == "KEYWORD"_s) {
       continue;
     }
     cmValue value = this->GeneratorTarget->GetProperty(keyIt);
@@ -1103,7 +1106,8 @@
     if (cmHasPrefix(i.first, vsDnRef)) {
       std::string path = i.second;
       if (!cmsys::SystemTools::FileIsFullPath(path)) {
-        path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
+        path =
+          cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', path);
       }
       ConvertToWindowsSlash(path);
       this->DotNetHintReferences[""].emplace_back(
@@ -1172,7 +1176,8 @@
     cmList argsSplit{ *imports };
     for (auto& path : argsSplit) {
       if (!cmsys::SystemTools::FileIsFullPath(path)) {
-        path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
+        path =
+          cmStrCat(this->Makefile->GetCurrentSourceDirectory(), '/', path);
       }
       ConvertToWindowsSlash(path);
       Elem e1(e0, "Import");
@@ -1187,7 +1192,8 @@
 
   static const std::string refpropPrefix = "VS_DOTNET_REFERENCEPROP_";
   static const std::string refpropInfix = "_TAG_";
-  const std::string refPropFullPrefix = refpropPrefix + ref + refpropInfix;
+  const std::string refPropFullPrefix =
+    cmStrCat(refpropPrefix, ref, refpropInfix);
   using CustomTags = std::map<std::string, std::string>;
   CustomTags tags;
   cmPropertyMap const& props = this->GeneratorTarget->Target->GetProperties();
@@ -1239,7 +1245,8 @@
       e2.Attribute("Include", obj);
 
       if (this->ProjectType != VsProjectType::csproj) {
-        std::string hFileName = obj.substr(0, obj.find_last_of('.')) + ".h";
+        std::string hFileName =
+          cmStrCat(obj.substr(0, obj.find_last_of('.')), ".h");
         e2.Element("DependentUpon", hFileName);
 
         for (std::string const& c : this->Configurations) {
@@ -1265,10 +1272,10 @@
           e2.Element("Link", link);
         }
         // Determine if this is a generated resource from a .Designer.cs file
-        std::string designerResource =
-          cmSystemTools::GetFilenamePath(oi->GetFullPath()) + "/" +
-          cmSystemTools::GetFilenameWithoutLastExtension(oi->GetFullPath()) +
-          ".Designer.cs";
+        std::string designerResource = cmStrCat(
+          cmSystemTools::GetFilenamePath(oi->GetFullPath()), '/',
+          cmSystemTools::GetFilenameWithoutLastExtension(oi->GetFullPath()),
+          ".Designer.cs");
         if (cmsys::SystemTools::FileExists(designerResource)) {
           std::string generator = "PublicResXFileCodeGenerator";
           if (cmValue g = oi->GetProperty("VS_RESOURCE_GENERATOR")) {
@@ -1333,7 +1340,7 @@
 {
   if (this->MSTools) {
     if (this->GlobalGenerator->TargetsWindowsPhone() &&
-        this->GlobalGenerator->GetSystemVersion() == "8.0") {
+        this->GlobalGenerator->GetSystemVersion() == "8.0"_s) {
       Elem(e0, "Import")
         .Attribute("Project",
                    "$(MSBuildExtensionsPath)\\Microsoft\\WindowsPhone\\v"
@@ -1354,9 +1361,9 @@
         if (j > 0) {
           oss << " Or ";
         }
-        oss << "'$(Configuration)'=='" << tac.Configs[j] << "'";
+        oss << "'$(Configuration)'=='" << tac.Configs[j] << '\'';
       }
-      oss << ")";
+      oss << ')';
     }
 
     Elem(e1, "Import")
@@ -1374,7 +1381,7 @@
   }
 
   if (this->GlobalGenerator->TargetsWindowsPhone() &&
-      this->GlobalGenerator->GetSystemVersion() == "8.0" &&
+      this->GlobalGenerator->GetSystemVersion() == "8.0"_s &&
       references.empty()) {
     references.push_back(std::string{ "platform.winmd" });
   }
@@ -1396,7 +1403,7 @@
   e1.Attribute("Label", "ProjectConfigurations");
   for (std::string const& c : this->Configurations) {
     Elem e2(e1, "ProjectConfiguration");
-    e2.Attribute("Include", c + "|" + this->Platform);
+    e2.Attribute("Include", cmStrCat(c, '|', this->Platform));
     e2.Element("Configuration", c);
     e2.Element("Platform", this->Platform);
   }
@@ -1504,9 +1511,9 @@
 
     std::string useOfMfcValue = "false";
     if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) {
-      if (mfcFlagValue == "1") {
+      if (mfcFlagValue == "1"_s) {
         useOfMfcValue = "Static";
-      } else if (mfcFlagValue == "2") {
+      } else if (mfcFlagValue == "2"_s) {
         useOfMfcValue = "Dynamic";
       }
     }
@@ -1572,7 +1579,8 @@
     e1.Element("DefineDebug", "true");
   }
 
-  std::string outDir = this->GeneratorTarget->GetDirectory(config) + "/";
+  std::string outDir =
+    cmStrCat(this->GeneratorTarget->GetDirectory(config), '/');
   ConvertToWindowsSlash(outDir);
   e1.Element("OutputPath", outDir);
 
@@ -1599,7 +1607,7 @@
 
   if (cmStateEnums::EXECUTABLE == this->GeneratorTarget->GetType()) {
     e1.Element("StartAction", "Program");
-    e1.Element("StartProgram", outDir + assemblyName + ".exe");
+    e1.Element("StartProgram", cmStrCat(outDir, assemblyName, ".exe"));
   }
 
   OptionsHelper oh(o, e1);
@@ -1614,10 +1622,10 @@
   const char* toolset = gg->GetPlatformToolset();
   e1.Element("NdkToolchainVersion", toolset ? toolset : "Default");
   if (cmValue minApi = this->GeneratorTarget->GetProperty("ANDROID_API_MIN")) {
-    e1.Element("AndroidMinAPI", "android-" + *minApi);
+    e1.Element("AndroidMinAPI", cmStrCat("android-", *minApi));
   }
   if (cmValue api = this->GeneratorTarget->GetProperty("ANDROID_API")) {
-    e1.Element("AndroidTargetAPI", "android-" + *api);
+    e1.Element("AndroidTargetAPI", cmStrCat("android-", *api));
   }
 
   if (cmValue cpuArch = this->GeneratorTarget->GetProperty("ANDROID_ARCH")) {
@@ -1642,7 +1650,7 @@
   }
   if (cmValue stlType =
         this->GeneratorTarget->GetProperty("ANDROID_STL_TYPE")) {
-    if (*stlType != "none") {
+    if (*stlType != "none"_s) {
       e1.Element("UseOfStl", *stlType);
     }
   }
@@ -1720,9 +1728,9 @@
         // preventing dependent rebuilds.
         this->ForceOld(sourcePath);
       } else {
-        std::string error =
-          cmStrCat("Could not create file: [", sourcePath, "]  ");
-        cmSystemTools::Error(error + cmSystemTools::GetLastSystemError());
+        cmSystemTools::Error(cmStrCat("Could not create file: [", sourcePath,
+                                      "]  ",
+                                      cmSystemTools::GetLastSystemError()));
       }
     }
   }
@@ -1807,8 +1815,9 @@
     }
     script += lg->FinishConstructScript(this->ProjectType);
     if (this->ProjectType == VsProjectType::csproj) {
-      std::string name = "CustomCommand_" + c + "_" +
-        cmSystemTools::ComputeStringMD5(sourcePath);
+      cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
+      std::string name =
+        cmStrCat("CustomCommand_", c, '_', hasher.HashString(sourcePath));
       this->WriteCustomRuleCSharp(e0, c, name, script, additional_inputs.str(),
                                   outputs.str(), comment, ccg);
     } else {
@@ -1879,7 +1888,7 @@
   e1.S << "\n    Inputs=\"" << cmVS10EscapeAttr(inputs) << "\"";
   e1.S << "\n    Outputs=\"" << cmVS10EscapeAttr(outputs) << "\"";
   if (!comment.empty()) {
-    Elem(e1, "Exec").Attribute("Command", "echo " + comment);
+    Elem(e1, "Exec").Attribute("Command", cmStrCat("echo ", comment));
   }
   Elem(e1, "Exec").Attribute("Command", script);
 }
@@ -1961,7 +1970,7 @@
       for (std::string const& oi : this->AddedFiles) {
         std::string fileName =
           cmSystemTools::LowerCase(cmSystemTools::GetFilenameName(oi));
-        if (fileName == "wmappmanifest.xml") {
+        if (fileName == "wmappmanifest.xml"_s) {
           Elem e2(e1, "XML");
           e2.Attribute("Include", oi);
           e2.Element("Filter", "Resource Files");
@@ -1970,7 +1979,7 @@
           Elem e2(e1, "AppxManifest");
           e2.Attribute("Include", oi);
           e2.Element("Filter", "Resource Files");
-        } else if (cmSystemTools::GetFilenameExtension(fileName) == ".pfx") {
+        } else if (cmSystemTools::GetFilenameExtension(fileName) == ".pfx"_s) {
           Elem e2(e1, "None");
           e2.Attribute("Include", oi);
           e2.Element("Filter", "Resource Files");
@@ -2004,11 +2013,11 @@
       for (cmSourceGroup const* sg : groupsVec) {
         std::string const& name = sg->GetFullName();
         if (!name.empty()) {
-          std::string guidName = "SG_Filter_" + name;
+          std::string guidName = cmStrCat("SG_Filter_", name);
           std::string guid = this->GlobalGenerator->GetGUID(guidName);
           Elem e2(e1, "Filter");
           e2.Attribute("Include", name);
-          e2.Element("UniqueIdentifier", "{" + guid + "}");
+          e2.Element("UniqueIdentifier", cmStrCat('{', guid, '}'));
         }
       }
 
@@ -2017,7 +2026,7 @@
         std::string guid = this->GlobalGenerator->GetGUID(guidName);
         Elem e2(e1, "Filter");
         e2.Attribute("Include", "Resource Files");
-        e2.Element("UniqueIdentifier", "{" + guid + "}");
+        e2.Element("UniqueIdentifier", cmStrCat('{', guid, '}'));
         e2.Element("Extensions",
                    "rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;"
                    "gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms");
@@ -2173,7 +2182,7 @@
   if (this->ProjectType == VsProjectType::csproj && !this->InSourceBuild) {
     toolHasSettings = true;
   }
-  if (ext == "hlsl") {
+  if (ext == "hlsl"_s) {
     tool = "FXCompile";
     // Figure out the type of shader compiler to use.
     if (cmValue st = sf->GetProperty("VS_SHADER_TYPE")) {
@@ -2251,22 +2260,22 @@
         toolSettings[config]["ObjectFileOutput"] = *sofn;
       }
     }
-  } else if (ext == "jpg" || ext == "png") {
+  } else if (ext == "jpg"_s || ext == "png"_s) {
     tool = "Image";
-  } else if (ext == "resw") {
+  } else if (ext == "resw"_s) {
     tool = "PRIResource";
-  } else if (ext == "xml") {
+  } else if (ext == "xml"_s) {
     tool = "XML";
-  } else if (ext == "natvis") {
+  } else if (ext == "natvis"_s) {
     tool = "Natvis";
-  } else if (ext == "settings") {
+  } else if (ext == "settings"_s) {
     settingsLastGenOutput =
       cmsys::SystemTools::GetFilenameName(sf->GetFullPath());
     std::size_t pos = settingsLastGenOutput.find(".settings");
     settingsLastGenOutput.replace(pos, 9, ".Designer.cs");
     settingsGenerator = "SettingsSingleFileGenerator";
     toolHasSettings = true;
-  } else if (ext == "vsixmanifest") {
+  } else if (ext == "vsixmanifest"_s) {
     subType = "Designer";
   }
   if (cmValue c = sf->GetProperty("VS_COPY_TO_OUT_DIR")) {
@@ -2287,13 +2296,13 @@
   if (this->NsightTegra) {
     // Nsight Tegra needs specific file types to check up-to-dateness.
     std::string name = cmSystemTools::LowerCase(sf->GetLocation().GetName());
-    if (name == "androidmanifest.xml" || name == "build.xml" ||
-        name == "proguard.cfg" || name == "proguard-project.txt" ||
-        ext == "properties") {
+    if (name == "androidmanifest.xml"_s || name == "build.xml"_s ||
+        name == "proguard.cfg"_s || name == "proguard-project.txt"_s ||
+        ext == "properties"_s) {
       tool = "AndroidBuild";
-    } else if (ext == "java") {
+    } else if (ext == "java"_s) {
       tool = "JCompile";
-    } else if (ext == "asm" || ext == "s") {
+    } else if (ext == "asm"_s || ext == "s"_s) {
       tool = "ClCompile";
     }
   }
@@ -2321,7 +2330,7 @@
 
   if (ParsedToolTargetSettings.find(tool) == ParsedToolTargetSettings.end()) {
     cmValue toolTargetProperty = this->GeneratorTarget->Target->GetProperty(
-      "VS_SOURCE_SETTINGS_" + std::string(tool));
+      cmStrCat("VS_SOURCE_SETTINGS_", tool));
     ConfigToSettings toolTargetSettings;
     if (toolTargetProperty) {
       ParseSettingsProperty(*toolTargetProperty, toolTargetSettings);
@@ -2353,19 +2362,22 @@
         ge.Parse(deployContent);
       // Deployment location cannot be set on a configuration basis
       if (!deployLocation.empty()) {
-        e2.Element("Link", deployLocation + "\\%(FileName)%(Extension)");
+        e2.Element("Link",
+                   cmStrCat(deployLocation, "\\%(FileName)%(Extension)"));
       }
       for (auto& config : this->Configurations) {
-        if (cge->Evaluate(this->LocalGenerator, config) == "1") {
-          e2.WritePlatformConfigTag("DeploymentContent",
-                                    "'$(Configuration)|$(Platform)'=='" +
-                                      config + "|" + this->Platform + "'",
-                                    "true");
+        if (cge->Evaluate(this->LocalGenerator, config) == "1"_s) {
+          e2.WritePlatformConfigTag(
+            "DeploymentContent",
+            cmStrCat("'$(Configuration)|$(Platform)'=='", config, '|',
+                     this->Platform, '\''),
+            "true");
         } else {
-          e2.WritePlatformConfigTag("ExcludedFromBuild",
-                                    "'$(Configuration)|$(Platform)'=='" +
-                                      config + "|" + this->Platform + "'",
-                                    "true");
+          e2.WritePlatformConfigTag(
+            "ExcludedFromBuild",
+            cmStrCat("'$(Configuration)|$(Platform)'=='", config, '|',
+                     this->Platform, '\''),
+            "true");
         }
       }
     }
@@ -2401,7 +2413,7 @@
   // conversion uses full paths when possible to allow deeper trees.
   // However, CUDA 8.0 msbuild rules fail on absolute paths so for CUDA
   // we must use relative paths.
-  bool forceRelative = sf->GetLanguage() == "CUDA";
+  bool forceRelative = sf->GetLanguage() == "CUDA"_s;
   std::string sourceFile = this->ConvertPath(sf->GetFullPath(), forceRelative);
   ConvertToWindowsSlash(sourceFile);
   e2.Attribute("Include", sourceFile);
@@ -2480,18 +2492,6 @@
         break;
       case cmGeneratorTarget::SourceKindExternalObject:
         tool = "Object";
-        if (this->LocalGenerator->GetVersion() <
-            cmGlobalVisualStudioGenerator::VSVersion::VS11) {
-          // For VS == 10 we cannot use LinkObjects to avoid linking custom
-          // command outputs.  If an object file is generated in this target,
-          // then vs10 will use it in the build, and we have to list it as
-          // None instead of Object.
-          std::vector<cmSourceFile*> const* d =
-            this->GeneratorTarget->GetSourceDepends(si.Source);
-          if (d && !d->empty()) {
-            tool = "None";
-          }
-        }
         break;
       case cmGeneratorTarget::SourceKindExtra:
         this->WriteExtraSource(e1, si.Source, toolSettings);
@@ -2508,25 +2508,27 @@
       case cmGeneratorTarget::SourceKindModuleDefinition:
         tool = "None";
         break;
+      case cmGeneratorTarget::SourceKindCxxModuleSource:
       case cmGeneratorTarget::SourceKindUnityBatched:
       case cmGeneratorTarget::SourceKindObjectSource: {
         const std::string& lang = si.Source->GetLanguage();
-        if (lang == "C" || lang == "CXX") {
+        if (lang == "C"_s || lang == "CXX"_s) {
           tool = "ClCompile";
-        } else if (lang == "ASM_MARMASM" &&
+        } else if (lang == "ASM_MARMASM"_s &&
                    this->GlobalGenerator->IsMarmasmEnabled()) {
           tool = "MARMASM";
-        } else if (lang == "ASM_MASM" &&
+        } else if (lang == "ASM_MASM"_s &&
                    this->GlobalGenerator->IsMasmEnabled()) {
           tool = "MASM";
-        } else if (lang == "ASM_NASM" &&
+        } else if (lang == "ASM_NASM"_s &&
                    this->GlobalGenerator->IsNasmEnabled()) {
           tool = "NASM";
-        } else if (lang == "RC") {
+        } else if (lang == "RC"_s) {
           tool = "ResourceCompile";
-        } else if (lang == "CSharp") {
+        } else if (lang == "CSharp"_s) {
           tool = "Compile";
-        } else if (lang == "CUDA" && this->GlobalGenerator->IsCudaEnabled()) {
+        } else if (lang == "CUDA"_s &&
+                   this->GlobalGenerator->IsCudaEnabled()) {
           tool = "CudaCompile";
         } else {
           tool = "None";
@@ -2555,7 +2557,7 @@
                           std::back_inserter(exclude_configs));
 
       Elem e2(e1, tool);
-      bool isCSharp = (si.Source->GetLanguage() == "CSharp");
+      bool isCSharp = (si.Source->GetLanguage() == "CSharp"_s);
       if (isCSharp && !exclude_configs.empty()) {
         std::stringstream conditions;
         bool firstConditionSet{ false };
@@ -2563,8 +2565,9 @@
           if (firstConditionSet) {
             conditions << " Or ";
           }
-          conditions << "('$(Configuration)|$(Platform)'=='" +
-              this->Configurations[ci] + "|" + this->Platform + "')";
+          conditions << "('$(Configuration)|$(Platform)'=='"
+                     << this->Configurations[ci] << '|' << this->Platform
+                     << "')";
           firstConditionSet = true;
         }
         e2.Attribute("Condition", conditions.str());
@@ -2658,9 +2661,9 @@
         writtenSettings.push_back(setting.first);
       } else {
         e2.WritePlatformConfigTag(setting.first,
-                                  "'$(Configuration)|$(Platform)'=='" +
-                                    configSettings.first + "|" +
-                                    this->Platform + "'",
+                                  cmStrCat("'$(Configuration)|$(Platform)'=='",
+                                           configSettings.first, '|',
+                                           this->Platform, '\''),
                                   setting.second);
       }
     }
@@ -2708,33 +2711,33 @@
   // Force language if the file extension does not match.
   // Note that MSVC treats the upper-case '.C' extension as C and not C++.
   std::string const ext = sf.GetExtension();
-  std::string const extLang = ext == "C"
+  std::string const extLang = ext == "C"_s
     ? "C"
     : this->GlobalGenerator->GetLanguageFromExtension(ext.c_str());
   std::string lang = this->LocalGenerator->GetSourceFileLanguage(sf);
   const char* compileAs = nullptr;
   if (lang != extLang) {
-    if (lang == "CXX") {
+    if (lang == "CXX"_s) {
       // force a C++ file type
       compileAs = "CompileAsCpp";
-    } else if (lang == "C") {
+    } else if (lang == "C"_s) {
       // force to c
       compileAs = "CompileAsC";
     }
   }
 
-  bool noWinRT = this->TargetCompileAsWinRT && lang == "C";
+  bool noWinRT = this->TargetCompileAsWinRT && lang == "C"_s;
   // for the first time we need a new line if there is something
   // produced here.
   if (!objectName.empty()) {
-    if (lang == "CUDA") {
-      e2.Element("CompileOut", "$(IntDir)/" + objectName);
+    if (lang == "CUDA"_s) {
+      e2.Element("CompileOut", cmStrCat("$(IntDir)/", objectName));
     } else {
-      e2.Element("ObjectFileName", "$(IntDir)/" + objectName);
+      e2.Element("ObjectFileName", cmStrCat("$(IntDir)/", objectName));
     }
   }
 
-  if (lang == "ASM_NASM") {
+  if (lang == "ASM_NASM"_s) {
     if (cmValue objectDeps = sf.GetProperty("OBJECT_DEPENDS")) {
       cmList depends{ *objectDeps };
       for (auto& d : depends) {
@@ -2754,7 +2757,7 @@
     std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
     if (cmValue ccdefs = sf.GetProperty(defPropName)) {
       if (!configDefines.empty()) {
-        configDefines += ";";
+        configDefines += ';';
       }
       configDependentDefines |=
         cmGeneratorExpression::Find(*ccdefs) != std::string::npos;
@@ -2819,20 +2822,20 @@
       cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator;
       cmIDEFlagTable const* flagtable = nullptr;
       const std::string& srclang = source->GetLanguage();
-      if (srclang == "C" || srclang == "CXX") {
+      if (srclang == "C"_s || srclang == "CXX"_s) {
         flagtable = gg->GetClFlagTable();
-      } else if (srclang == "ASM_MARMASM" &&
+      } else if (srclang == "ASM_MARMASM"_s &&
                  this->GlobalGenerator->IsMarmasmEnabled()) {
         flagtable = gg->GetMarmasmFlagTable();
-      } else if (srclang == "ASM_MASM" &&
+      } else if (srclang == "ASM_MASM"_s &&
                  this->GlobalGenerator->IsMasmEnabled()) {
         flagtable = gg->GetMasmFlagTable();
-      } else if (lang == "ASM_NASM" &&
+      } else if (lang == "ASM_NASM"_s &&
                  this->GlobalGenerator->IsNasmEnabled()) {
         flagtable = gg->GetNasmFlagTable();
-      } else if (srclang == "RC") {
+      } else if (srclang == "RC"_s) {
         flagtable = gg->GetRcFlagTable();
-      } else if (srclang == "CSharp") {
+      } else if (srclang == "CSharp"_s) {
         flagtable = gg->GetCSharpFlagTable();
       }
       cmGeneratorExpressionInterpreter genexInterpreter(
@@ -2957,9 +2960,9 @@
 {
   for (size_t ci : exclude_configs) {
     e2.WritePlatformConfigTag("ExcludedFromBuild",
-                              "'$(Configuration)|$(Platform)'=='" +
-                                this->Configurations[ci] + "|" +
-                                this->Platform + "'",
+                              cmStrCat("'$(Configuration)|$(Platform)'=='",
+                                       this->Configurations[ci], '|',
+                                       this->Platform, '\''),
                               "true");
   }
 }
@@ -2993,7 +2996,7 @@
         outDir = intermediateDir;
         targetNameFull = cmStrCat(this->GeneratorTarget->GetName(), ".lib");
       } else {
-        outDir = this->GeneratorTarget->GetDirectory(config) + "/";
+        outDir = cmStrCat(this->GeneratorTarget->GetDirectory(config), '/');
         targetNameFull = this->GeneratorTarget->GetFullName(config);
       }
       ConvertToWindowsSlash(intermediateDir);
@@ -3178,7 +3181,7 @@
   }
   const auto& nameComponents =
     this->GeneratorTarget->GetFullNameComponents(config);
-  return nameComponents.prefix + nameComponents.base;
+  return cmStrCat(nameComponents.prefix, nameComponents.base);
 }
 
 bool cmVisualStudio10TargetGenerator::ComputeClOptions()
@@ -3216,8 +3219,8 @@
   const std::string& linkLanguage =
     this->GeneratorTarget->GetLinkerLanguage(configName);
   if (linkLanguage.empty()) {
-    cmSystemTools::Error(
-      "CMake can not determine linker language for target: " + this->Name);
+    cmSystemTools::Error(cmStrCat(
+      "CMake can not determine linker language for target: ", this->Name));
     return false;
   }
 
@@ -3309,11 +3312,11 @@
         defineFlags.find("/clr") != std::string::npos ||
         defineFlags.find("-clr") != std::string::npos) {
       if (configName == this->Configurations[0]) {
-        std::string message = "For the target \"" +
-          this->GeneratorTarget->GetName() +
-          "\" the /clr compiler flag was added manually. " +
-          "Set usage of C++/CLI by setting COMMON_LANGUAGE_RUNTIME "
-          "target property.";
+        std::string message =
+          cmStrCat("For the target \"", this->GeneratorTarget->GetName(),
+                   "\" the /clr compiler flag was added manually. ",
+                   "Set usage of C++/CLI by setting COMMON_LANGUAGE_RUNTIME "
+                   "target property.");
         this->Makefile->IssueMessage(MessageType::WARNING, message);
       }
     }
@@ -3321,9 +3324,9 @@
           this->GeneratorTarget->GetProperty("COMMON_LANGUAGE_RUNTIME")) {
       std::string clrString = *clr;
       if (!clrString.empty()) {
-        clrString = ":" + clrString;
+        clrString = cmStrCat(':', clrString);
       }
-      flags += " /clr" + clrString;
+      flags += cmStrCat(" /clr", clrString);
     }
   }
 
@@ -3396,7 +3399,7 @@
   }
 
   // Add C-specific flags expressible in a ClCompile meant for C++.
-  if (langForClCompile == "CXX") {
+  if (langForClCompile == "CXX"_s) {
     std::set<std::string> languages;
     this->GeneratorTarget->GetLanguages(languages, configName);
     if (languages.count("C")) {
@@ -3451,7 +3454,7 @@
     std::string managedType = clOptions.HasFlag("CompileAsManaged")
       ? clOptions.GetFlag("CompileAsManaged")
       : "Mixed";
-    if (managedType == "Safe" || managedType == "Pure") {
+    if (managedType == "Safe"_s || managedType == "Pure"_s) {
       // force empty calling convention if safe clr is used
       clOptions.AddFlag("CallingConvention", "");
     }
@@ -3475,7 +3478,7 @@
 
   // Remove any target-wide -TC or -TP flag added by the project.
   // Such flags are unnecessary and break our model of language selection.
-  if (langForClCompile == "C" || langForClCompile == "CXX") {
+  if (langForClCompile == "C"_s || langForClCompile == "CXX"_s) {
     clOptions.RemoveFlag("CompileAs");
   }
 
@@ -3570,9 +3573,10 @@
   Options& rcOptions = *pOptions;
 
   std::string CONFIG = cmSystemTools::UpperCase(configName);
-  std::string rcConfigFlagsVar = "CMAKE_RC_FLAGS_" + CONFIG;
-  std::string flags = this->Makefile->GetSafeDefinition("CMAKE_RC_FLAGS") +
-    " " + this->Makefile->GetSafeDefinition(rcConfigFlagsVar);
+  std::string rcConfigFlagsVar = cmStrCat("CMAKE_RC_FLAGS_", CONFIG);
+  std::string flags =
+    cmStrCat(this->Makefile->GetSafeDefinition("CMAKE_RC_FLAGS"), ' ',
+             this->Makefile->GetSafeDefinition(rcConfigFlagsVar));
 
   rcOptions.Parse(flags);
 
@@ -3713,7 +3717,7 @@
       // limitation by creating the directory and passing the flag ourselves.
       pdb = this->ConvertPath(pdb, true);
       ConvertToWindowsSlash(pdb);
-      std::string const clFd = R"(-Xcompiler="-Fd\")" + pdb + R"(\"")";
+      std::string const clFd = cmStrCat(R"(-Xcompiler="-Fd\")", pdb, R"(\"")");
       cudaOptions.AppendFlagString("AdditionalOptions", clFd);
     }
   }
@@ -3721,7 +3725,7 @@
   // CUDA automatically passes the proper '--machine' flag to nvcc
   // for the current architecture, but does not reflect this default
   // in the user-visible IDE settings.  Set it explicitly.
-  if (this->Platform == "x64") {
+  if (this->Platform == "x64"_s) {
     cudaOptions.AddFlag("TargetMachinePlatform", "64");
   }
 
@@ -3738,7 +3742,8 @@
     cudaOptions.RemoveFlag("AdditionalCompilerOptions");
     if (!aco.empty()) {
       aco = this->LocalGenerator->EscapeForShell(aco, false);
-      cudaOptions.AppendFlagString("AdditionalOptions", "-Xcompiler=" + aco);
+      cudaOptions.AppendFlagString("AdditionalOptions",
+                                   cmStrCat("-Xcompiler=", aco));
     }
   }
 
@@ -3764,11 +3769,11 @@
   // Add runtime library selection flag.
   std::string const& cudaRuntime =
     this->GeneratorTarget->GetRuntimeLinkLibrary("CUDA", configName);
-  if (cudaRuntime == "STATIC") {
+  if (cudaRuntime == "STATIC"_s) {
     cudaOptions.AddFlag("CudaRuntime", "Static");
-  } else if (cudaRuntime == "SHARED") {
+  } else if (cudaRuntime == "SHARED"_s) {
     cudaOptions.AddFlag("CudaRuntime", "Shared");
-  } else if (cudaRuntime == "NONE") {
+  } else if (cudaRuntime == "NONE"_s) {
     cudaOptions.AddFlag("CudaRuntime", "None");
   }
 
@@ -3850,22 +3855,41 @@
   }
   cudaLinkOptions.AppendFlagString("AdditionalOptions", linkFlags);
 
-  // For static libraries that have device linking enabled compute
-  // the  libraries
-  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY &&
-      doDeviceLinking) {
-    cmComputeLinkInformation& cli = *pcli;
-    cmLinkLineDeviceComputer computer(
-      this->LocalGenerator,
-      this->LocalGenerator->GetStateSnapshot().GetDirectory());
-    std::vector<BT<std::string>> btLibVec;
-    computer.ComputeLinkLibraries(cli, std::string{}, btLibVec);
+  if (doDeviceLinking) {
     std::vector<std::string> libVec;
-    for (auto const& item : btLibVec) {
-      libVec.emplace_back(item.Value);
+    auto const& kinded = this->GeneratorTarget->GetKindedSources(configName);
+    // CMake conversion uses full paths when possible to allow deeper trees.
+    // However, CUDA 8.0 msbuild rules fail on absolute paths so for CUDA
+    // we must use relative paths.
+    const bool forceRelative = true;
+    for (cmGeneratorTarget::SourceAndKind const& si : kinded.Sources) {
+      switch (si.Kind) {
+        case cmGeneratorTarget::SourceKindExternalObject: {
+          std::string path =
+            this->ConvertPath(si.Source.Value->GetFullPath(), forceRelative);
+          ConvertToWindowsSlash(path);
+          libVec.emplace_back(std::move(path));
+        } break;
+        default:
+          break;
+      }
     }
-
-    cudaLinkOptions.AddFlag("AdditionalDependencies", libVec);
+    // For static libraries that have device linking enabled compute
+    // the  libraries
+    if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
+      cmComputeLinkInformation& cli = *pcli;
+      cmLinkLineDeviceComputer computer(
+        this->LocalGenerator,
+        this->LocalGenerator->GetStateSnapshot().GetDirectory());
+      std::vector<BT<std::string>> btLibVec;
+      computer.ComputeLinkLibraries(cli, std::string{}, btLibVec);
+      for (auto const& item : btLibVec) {
+        libVec.emplace_back(item.Value);
+      }
+    }
+    if (!libVec.empty()) {
+      cudaLinkOptions.AddFlag("AdditionalDependencies", libVec);
+    }
   }
 
   this->CudaLinkOptions[configName] = std::move(pOptions);
@@ -4115,14 +4139,15 @@
       e2.Element("AdditionalManifestFiles", oss.str());
     }
     if (dpiAware) {
-      if (*dpiAware == "PerMonitor") {
+      if (*dpiAware == "PerMonitor"_s) {
         e2.Element("EnableDpiAwareness", "PerMonitorHighDPIAware");
       } else if (cmIsOn(*dpiAware)) {
         e2.Element("EnableDpiAwareness", "true");
       } else if (cmIsOff(*dpiAware)) {
         e2.Element("EnableDpiAwareness", "false");
       } else {
-        cmSystemTools::Error("Bad parameter for VS_DPI_AWARE: " + *dpiAware);
+        cmSystemTools::Error(
+          cmStrCat("Bad parameter for VS_DPI_AWARE: ", *dpiAware));
       }
     }
   }
@@ -4210,7 +4235,7 @@
   }
 
   {
-    std::string manifest_xml = rootDir + "/AndroidManifest.xml";
+    std::string manifest_xml = cmStrCat(rootDir, "/AndroidManifest.xml");
     ConvertToWindowsSlash(manifest_xml);
     e2.Element("AndroidManifestLocation", manifest_xml);
   }
@@ -4218,7 +4243,7 @@
   if (cmValue antAdditionalOptions =
         this->GeneratorTarget->GetProperty("ANDROID_ANT_ADDITIONAL_OPTIONS")) {
     e2.Element("AdditionalOptions",
-               *antAdditionalOptions + " %(AdditionalOptions)");
+               cmStrCat(*antAdditionalOptions, " %(AdditionalOptions)"));
   }
 }
 
@@ -4249,8 +4274,8 @@
 
   const std::string& linkLanguage = linkClosure->LinkerLanguage;
   if (linkLanguage.empty()) {
-    cmSystemTools::Error(
-      "CMake can not determine linker language for target: " + this->Name);
+    cmSystemTools::Error(cmStrCat(
+      "CMake can not determine linker language for target: ", this->Name));
     return false;
   }
 
@@ -4265,19 +4290,19 @@
   }
   std::string flags;
   std::string linkFlagVarBase = cmStrCat("CMAKE_", linkType, "_LINKER_FLAGS");
-  flags += " ";
+  flags += ' ';
   flags += this->Makefile->GetRequiredDefinition(linkFlagVarBase);
-  std::string linkFlagVar = linkFlagVarBase + "_" + CONFIG;
-  flags += " ";
+  std::string linkFlagVar = cmStrCat(linkFlagVarBase, '_', CONFIG);
+  flags += ' ';
   flags += this->Makefile->GetRequiredDefinition(linkFlagVar);
   cmValue targetLinkFlags = this->GeneratorTarget->GetProperty("LINK_FLAGS");
   if (targetLinkFlags) {
-    flags += " ";
+    flags += ' ';
     flags += *targetLinkFlags;
   }
   std::string flagsProp = cmStrCat("LINK_FLAGS_", CONFIG);
   if (cmValue flagsConfig = this->GeneratorTarget->GetProperty(flagsProp)) {
-    flags += " ";
+    flags += ' ';
     flags += *flagsConfig;
   }
 
@@ -4290,8 +4315,8 @@
     this->GeneratorTarget->GetLinkInformation(config);
   if (!pcli) {
     cmSystemTools::Error(
-      "CMake can not compute cmComputeLinkInformation for target: " +
-      this->Name);
+      cmStrCat("CMake can not compute cmComputeLinkInformation for target: ",
+               this->Name));
     return false;
   }
   cmComputeLinkInformation& cli = *pcli;
@@ -4316,7 +4341,7 @@
     // first just full path
     linkDirs.push_back(d);
     // next path with configuration type Debug, Release, etc
-    linkDirs.push_back(d + "/$(Configuration)");
+    linkDirs.emplace_back(cmStrCat(d, "/$(Configuration)"));
   }
   linkDirs.push_back("%(AdditionalLibraryDirectories)");
   linkOptions.AddFlag("AdditionalLibraryDirectories", linkDirs);
@@ -4358,7 +4383,7 @@
     }
 
     if (cmValue stackVal = this->Makefile->GetDefinition(
-          "CMAKE_" + linkLanguage + "_STACK_SIZE")) {
+          cmStrCat("CMAKE_", linkLanguage, "_STACK_SIZE"))) {
       linkOptions.AddFlag("StackReserveSize", *stackVal);
     }
 
@@ -4390,7 +4415,7 @@
     }
 
     if (this->GlobalGenerator->TargetsWindowsPhone() &&
-        this->GlobalGenerator->GetSystemVersion() == "8.0") {
+        this->GlobalGenerator->GetSystemVersion() == "8.0"_s) {
       // WindowsPhone 8.0 does not have ole32.
       linkOptions.AppendFlag("IgnoreSpecificDefaultLibraries", "ole32.lib");
     }
@@ -4455,8 +4480,8 @@
     this->GeneratorTarget->GetLinkInformation(config);
   if (!pcli) {
     cmSystemTools::Error(
-      "CMake can not compute cmComputeLinkInformation for target: " +
-      this->Name);
+      cmStrCat("CMake can not compute cmComputeLinkInformation for target: ",
+               this->Name));
     return false;
   }
 
@@ -4562,7 +4587,8 @@
                                         : path);
       }
     } else if (!l.Target ||
-               l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
+               (l.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY &&
+                l.Target->GetType() != cmStateEnums::OBJECT_LIBRARY)) {
       libVec.push_back(l.Value.Value);
     }
   }
@@ -4772,7 +4798,8 @@
     ConvertToWindowsSlash(path);
     Elem e2(e1, "ProjectReference");
     e2.Attribute("Include", path);
-    e2.Element("Project", "{" + this->GlobalGenerator->GetGUID(name) + "}");
+    e2.Element("Project",
+               cmStrCat('{', this->GlobalGenerator->GetGUID(name), '}'));
     e2.Element("Name", name);
     this->WriteDotNetReferenceCustomTags(e2, name);
     if (dt->IsCSharpOnly() || cmHasLiteralSuffix(path, "csproj")) {
@@ -4811,17 +4838,18 @@
 void cmVisualStudio10TargetGenerator::WriteSinglePlatformExtension(
   Elem& e1, std::string const& extension, std::string const& version)
 {
-  const std::string s = "$([Microsoft.Build.Utilities.ToolLocationHelper]"
-                        "::GetPlatformExtensionSDKLocation(`" +
-    extension + ", Version=" + version +
-    "`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), null, "
-    "$(ExtensionSDKDirectoryRoot), null))"
-    "\\DesignTime\\CommonConfiguration\\Neutral\\" +
-    extension + ".props";
+  const std::string s =
+    cmStrCat("$([Microsoft.Build.Utilities.ToolLocationHelper]"
+             "::GetPlatformExtensionSDKLocation(`",
+             extension, ", Version=", version,
+             "`, $(TargetPlatformIdentifier), $(TargetPlatformVersion), null, "
+             "$(ExtensionSDKDirectoryRoot), null))"
+             "\\DesignTime\\CommonConfiguration\\Neutral\\",
+             extension, ".props");
 
   Elem e2(e1, "Import");
   e2.Attribute("Project", s);
-  e2.Attribute("Condition", "exists('" + s + "')");
+  e2.Attribute("Condition", cmStrCat("exists('", s, "')"));
 }
 
 void cmVisualStudio10TargetGenerator::WriteSDKReferences(Elem& e0)
@@ -4872,7 +4900,74 @@
   Elem& e1, std::string const& extension, std::string const& version)
 {
   Elem(e1, "SDKReference")
-    .Attribute("Include", extension + ", Version=" + version);
+    .Attribute("Include", cmStrCat(extension, ", Version=", version));
+}
+
+namespace {
+std::string ComputeCertificateThumbprint(const std::string& source)
+{
+  std::string thumbprint;
+
+  CRYPT_INTEGER_BLOB cryptBlob;
+  HCERTSTORE certStore = nullptr;
+  PCCERT_CONTEXT certContext = nullptr;
+
+  HANDLE certFile = CreateFileW(
+    cmsys::Encoding::ToWide(source.c_str()).c_str(), GENERIC_READ,
+    FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+  if (certFile != INVALID_HANDLE_VALUE && certFile != nullptr) {
+    DWORD fileSize = GetFileSize(certFile, nullptr);
+    if (fileSize != INVALID_FILE_SIZE) {
+      auto certData = cm::make_unique<BYTE[]>(fileSize);
+      if (certData != nullptr) {
+        DWORD dwRead = 0;
+        if (ReadFile(certFile, certData.get(), fileSize, &dwRead, nullptr)) {
+          cryptBlob.cbData = fileSize;
+          cryptBlob.pbData = certData.get();
+
+          // Verify that this is a valid cert
+          if (PFXIsPFXBlob(&cryptBlob)) {
+            // Open the certificate as a store
+            certStore =
+              PFXImportCertStore(&cryptBlob, nullptr, CRYPT_EXPORTABLE);
+            if (certStore != nullptr) {
+              // There should only be 1 cert.
+              certContext =
+                CertEnumCertificatesInStore(certStore, certContext);
+              if (certContext != nullptr) {
+                // The hash is 20 bytes
+                BYTE hashData[20];
+                DWORD hashLength = 20;
+
+                // Buffer to print the hash. Each byte takes 2 chars +
+                // terminating character
+                char hashPrint[41];
+                char* pHashPrint = hashPrint;
+                // Get the hash property from the certificate
+                if (CertGetCertificateContextProperty(
+                      certContext, CERT_HASH_PROP_ID, hashData, &hashLength)) {
+                  for (DWORD i = 0; i < hashLength; i++) {
+                    // Convert each byte to hexadecimal
+                    snprintf(pHashPrint, 3, "%02X", hashData[i]);
+                    pHashPrint += 2;
+                  }
+                  *pHashPrint = '\0';
+                  thumbprint = hashPrint;
+                }
+                CertFreeCertificateContext(certContext);
+              }
+              CertCloseStore(certStore, 0);
+            }
+          }
+        }
+      }
+    }
+    CloseHandle(certFile);
+  }
+
+  return thumbprint;
+}
 }
 
 void cmVisualStudio10TargetGenerator::WriteWinRTPackageCertificateKeyFile(
@@ -4893,15 +4988,15 @@
 
     if (this->IsMissingFiles &&
         !(this->GlobalGenerator->TargetsWindowsPhone() &&
-          this->GlobalGenerator->GetSystemVersion() == "8.0")) {
+          this->GlobalGenerator->GetSystemVersion() == "8.0"_s)) {
       // Move the manifest to a project directory to avoid clashes
       std::string artifactDir =
         this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
       ConvertToWindowsSlash(artifactDir);
       Elem e1(e0, "PropertyGroup");
-      e1.Element("AppxPackageArtifactsDir", artifactDir + "\\");
+      e1.Element("AppxPackageArtifactsDir", cmStrCat(artifactDir, '\\'));
       std::string resourcePriFile =
-        this->DefaultArtifactDir + "/resources.pri";
+        cmStrCat(this->DefaultArtifactDir, "/resources.pri");
       ConvertToWindowsSlash(resourcePriFile);
       e1.Element("ProjectPriFullPath", resourcePriFile);
 
@@ -4909,24 +5004,26 @@
       // aren't targeting WP8.0, add a default certificate
       if (pfxFile.empty()) {
         std::string templateFolder =
-          cmSystemTools::GetCMakeRoot() + "/Templates/Windows";
-        pfxFile = this->DefaultArtifactDir + "/Windows_TemporaryKey.pfx";
-        cmSystemTools::CopyAFile(templateFolder + "/Windows_TemporaryKey.pfx",
-                                 pfxFile, false);
+          cmStrCat(cmSystemTools::GetCMakeRoot(), "/Templates/Windows");
+        pfxFile =
+          cmStrCat(this->DefaultArtifactDir, "/Windows_TemporaryKey.pfx");
+        cmSystemTools::CopyAFile(
+          cmStrCat(templateFolder, "/Windows_TemporaryKey.pfx"), pfxFile,
+          false);
         ConvertToWindowsSlash(pfxFile);
         this->AddedFiles.push_back(pfxFile);
         this->AddedDefaultCertificate = true;
       }
 
       e1.Element("PackageCertificateKeyFile", pfxFile);
-      std::string thumb = cmSystemTools::ComputeCertificateThumbprint(pfxFile);
+      std::string thumb = ComputeCertificateThumbprint(pfxFile);
       if (!thumb.empty()) {
         e1.Element("PackageCertificateThumbprint", thumb);
       }
     } else if (!pfxFile.empty()) {
       Elem e1(e0, "PropertyGroup");
       e1.Element("PackageCertificateKeyFile", pfxFile);
-      std::string thumb = cmSystemTools::ComputeCertificateThumbprint(pfxFile);
+      std::string thumb = ComputeCertificateThumbprint(pfxFile);
       if (!thumb.empty()) {
         e1.Element("PackageCertificateThumbprint", thumb);
       }
@@ -4953,7 +5050,8 @@
       // where the user supplied the file name and Visual Studio
       // appended the suffix.
       std::string resx = acs.Source->ResolveFullPath();
-      std::string hFileName = resx.substr(0, resx.find_last_of('.')) + ".h";
+      std::string hFileName =
+        cmStrCat(resx.substr(0, resx.find_last_of('.')), ".h");
       this->ExpectedResxHeaders.insert(hFileName);
     } break;
     case cmGeneratorTarget::SourceKindXaml: {
@@ -4963,8 +5061,8 @@
       // where the user supplied the file name and Visual Studio
       // appended the suffix.
       std::string xaml = acs.Source->ResolveFullPath();
-      std::string hFileName = xaml + ".h";
-      std::string cppFileName = xaml + ".cpp";
+      std::string hFileName = cmStrCat(xaml, ".h");
+      std::string cppFileName = cmStrCat(xaml, ".cpp");
       this->ExpectedXamlHeaders.insert(hFileName);
       this->ExpectedXamlSources.insert(cppFileName);
     } break;
@@ -5003,7 +5101,7 @@
     e1.Element("ApplicationType",
                (isWindowsPhone ? "Windows Phone" : "Windows Store"));
     e1.Element("DefaultLanguage", "en-US");
-    if (rev == "10.0") {
+    if (rev == "10.0"_s) {
       e1.Element("ApplicationTypeRevision", rev);
       // Visual Studio 14.0 is necessary for building 10.0 apps
       e1.Element("MinimumVisualStudioVersion", "14.0");
@@ -5011,7 +5109,7 @@
       if (this->GeneratorTarget->GetType() < cmStateEnums::UTILITY) {
         isAppContainer = true;
       }
-    } else if (rev == "8.1") {
+    } else if (rev == "8.1"_s) {
       e1.Element("ApplicationTypeRevision", rev);
       // Visual Studio 12.0 is necessary for building 8.1 apps
       e1.Element("MinimumVisualStudioVersion", "12.0");
@@ -5019,7 +5117,7 @@
       if (this->GeneratorTarget->GetType() < cmStateEnums::UTILITY) {
         isAppContainer = true;
       }
-    } else if (rev == "8.0") {
+    } else if (rev == "8.0"_s) {
       e1.Element("ApplicationTypeRevision", rev);
       // Visual Studio 11.0 is necessary for building 8.0 apps
       e1.Element("MinimumVisualStudioVersion", "11.0");
@@ -5032,7 +5130,7 @@
                    cmStateEnums::EXECUTABLE) {
         e1.Element("XapOutputs", "true");
         e1.Element("XapFilename",
-                   this->Name + "_$(Configuration)_$(Platform).xap");
+                   cmStrCat(this->Name, "_$(Configuration)_$(Platform).xap"));
       }
     }
   } else if (isAndroid) {
@@ -5043,9 +5141,9 @@
   if (isAppContainer) {
     e1.Element("AppContainerApplication", "true");
   } else if (!isAndroid) {
-    if (this->Platform == "ARM64") {
+    if (this->Platform == "ARM64"_s) {
       e1.Element("WindowsSDKDesktopARM64Support", "true");
-    } else if (this->Platform == "ARM") {
+    } else if (this->Platform == "ARM"_s) {
       e1.Element("WindowsSDKDesktopARMSupport", "true");
     }
   }
@@ -5058,7 +5156,7 @@
     "VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION");
   if (targetPlatformMinVersion) {
     e1.Element("WindowsTargetPlatformMinVersion", *targetPlatformMinVersion);
-  } else if (isWindowsStore && rev == "10.0") {
+  } else if (isWindowsStore && rev == "10.0"_s) {
     // If the min version is not set, then use the TargetPlatformVersion
     if (!targetPlatformVersion.empty()) {
       e1.Element("WindowsTargetPlatformMinVersion", targetPlatformVersion);
@@ -5081,7 +5179,7 @@
         cmGeneratorTarget::SourceKindAppManifest);
     std::string const& v = this->GlobalGenerator->GetSystemVersion();
     if (this->GlobalGenerator->TargetsWindowsPhone()) {
-      if (v == "8.0") {
+      if (v == "8.0"_s) {
         // Look through the sources for WMAppManifest.xml
         bool foundManifest = false;
         for (cmGeneratorTarget::AllConfigSource const& source :
@@ -5097,16 +5195,16 @@
         if (!foundManifest) {
           this->IsMissingFiles = true;
         }
-      } else if (v == "8.1") {
+      } else if (v == "8.1"_s) {
         if (manifestSources.empty()) {
           this->IsMissingFiles = true;
         }
       }
     } else if (this->GlobalGenerator->TargetsWindowsStore()) {
       if (manifestSources.empty()) {
-        if (v == "8.0") {
+        if (v == "8.0"_s) {
           this->IsMissingFiles = true;
-        } else if (v == "8.1" || cmHasLiteralPrefix(v, "10.0")) {
+        } else if (v == "8.1"_s || cmHasLiteralPrefix(v, "10.0")) {
           this->IsMissingFiles = true;
         }
       }
@@ -5118,15 +5216,15 @@
 {
   std::string const& v = this->GlobalGenerator->GetSystemVersion();
   if (this->GlobalGenerator->TargetsWindowsPhone()) {
-    if (v == "8.0") {
+    if (v == "8.0"_s) {
       this->WriteMissingFilesWP80(e1);
-    } else if (v == "8.1") {
+    } else if (v == "8.1"_s) {
       this->WriteMissingFilesWP81(e1);
     }
   } else if (this->GlobalGenerator->TargetsWindowsStore()) {
-    if (v == "8.0") {
+    if (v == "8.0"_s) {
       this->WriteMissingFilesWS80(e1);
-    } else if (v == "8.1") {
+    } else if (v == "8.1"_s) {
       this->WriteMissingFilesWS81(e1);
     } else if (cmHasLiteralPrefix(v, "10.0")) {
       this->WriteMissingFilesWS10_0(e1);
@@ -5137,13 +5235,13 @@
 void cmVisualStudio10TargetGenerator::WriteMissingFilesWP80(Elem& e1)
 {
   std::string templateFolder =
-    cmSystemTools::GetCMakeRoot() + "/Templates/Windows";
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Templates/Windows");
 
   // For WP80, the manifest needs to be in the same folder as the project
   // this can cause an overwrite problem if projects aren't organized in
   // folders
-  std::string manifestFile =
-    this->LocalGenerator->GetCurrentBinaryDirectory() + "/WMAppManifest.xml";
+  std::string manifestFile = cmStrCat(
+    this->LocalGenerator->GetCurrentBinaryDirectory(), "/WMAppManifest.xml");
   std::string artifactDir =
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   ConvertToWindowsSlash(artifactDir);
@@ -5200,22 +5298,22 @@
   }
   this->AddedFiles.push_back(sourceFile);
 
-  std::string smallLogo = this->DefaultArtifactDir + "/SmallLogo.png";
-  cmSystemTools::CopyAFile(templateFolder + "/SmallLogo.png", smallLogo,
-                           false);
+  std::string smallLogo = cmStrCat(this->DefaultArtifactDir, "/SmallLogo.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/SmallLogo.png"),
+                           smallLogo, false);
   ConvertToWindowsSlash(smallLogo);
   Elem(e1, "Image").Attribute("Include", smallLogo);
   this->AddedFiles.push_back(smallLogo);
 
-  std::string logo = this->DefaultArtifactDir + "/Logo.png";
-  cmSystemTools::CopyAFile(templateFolder + "/Logo.png", logo, false);
+  std::string logo = cmStrCat(this->DefaultArtifactDir, "/Logo.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/Logo.png"), logo, false);
   ConvertToWindowsSlash(logo);
   Elem(e1, "Image").Attribute("Include", logo);
   this->AddedFiles.push_back(logo);
 
   std::string applicationIcon =
-    this->DefaultArtifactDir + "/ApplicationIcon.png";
-  cmSystemTools::CopyAFile(templateFolder + "/ApplicationIcon.png",
+    cmStrCat(this->DefaultArtifactDir, "/ApplicationIcon.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/ApplicationIcon.png"),
                            applicationIcon, false);
   ConvertToWindowsSlash(applicationIcon);
   Elem(e1, "Image").Attribute("Include", applicationIcon);
@@ -5225,7 +5323,7 @@
 void cmVisualStudio10TargetGenerator::WriteMissingFilesWP81(Elem& e1)
 {
   std::string manifestFile =
-    this->DefaultArtifactDir + "/package.appxManifest";
+    cmStrCat(this->DefaultArtifactDir, "/package.appxManifest");
   std::string artifactDir =
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   ConvertToWindowsSlash(artifactDir);
@@ -5287,7 +5385,7 @@
 void cmVisualStudio10TargetGenerator::WriteMissingFilesWS80(Elem& e1)
 {
   std::string manifestFile =
-    this->DefaultArtifactDir + "/package.appxManifest";
+    cmStrCat(this->DefaultArtifactDir, "/package.appxManifest");
   std::string artifactDir =
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   ConvertToWindowsSlash(artifactDir);
@@ -5341,7 +5439,7 @@
 void cmVisualStudio10TargetGenerator::WriteMissingFilesWS81(Elem& e1)
 {
   std::string manifestFile =
-    this->DefaultArtifactDir + "/package.appxManifest";
+    cmStrCat(this->DefaultArtifactDir, "/package.appxManifest");
   std::string artifactDir =
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   ConvertToWindowsSlash(artifactDir);
@@ -5400,7 +5498,7 @@
 void cmVisualStudio10TargetGenerator::WriteMissingFilesWS10_0(Elem& e1)
 {
   std::string manifestFile =
-    this->DefaultArtifactDir + "/package.appxManifest";
+    cmStrCat(this->DefaultArtifactDir, "/package.appxManifest");
   std::string artifactDir =
     this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
   ConvertToWindowsSlash(artifactDir);
@@ -5461,7 +5559,7 @@
   Elem& e1, const std::string& manifestFile)
 {
   std::string templateFolder =
-    cmSystemTools::GetCMakeRoot() + "/Templates/Windows";
+    cmStrCat(cmSystemTools::GetCMakeRoot(), "/Templates/Windows");
 
   std::string sourceFile = this->ConvertPath(manifestFile, false);
   ConvertToWindowsSlash(sourceFile);
@@ -5472,36 +5570,38 @@
   }
   this->AddedFiles.push_back(sourceFile);
 
-  std::string smallLogo = this->DefaultArtifactDir + "/SmallLogo.png";
-  cmSystemTools::CopyAFile(templateFolder + "/SmallLogo.png", smallLogo,
-                           false);
+  std::string smallLogo = cmStrCat(this->DefaultArtifactDir, "/SmallLogo.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/SmallLogo.png"),
+                           smallLogo, false);
   ConvertToWindowsSlash(smallLogo);
   Elem(e1, "Image").Attribute("Include", smallLogo);
   this->AddedFiles.push_back(smallLogo);
 
-  std::string smallLogo44 = this->DefaultArtifactDir + "/SmallLogo44x44.png";
-  cmSystemTools::CopyAFile(templateFolder + "/SmallLogo44x44.png", smallLogo44,
-                           false);
+  std::string smallLogo44 =
+    cmStrCat(this->DefaultArtifactDir, "/SmallLogo44x44.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/SmallLogo44x44.png"),
+                           smallLogo44, false);
   ConvertToWindowsSlash(smallLogo44);
   Elem(e1, "Image").Attribute("Include", smallLogo44);
   this->AddedFiles.push_back(smallLogo44);
 
-  std::string logo = this->DefaultArtifactDir + "/Logo.png";
-  cmSystemTools::CopyAFile(templateFolder + "/Logo.png", logo, false);
+  std::string logo = cmStrCat(this->DefaultArtifactDir, "/Logo.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/Logo.png"), logo, false);
   ConvertToWindowsSlash(logo);
   Elem(e1, "Image").Attribute("Include", logo);
   this->AddedFiles.push_back(logo);
 
-  std::string storeLogo = this->DefaultArtifactDir + "/StoreLogo.png";
-  cmSystemTools::CopyAFile(templateFolder + "/StoreLogo.png", storeLogo,
-                           false);
+  std::string storeLogo = cmStrCat(this->DefaultArtifactDir, "/StoreLogo.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/StoreLogo.png"),
+                           storeLogo, false);
   ConvertToWindowsSlash(storeLogo);
   Elem(e1, "Image").Attribute("Include", storeLogo);
   this->AddedFiles.push_back(storeLogo);
 
-  std::string splashScreen = this->DefaultArtifactDir + "/SplashScreen.png";
-  cmSystemTools::CopyAFile(templateFolder + "/SplashScreen.png", splashScreen,
-                           false);
+  std::string splashScreen =
+    cmStrCat(this->DefaultArtifactDir, "/SplashScreen.png");
+  cmSystemTools::CopyAFile(cmStrCat(templateFolder, "/SplashScreen.png"),
+                           splashScreen, false);
   ConvertToWindowsSlash(splashScreen);
   Elem(e1, "Image").Attribute("Include", splashScreen);
   this->AddedFiles.push_back(splashScreen);
@@ -5509,7 +5609,7 @@
   if (this->AddedDefaultCertificate) {
     // This file has already been added to the build so don't copy it
     std::string keyFile =
-      this->DefaultArtifactDir + "/Windows_TemporaryKey.pfx";
+      cmStrCat(this->DefaultArtifactDir, "/Windows_TemporaryKey.pfx");
     ConvertToWindowsSlash(keyFile);
     Elem(e1, "None").Attribute("Include", keyFile);
   }
@@ -5584,8 +5684,9 @@
   cmSourceGroup* sourceGroup =
     this->Makefile->FindSourceGroup(fullFileName, sourceGroups);
   if (sourceGroup && !sourceGroup->GetFullName().empty()) {
-    sourceGroupedFile = sourceGroup->GetFullName() + "/" +
-      cmsys::SystemTools::GetFilenameName(fullFileName);
+    sourceGroupedFile =
+      cmStrCat(sourceGroup->GetFullName(), '/',
+               cmsys::SystemTools::GetFilenameName(fullFileName));
     cmsys::SystemTools::ConvertToUnixSlashes(sourceGroupedFile);
   }
 
@@ -5633,8 +5734,9 @@
     // Store a cache entry that later determines, if a package restore is
     // required.
     this->GeneratorTarget->Makefile->AddCacheDefinition(
-      this->GeneratorTarget->GetName() + "_REQUIRES_VS_PACKAGE_RESTORE", "ON",
-      "Value Computed by CMake", cmStateEnums::STATIC);
+      cmStrCat(this->GeneratorTarget->GetName(),
+               "_REQUIRES_VS_PACKAGE_RESTORE"),
+      "ON", "Value Computed by CMake", cmStateEnums::STATIC);
   } else {
     // If there are any dependencies that require package restore, inherit the
     // cache variable.
@@ -5647,7 +5749,8 @@
     for (cmGeneratorTarget const* dt : depends) {
       if (dt->HasPackageReferences()) {
         this->GeneratorTarget->Makefile->AddCacheDefinition(
-          this->GeneratorTarget->GetName() + "_REQUIRES_VS_PACKAGE_RESTORE",
+          cmStrCat(this->GeneratorTarget->GetName(),
+                   "_REQUIRES_VS_PACKAGE_RESTORE"),
           "ON", "Value Computed by CMake", cmStateEnums::STATIC);
       }
     }
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index 6e98874..6188134 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -7,6 +7,7 @@
 #include <vector>
 
 #include <cm/iterator>
+#include <cmext/string_view>
 
 #include "cmAlgorithms.h"
 #include "cmLocalVisualStudioGenerator.h"
@@ -75,7 +76,6 @@
   // the flag to disable exception handling.  When the user does
   // remove the flag we need to override the IDE default of on.
   switch (this->Version) {
-    case cmGlobalVisualStudioGenerator::VSVersion::VS11:
     case cmGlobalVisualStudioGenerator::VSVersion::VS12:
     case cmGlobalVisualStudioGenerator::VSVersion::VS14:
     case cmGlobalVisualStudioGenerator::VSVersion::VS15:
@@ -118,7 +118,7 @@
   auto i = this->FlagMap.find("DebugType");
   if (i != this->FlagMap.end()) {
     if (i->second.size() == 1) {
-      return i->second[0] != "none";
+      return i->second[0] != "none"_s;
     }
   }
   return false;
@@ -138,13 +138,13 @@
 {
   // Look for a _UNICODE definition.
   return std::any_of(this->Defines.begin(), this->Defines.end(),
-                     [](std::string const& di) { return di == "_UNICODE"; });
+                     [](std::string const& di) { return di == "_UNICODE"_s; });
 }
 bool cmVisualStudioGeneratorOptions::UsingSBCS() const
 {
   // Look for a _SBCS definition.
   return std::any_of(this->Defines.begin(), this->Defines.end(),
-                     [](std::string const& di) { return di == "_SBCS"; });
+                     [](std::string const& di) { return di == "_SBCS"_s; });
 }
 
 void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration()
@@ -172,7 +172,7 @@
     return;
   }
 
-  if (subOptions.size() == 1 && subOptions[0] == "NO") {
+  if (subOptions.size() == 1 && subOptions[0] == "NO"_s) {
     AddFlag(ENABLE_UAC, "false");
     return;
   }
@@ -199,7 +199,7 @@
         1, std::max(std::string::size_type(0), keyValue[1].length() - 2));
     }
 
-    if (keyValue[0] == "level") {
+    if (keyValue[0] == "level"_s) {
       if (uacExecuteLevelMap.find(keyValue[1]) == uacExecuteLevelMap.end()) {
         // unknown level value
         continue;
@@ -209,8 +209,8 @@
       continue;
     }
 
-    if (keyValue[0] == "uiAccess") {
-      if (keyValue[1] != "true" && keyValue[1] != "false") {
+    if (keyValue[0] == "uiAccess"_s) {
+      if (keyValue[1] != "true"_s && keyValue[1] != "false"_s) {
         // unknown uiAccess value
         continue;
       }
@@ -261,11 +261,11 @@
     auto i = this->FlagMap.find("CudaRuntime");
     if (i != this->FlagMap.end() && i->second.size() == 1) {
       std::string& cudaRuntime = i->second[0];
-      if (cudaRuntime == "static") {
+      if (cudaRuntime == "static"_s) {
         cudaRuntime = "Static";
-      } else if (cudaRuntime == "shared") {
+      } else if (cudaRuntime == "shared"_s) {
         cudaRuntime = "Shared";
-      } else if (cudaRuntime == "none") {
+      } else if (cudaRuntime == "none"_s) {
         cudaRuntime = "None";
       }
     }
@@ -280,7 +280,7 @@
     return;
   }
   std::string& value = i->second[0];
-  value = "%(" + key + ") " + value;
+  value = cmStrCat("%(", key, ") ", value);
 }
 
 void cmVisualStudioGeneratorOptions::Reparse(std::string const& key)
@@ -299,19 +299,19 @@
 {
   // Look for Intel Fortran flags that do not map well in the flag table.
   if (this->CurrentTool == FortranCompiler) {
-    if (flag == "/dbglibs" || flag == "-dbglibs") {
+    if (flag == "/dbglibs"_s || flag == "-dbglibs"_s) {
       this->FortranRuntimeDebug = true;
       return;
     }
-    if (flag == "/threads" || flag == "-threads") {
+    if (flag == "/threads"_s || flag == "-threads"_s) {
       this->FortranRuntimeMT = true;
       return;
     }
-    if (flag == "/libs:dll" || flag == "-libs:dll") {
+    if (flag == "/libs:dll"_s || flag == "-libs:dll"_s) {
       this->FortranRuntimeDLL = true;
       return;
     }
-    if (flag == "/libs:static" || flag == "-libs:static") {
+    if (flag == "/libs:static"_s || flag == "-libs:static"_s) {
       this->FortranRuntimeDLL = false;
       return;
     }
@@ -355,13 +355,13 @@
     return;
   }
   std::string tag = "PreprocessorDefinitions";
-  if (lang == "CUDA") {
+  if (lang == "CUDA"_s) {
     tag = "Defines";
   }
 
   std::ostringstream oss;
   if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
-    oss << "%(" << tag << ")";
+    oss << "%(" << tag << ')';
   }
   auto de = cmRemoveDuplicates(this->Defines);
   for (std::string const& di : cmMakeRange(this->Defines.cbegin(), de)) {
@@ -375,7 +375,7 @@
     // Escape this flag for the MSBuild.
     if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
       cmVS10EscapeForMSBuild(define);
-      if (lang == "RC") {
+      if (lang == "RC"_s) {
         cmSystemTools::ReplaceString(define, "\"", "\\\"");
       }
     }
@@ -394,9 +394,9 @@
   }
 
   std::string tag = "AdditionalIncludeDirectories";
-  if (lang == "CUDA") {
+  if (lang == "CUDA"_s) {
     tag = "Include";
-  } else if (lang == "ASM_MASM" || lang == "ASM_NASM") {
+  } else if (lang == "ASM_MASM"_s || lang == "ASM_NASM"_s) {
     tag = "IncludePaths";
   }
 
@@ -410,8 +410,8 @@
       pos++;
     }
 
-    if (lang == "ASM_NASM") {
-      include += "\\";
+    if (lang == "ASM_NASM"_s) {
+      include += '\\';
     }
 
     // Escape this include for the MSBuild.
@@ -421,14 +421,14 @@
     oss << sep << include;
     sep = ";";
 
-    if (lang == "Fortran") {
+    if (lang == "Fortran"_s) {
       include += "/$(ConfigurationName)";
       oss << sep << include;
     }
   }
 
   if (this->Version != cmGlobalVisualStudioGenerator::VSVersion::VS9) {
-    oss << sep << "%(" << tag << ")";
+    oss << sep << "%(" << tag << ')';
   }
 
   this->OutputFlag(fout, indent, tag, oss.str());
diff --git a/Source/cmVisualStudioSlnData.cxx b/Source/cmVisualStudioSlnData.cxx
index 4b6754e..f685158 100644
--- a/Source/cmVisualStudioSlnData.cxx
+++ b/Source/cmVisualStudioSlnData.cxx
@@ -5,6 +5,7 @@
 #include <cstddef>
 #include <utility>
 
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 void cmSlnProjectEntry::AddProjectConfiguration(
@@ -72,7 +73,8 @@
   const std::string& projectName, const std::string& solutionConfiguration,
   const std::string& platformName)
 {
-  std::string solutionTarget = solutionConfiguration + "|" + platformName;
+  std::string solutionTarget =
+    cmStrCat(solutionConfiguration, '|', platformName);
   cm::optional<cmSlnProjectEntry> project = GetProjectByName(projectName);
   if (!project) {
     return platformName;
diff --git a/Source/cmVisualStudioSlnParser.cxx b/Source/cmVisualStudioSlnParser.cxx
index 71c758e..adfd4c5 100644
--- a/Source/cmVisualStudioSlnParser.cxx
+++ b/Source/cmVisualStudioSlnParser.cxx
@@ -8,6 +8,8 @@
 #include <utility>
 #include <vector>
 
+#include <cmext/string_view>
+
 #include "cmsys/FStream.hxx"
 
 #include "cmStringAlgorithms.h"
@@ -81,7 +83,7 @@
 std::string cmVisualStudioSlnParser::ParsedLine::GetArgVerbatim() const
 {
   if (this->Arg.second) {
-    return Quote + this->Arg.first + Quote;
+    return cmStrCat(Quote, this->Arg.first, Quote);
   }
   return this->Arg.first;
 }
@@ -101,7 +103,7 @@
   if (idxValue < this->Values.size()) {
     const StringData& data = this->Values[idxValue];
     if (data.second) {
-      return Quote + data.first + Quote;
+      return cmStrCat(Quote, data.first, Quote);
     }
     return data.first;
   }
@@ -169,17 +171,12 @@
     case FileStateTopLevel:
       return LineMultiValueTag;
     case FileStateProject:
-      return LineSingleValueTag;
-    case FileStateProjectDependencies:
-      return LineKeyValuePair;
     case FileStateGlobal:
       return LineSingleValueTag;
+    case FileStateProjectDependencies:
     case FileStateSolutionConfigurations:
-      return LineKeyValuePair;
     case FileStateProjectConfigurations:
-      return LineKeyValuePair;
     case FileStateSolutionFilters:
-      return LineKeyValuePair;
     case FileStateGlobalSection:
       return LineKeyValuePair;
     case FileStateIgnore:
@@ -206,7 +203,7 @@
       this->Stack.push(FileStateTopLevel);
       break;
     case FileStateTopLevel:
-      if (line.GetTag() == "Project") {
+      if (line.GetTag() == "Project"_s) {
         if (line.GetValueCount() != 3) {
           result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
           return false;
@@ -221,12 +218,12 @@
         } else {
           this->IgnoreUntilTag("EndProject");
         }
-      } else if (line.GetTag() == "Global") {
+      } else if (line.GetTag() == "Global"_s) {
 
         this->Stack.push(FileStateGlobal);
-      } else if (line.GetTag() == "VisualStudioVersion") {
+      } else if (line.GetTag() == "VisualStudioVersion"_s) {
         output.SetVisualStudioVersion(line.GetValue(0));
-      } else if (line.GetTag() == "MinimumVisualStudioVersion") {
+      } else if (line.GetTag() == "MinimumVisualStudioVersion"_s) {
         output.SetMinimumVisualStudioVersion(line.GetValue(0));
       } else {
         result.SetError(ResultErrorInputStructure, this->GetCurrentLine());
@@ -234,11 +231,11 @@
       }
       break;
     case FileStateProject:
-      if (line.GetTag() == "EndProject") {
+      if (line.GetTag() == "EndProject"_s) {
         this->Stack.pop();
-      } else if (line.GetTag() == "ProjectSection") {
-        if (line.GetArg() == "ProjectDependencies" &&
-            line.GetValue(0) == "postProject") {
+      } else if (line.GetTag() == "ProjectSection"_s) {
+        if (line.GetArg() == "ProjectDependencies"_s &&
+            line.GetValue(0) == "postProject"_s) {
           if (this->RequestedData.test(DataGroupProjectDependenciesBit)) {
             this->Stack.push(FileStateProjectDependencies);
           } else {
@@ -253,7 +250,7 @@
       }
       break;
     case FileStateProjectDependencies:
-      if (line.GetTag() == "EndProjectSection") {
+      if (line.GetTag() == "EndProjectSection"_s) {
         this->Stack.pop();
       } else if (line.IsKeyValuePair()) {
         // implement dependency storing here, once needed
@@ -264,25 +261,25 @@
       }
       break;
     case FileStateGlobal:
-      if (line.GetTag() == "EndGlobal") {
+      if (line.GetTag() == "EndGlobal"_s) {
         this->Stack.pop();
-      } else if (line.GetTag() == "GlobalSection") {
-        if (line.GetArg() == "SolutionConfigurationPlatforms" &&
-            line.GetValue(0) == "preSolution") {
+      } else if (line.GetTag() == "GlobalSection"_s) {
+        if (line.GetArg() == "SolutionConfigurationPlatforms"_s &&
+            line.GetValue(0) == "preSolution"_s) {
           if (this->RequestedData.test(DataGroupSolutionConfigurationsBit)) {
             this->Stack.push(FileStateSolutionConfigurations);
           } else {
             this->IgnoreUntilTag("EndGlobalSection");
           }
-        } else if (line.GetArg() == "ProjectConfigurationPlatforms" &&
-                   line.GetValue(0) == "postSolution") {
+        } else if (line.GetArg() == "ProjectConfigurationPlatforms"_s &&
+                   line.GetValue(0) == "postSolution"_s) {
           if (this->RequestedData.test(DataGroupProjectConfigurationsBit)) {
             this->Stack.push(FileStateProjectConfigurations);
           } else {
             this->IgnoreUntilTag("EndGlobalSection");
           }
-        } else if (line.GetArg() == "NestedProjects" &&
-                   line.GetValue(0) == "preSolution") {
+        } else if (line.GetArg() == "NestedProjects"_s &&
+                   line.GetValue(0) == "preSolution"_s) {
           if (this->RequestedData.test(DataGroupSolutionFiltersBit)) {
             this->Stack.push(FileStateSolutionFilters);
           } else {
@@ -300,7 +297,7 @@
       }
       break;
     case FileStateSolutionConfigurations:
-      if (line.GetTag() == "EndGlobalSection") {
+      if (line.GetTag() == "EndGlobalSection"_s) {
         this->Stack.pop();
       } else if (line.IsKeyValuePair()) {
         output.AddConfiguration(line.GetValue(0));
@@ -310,7 +307,7 @@
       }
       break;
     case FileStateProjectConfigurations:
-      if (line.GetTag() == "EndGlobalSection") {
+      if (line.GetTag() == "EndGlobalSection"_s) {
         this->Stack.pop();
       } else if (line.IsKeyValuePair()) {
         std::vector<std::string> tagElements =
@@ -331,7 +328,7 @@
           return false;
         }
 
-        if (activeBuild == "ActiveCfg") {
+        if (activeBuild == "ActiveCfg"_s) {
           projectEntry->AddProjectConfiguration(solutionConfiguration,
                                                 line.GetValue(0));
         }
@@ -341,7 +338,7 @@
       }
       break;
     case FileStateSolutionFilters:
-      if (line.GetTag() == "EndGlobalSection") {
+      if (line.GetTag() == "EndGlobalSection"_s) {
         this->Stack.pop();
       } else if (line.IsKeyValuePair()) {
         // implement filter storing here, once needed
@@ -352,7 +349,7 @@
       }
       break;
     case FileStateGlobalSection:
-      if (line.GetTag() == "EndGlobalSection") {
+      if (line.GetTag() == "EndGlobalSection"_s) {
         this->Stack.pop();
       } else if (line.IsKeyValuePair()) {
         // implement section storing here, once needed
@@ -544,7 +541,7 @@
     return false;
   }
   if (!this->LastResult.HadBOM) {
-    line = bom + line; // it wasn't a BOM, prepend it to first line
+    line = cmStrCat(bom, line); // it wasn't a BOM, prepend it to first line
   }
   return true;
 }
diff --git a/Source/cmVisualStudioWCEPlatformParser.cxx b/Source/cmVisualStudioWCEPlatformParser.cxx
index d8d0da9..8aa5384 100644
--- a/Source/cmVisualStudioWCEPlatformParser.cxx
+++ b/Source/cmVisualStudioWCEPlatformParser.cxx
@@ -7,14 +7,15 @@
 #include <utility>
 
 #include "cmGlobalVisualStudioGenerator.h"
+#include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
 int cmVisualStudioWCEPlatformParser::ParseVersion(const char* version)
 {
   const std::string registryBase =
     cmGlobalVisualStudioGenerator::GetRegistryBase(version);
-  const std::string vckey = registryBase + "\\Setup\\VC;ProductDir";
-  const std::string vskey = registryBase + "\\Setup\\VS;ProductDir";
+  const std::string vckey = cmStrCat(registryBase, "\\Setup\\VC;ProductDir");
+  const std::string vskey = cmStrCat(registryBase, "\\Setup\\VS;ProductDir");
 
   if (!cmSystemTools::ReadRegistryValue(vckey, this->VcInstallDir,
                                         cmSystemTools::KeyWOW64_32) ||
@@ -24,11 +25,10 @@
   }
   cmSystemTools::ConvertToUnixSlashes(this->VcInstallDir);
   cmSystemTools::ConvertToUnixSlashes(this->VsInstallDir);
-  this->VcInstallDir.append("/");
-  this->VsInstallDir.append("/");
+  this->VcInstallDir.append("//");
 
   const std::string configFilename =
-    this->VcInstallDir + "vcpackages/WCE.VCPlatform.config";
+    cmStrCat(this->VcInstallDir, "vcpackages/WCE.VCPlatform.config");
 
   return this->ParseFile(configFilename.c_str());
 }
@@ -39,7 +39,7 @@
     return OSMajorVersion;
   }
 
-  return OSMajorVersion + "." + OSMinorVersion;
+  return cmStrCat(OSMajorVersion, '.', OSMinorVersion);
 }
 
 const char* cmVisualStudioWCEPlatformParser::GetArchitectureFamily() const
@@ -61,14 +61,14 @@
 
   this->CharacterData.clear();
 
-  if (name == "PlatformData") {
+  if (name == "PlatformData"_s) {
     this->PlatformName.clear();
     this->OSMajorVersion.clear();
     this->OSMinorVersion.clear();
     this->Macros.clear();
   }
 
-  if (name == "Macro") {
+  if (name == "Macro"_s) {
     std::string macroName;
     std::string macroValue;
 
@@ -83,7 +83,7 @@
     if (!macroName.empty()) {
       this->Macros[macroName] = macroValue;
     }
-  } else if (name == "Directories") {
+  } else if (name == "Directories"_s) {
     for (const char** attr = attributes; *attr; attr += 2) {
       if (strcmp(attr[0], "Include") == 0) {
         this->Include = attr[1];
@@ -99,7 +99,7 @@
 void cmVisualStudioWCEPlatformParser::EndElement(const std::string& name)
 {
   if (!this->RequiredName) {
-    if (name == "PlatformName") {
+    if (name == "PlatformName"_s) {
       this->AvailablePlatforms.push_back(this->CharacterData);
     }
     return;
@@ -109,13 +109,13 @@
     return;
   }
 
-  if (name == "PlatformName") {
+  if (name == "PlatformName"_s) {
     this->PlatformName = this->CharacterData;
-  } else if (name == "OSMajorVersion") {
+  } else if (name == "OSMajorVersion"_s) {
     this->OSMajorVersion = this->CharacterData;
-  } else if (name == "OSMinorVersion") {
+  } else if (name == "OSMinorVersion"_s) {
     this->OSMinorVersion = this->CharacterData;
-  } else if (name == "Platform") {
+  } else if (name == "Platform"_s) {
     if (this->PlatformName == this->RequiredName) {
       this->FoundRequiredName = true;
     }
diff --git a/Source/cmWindowsRegistry.cxx b/Source/cmWindowsRegistry.cxx
index 6dba863..a4a1ad6 100644
--- a/Source/cmWindowsRegistry.cxx
+++ b/Source/cmWindowsRegistry.cxx
@@ -6,7 +6,6 @@
 
 #include <cctype>
 #include <cstddef>
-#include <functional>
 #include <type_traits>
 #include <unordered_map>
 #include <utility>
diff --git a/Source/cmWindowsRegistry.h b/Source/cmWindowsRegistry.h
index f4a0e7b..42179ed 100644
--- a/Source/cmWindowsRegistry.h
+++ b/Source/cmWindowsRegistry.h
@@ -5,6 +5,7 @@
 #include "cmConfigure.h" // IWYU pragma: keep
 
 #include <cstdint> // IWYU pragma: keep
+#include <initializer_list>
 #include <string>
 #include <vector>
 
diff --git a/Source/cmXCode21Object.cxx b/Source/cmXCode21Object.cxx
index 9b0dc58..7a40eaa 100644
--- a/Source/cmXCode21Object.cxx
+++ b/Source/cmXCode21Object.cxx
@@ -26,9 +26,7 @@
   if (this->Comment.empty()) {
     return;
   }
-  out << " /* ";
-  out << this->Comment;
-  out << " */";
+  out << " /* " << this->Comment << " */";
 }
 
 void cmXCode21Object::PrintList(
diff --git a/Source/cmXCodeObject.cxx b/Source/cmXCodeObject.cxx
index c817980..ddd9669 100644
--- a/Source/cmXCodeObject.cxx
+++ b/Source/cmXCodeObject.cxx
@@ -4,6 +4,8 @@
 
 #include <ostream>
 
+#include <cmext/string_view>
+
 #include <CoreFoundation/CoreFoundation.h>
 
 const char* cmXCodeObject::PBXTypeNames[] = {
@@ -91,13 +93,13 @@
   out << this->Id;
   this->PrintComment(out);
   out << " = {";
-  if (separator == "\n") {
+  if (separator == "\n"_s) {
     out << separator;
   }
   cmXCodeObject::Indent(3 * indentFactor, out);
   out << "isa = " << PBXTypeNames[this->IsA] << ";" << separator;
   for (const auto& keyVal : this->ObjectAttributes) {
-    if (keyVal.first == "isa") {
+    if (keyVal.first == "isa"_s) {
       continue;
     }
 
@@ -142,7 +144,7 @@
 
     case ATTRIBUTE_GROUP: {
       out << name << " = {";
-      if (separator == "\n") {
+      if (separator == "\n"_s) {
         out << separator;
       }
       for (const auto& keyVal : object->ObjectAttributes) {
@@ -156,7 +158,7 @@
     case OBJECT_REF: {
       cmXCodeObject::PrintString(out, name);
       out << " = " << object->Object->Id;
-      if (object->Object->HasComment() && name != "remoteGlobalIDString") {
+      if (object->Object->HasComment() && name != "remoteGlobalIDString"_s) {
         object->Object->PrintComment(out);
       }
       out << ";" << separator;
@@ -180,7 +182,7 @@
 {
   cmXCodeObject::Indent(1, out);
   out << "objects = {\n";
-  for (auto obj : objs) {
+  for (auto* obj : objs) {
     if (obj->TypeValue == OBJECT) {
       obj->Print(out);
     }
diff --git a/Source/cmXCodeObject.h b/Source/cmXCodeObject.h
index 389fb62..10a6861 100644
--- a/Source/cmXCodeObject.h
+++ b/Source/cmXCodeObject.h
@@ -129,7 +129,7 @@
   // search the attribute list for an object of the specified type
   cmXCodeObject* GetObject(cmXCodeObject::PBXType t) const
   {
-    for (auto o : this->List) {
+    for (auto* o : this->List) {
       if (o->IsA == t) {
         return o;
       }
diff --git a/Source/cmXCodeScheme.cxx b/Source/cmXCodeScheme.cxx
index 7f26fd8..80327e4 100644
--- a/Source/cmXCodeScheme.cxx
+++ b/Source/cmXCodeScheme.cxx
@@ -7,6 +7,7 @@
 #include <utility>
 
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include "cmsys/String.h"
 
@@ -121,7 +122,7 @@
   xout.Attribute("shouldUseLaunchSchemeArgsEnv", "YES");
 
   xout.StartElement("Testables");
-  for (auto test : this->Tests) {
+  for (auto const* test : this->Tests) {
     xout.StartElement("TestableReference");
     xout.BreakAttributes();
     xout.Attribute("skipped", "NO");
@@ -157,7 +158,7 @@
     cmValue launchMode =
       this->Target->GetTarget()->GetProperty("XCODE_SCHEME_LAUNCH_MODE");
     std::string value = "0"; // == 'AUTO'
-    if (launchMode && *launchMode == "WAIT") {
+    if (launchMode && *launchMode == "WAIT"_s) {
       value = "1";
     }
     xout.Attribute("launchStyle", value);
@@ -447,7 +448,7 @@
   std::string const noConfig; // FIXME: What config to use here?
   xout.Attribute("BuildableName", xcObj->GetTarget()->GetFullName(noConfig));
   xout.Attribute("BlueprintName", xcObj->GetTarget()->GetName());
-  xout.Attribute("ReferencedContainer", "container:" + container);
+  xout.Attribute("ReferencedContainer", cmStrCat("container:", container));
   xout.EndElement();
 }
 
diff --git a/Source/cmXcFramework.cxx b/Source/cmXcFramework.cxx
new file mode 100644
index 0000000..e377fc9
--- /dev/null
+++ b/Source/cmXcFramework.cxx
@@ -0,0 +1,209 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmXcFramework.h"
+
+#include <string>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include <cm3p/json/value.h>
+
+#include "cmJSONHelpers.h"
+#include "cmJSONState.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmPlistParser.h"
+#include "cmStringAlgorithms.h"
+#include "cmake.h"
+
+namespace {
+struct PlistMetadata
+{
+  std::string CFBundlePackageType;
+  std::string XCFrameworkFormatVersion;
+};
+
+auto const PlistMetadataHelper =
+  cmJSONHelperBuilder::Object<PlistMetadata>{}
+    .Bind("CFBundlePackageType"_s, &PlistMetadata::CFBundlePackageType,
+          cmJSONHelperBuilder::String())
+    .Bind("XCFrameworkFormatVersion"_s,
+          &PlistMetadata::XCFrameworkFormatVersion,
+          cmJSONHelperBuilder::String());
+
+bool PlistSupportedPlatformHelper(
+  cmXcFrameworkPlistSupportedPlatform& platform, const Json::Value* value,
+  cmJSONState* /*state*/)
+{
+  if (!value) {
+    return false;
+  }
+
+  if (!value->isString()) {
+    return false;
+  }
+
+  if (value->asString() == "macos"_s) {
+    platform = cmXcFrameworkPlistSupportedPlatform::macOS;
+    return true;
+  }
+  if (value->asString() == "ios"_s) {
+    platform = cmXcFrameworkPlistSupportedPlatform::iOS;
+    return true;
+  }
+  if (value->asString() == "tvos"_s) {
+    platform = cmXcFrameworkPlistSupportedPlatform::tvOS;
+    return true;
+  }
+  if (value->asString() == "watchos"_s) {
+    platform = cmXcFrameworkPlistSupportedPlatform::watchOS;
+    return true;
+  }
+  if (value->asString() == "xros"_s) {
+    platform = cmXcFrameworkPlistSupportedPlatform::visionOS;
+    return true;
+  }
+
+  return false;
+}
+
+bool PlistSupportedPlatformVariantHelper(
+  cmXcFrameworkPlistSupportedPlatformVariant& variant,
+  const Json::Value* value, cmJSONState* /*state*/)
+{
+  if (!value) {
+    return false;
+  }
+
+  if (!value->isString()) {
+    return false;
+  }
+
+  if (value->asString() == "maccatalyst"_s) {
+    variant = cmXcFrameworkPlistSupportedPlatformVariant::maccatalyst;
+    return true;
+  }
+  if (value->asString() == "simulator"_s) {
+    variant = cmXcFrameworkPlistSupportedPlatformVariant::simulator;
+    return true;
+  }
+
+  return false;
+}
+
+auto const PlistLibraryHelper =
+  cmJSONHelperBuilder::Object<cmXcFrameworkPlistLibrary>{}
+    .Bind("LibraryIdentifier"_s, &cmXcFrameworkPlistLibrary::LibraryIdentifier,
+          cmJSONHelperBuilder::String())
+    .Bind("LibraryPath"_s, &cmXcFrameworkPlistLibrary::LibraryPath,
+          cmJSONHelperBuilder::String())
+    .Bind("HeadersPath"_s, &cmXcFrameworkPlistLibrary::HeadersPath,
+          cmJSONHelperBuilder::String(), false)
+    .Bind("SupportedArchitectures"_s,
+          &cmXcFrameworkPlistLibrary::SupportedArchitectures,
+          cmJSONHelperBuilder::Vector<std::string>(
+            JsonErrors::EXPECTED_TYPE("array"), cmJSONHelperBuilder::String()))
+    .Bind("SupportedPlatform"_s, &cmXcFrameworkPlistLibrary::SupportedPlatform,
+          PlistSupportedPlatformHelper)
+    .Bind("SupportedPlatformVariant"_s,
+          &cmXcFrameworkPlistLibrary::SupportedPlatformVariant,
+          cmJSONHelperBuilder::Optional<
+            cmXcFrameworkPlistSupportedPlatformVariant>(
+            PlistSupportedPlatformVariantHelper),
+          false);
+
+auto const PlistHelper =
+  cmJSONHelperBuilder::Object<cmXcFrameworkPlist>{}.Bind(
+    "AvailableLibraries"_s, &cmXcFrameworkPlist::AvailableLibraries,
+    cmJSONHelperBuilder::Vector<cmXcFrameworkPlistLibrary>(
+      JsonErrors::EXPECTED_TYPE("array"), PlistLibraryHelper));
+}
+
+cm::optional<cmXcFrameworkPlist> cmParseXcFrameworkPlist(
+  const std::string& xcframeworkPath, const cmMakefile& mf,
+  const cmListFileBacktrace& bt)
+{
+  std::string plistPath = cmStrCat(xcframeworkPath, "/Info.plist");
+
+  auto value = cmParsePlist(plistPath);
+  if (!value) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Unable to parse plist file:\n  ", plistPath), bt);
+    return cm::nullopt;
+  }
+
+  cmJSONState state;
+
+  PlistMetadata metadata;
+  if (!PlistMetadataHelper(metadata, &*value, &state)) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Invalid xcframework .plist file:\n  ", plistPath), bt);
+    return cm::nullopt;
+  }
+  if (metadata.CFBundlePackageType != "XFWK"_s ||
+      metadata.XCFrameworkFormatVersion != "1.0"_s) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Expected:\n  ", plistPath,
+               "\nto have CFBundlePackageType \"XFWK\" and "
+               "XCFrameworkFormatVersion \"1.0\""),
+      bt);
+    return cm::nullopt;
+  }
+
+  cmXcFrameworkPlist plist;
+  if (!PlistHelper(plist, &*value, &state)) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Invalid xcframework .plist file:\n  ", plistPath), bt);
+    return cm::nullopt;
+  }
+  plist.Path = plistPath;
+  return cm::optional<cmXcFrameworkPlist>(plist);
+}
+
+const cmXcFrameworkPlistLibrary* cmXcFrameworkPlist::SelectSuitableLibrary(
+  const cmMakefile& mf, const cmListFileBacktrace& bt) const
+{
+  auto systemName = mf.GetSafeDefinition("CMAKE_SYSTEM_NAME");
+  cm::optional<cmXcFrameworkPlistSupportedPlatformVariant> systemVariant;
+  if (mf.PlatformIsAppleSimulator()) {
+    systemVariant = cmXcFrameworkPlistSupportedPlatformVariant::simulator;
+  }
+
+  for (auto const& lib : this->AvailableLibraries) {
+    std::string supportedSystemName;
+    switch (lib.SupportedPlatform) {
+      case cmXcFrameworkPlistSupportedPlatform::macOS:
+        supportedSystemName = "Darwin";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::iOS:
+        supportedSystemName = "iOS";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::tvOS:
+        supportedSystemName = "tvOS";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::watchOS:
+        supportedSystemName = "watchOS";
+        break;
+      case cmXcFrameworkPlistSupportedPlatform::visionOS:
+        supportedSystemName = "visionOS";
+        break;
+    }
+
+    if (systemName == supportedSystemName &&
+        systemVariant == lib.SupportedPlatformVariant) {
+      return &lib;
+    }
+  }
+
+  mf.GetCMakeInstance()->IssueMessage(
+    MessageType::FATAL_ERROR,
+    cmStrCat("Unable to find suitable library in:\n  ", this->Path,
+             "\nfor system name \"", systemName, '"'),
+    bt);
+  return nullptr;
+}
diff --git a/Source/cmXcFramework.h b/Source/cmXcFramework.h
new file mode 100644
index 0000000..7ca91da
--- /dev/null
+++ b/Source/cmXcFramework.h
@@ -0,0 +1,52 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <cm/optional>
+
+#include "cmListFileCache.h"
+
+class cmMakefile;
+
+enum class cmXcFrameworkPlistSupportedPlatform
+{
+  macOS,
+  iOS,
+  tvOS,
+  watchOS,
+  visionOS,
+};
+
+enum class cmXcFrameworkPlistSupportedPlatformVariant
+{
+  maccatalyst,
+  simulator,
+};
+
+struct cmXcFrameworkPlistLibrary
+{
+  std::string LibraryIdentifier;
+  std::string LibraryPath;
+  std::string HeadersPath;
+  std::vector<std::string> SupportedArchitectures;
+  cmXcFrameworkPlistSupportedPlatform SupportedPlatform;
+  cm::optional<cmXcFrameworkPlistSupportedPlatformVariant>
+    SupportedPlatformVariant;
+};
+
+struct cmXcFrameworkPlist
+{
+  std::string Path;
+  std::vector<cmXcFrameworkPlistLibrary> AvailableLibraries;
+
+  const cmXcFrameworkPlistLibrary* SelectSuitableLibrary(
+    const cmMakefile& mf,
+    const cmListFileBacktrace& bt = cmListFileBacktrace{}) const;
+};
+
+cm::optional<cmXcFrameworkPlist> cmParseXcFrameworkPlist(
+  const std::string& xcframeworkPath, const cmMakefile& mf,
+  const cmListFileBacktrace& bt = cmListFileBacktrace{});
diff --git a/Source/cm_codecvt.cxx b/Source/cm_codecvt.cxx
index 12877b8..60faced 100644
--- a/Source/cm_codecvt.cxx
+++ b/Source/cm_codecvt.cxx
@@ -13,19 +13,21 @@
 #  include "cm_utf8.h"
 #endif
 
-codecvt::codecvt(Encoding e)
+#include "cm_codecvt_Encoding.hxx"
+
+codecvt::codecvt(codecvt_Encoding e)
 #if defined(_WIN32)
   : m_codepage(0)
 #endif
 {
   switch (e) {
-    case codecvt::ConsoleOutput:
+    case codecvt_Encoding::ConsoleOutput:
 #if defined(_WIN32)
       m_noconv = false;
       m_codepage = GetConsoleOutputCP();
       break;
 #endif
-    case codecvt::ANSI:
+    case codecvt_Encoding::ANSI:
 #if defined(_WIN32)
       m_noconv = false;
       m_codepage = CP_ACP;
@@ -33,10 +35,10 @@
 #endif
     // We don't know which ANSI encoding to use for other platforms than
     // Windows so we don't do any conversion there
-    case codecvt::UTF8:
-    case codecvt::UTF8_WITH_BOM:
+    case codecvt_Encoding::UTF8:
+    case codecvt_Encoding::UTF8_WITH_BOM:
     // Assume internal encoding is UTF-8
-    case codecvt::None:
+    case codecvt_Encoding::None:
     // No encoding
     default:
       this->m_noconv = true;
diff --git a/Source/cm_codecvt.hxx b/Source/cm_codecvt.hxx
index f628de7..c25f9ef 100644
--- a/Source/cm_codecvt.hxx
+++ b/Source/cm_codecvt.hxx
@@ -7,21 +7,14 @@
 #include <cwchar>
 #include <locale>
 
+enum class codecvt_Encoding;
+
 class codecvt : public std::codecvt<char, char, mbstate_t>
 {
 public:
-  enum Encoding
-  {
-    None,
-    UTF8,
-    UTF8_WITH_BOM,
-    ANSI,
-    ConsoleOutput,
-  };
-
 #ifndef CMAKE_BOOTSTRAP
 
-  codecvt(Encoding e);
+  codecvt(codecvt_Encoding e);
 
 protected:
   ~codecvt() override;
diff --git a/Source/cm_codecvt_Encoding.hxx b/Source/cm_codecvt_Encoding.hxx
new file mode 100644
index 0000000..b91ad8f
--- /dev/null
+++ b/Source/cm_codecvt_Encoding.hxx
@@ -0,0 +1,12 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+enum class codecvt_Encoding
+{
+  None,
+  UTF8,
+  UTF8_WITH_BOM,
+  ANSI,
+  ConsoleOutput,
+};
diff --git a/Source/cm_fileno.cxx b/Source/cm_fileno.cxx
new file mode 100644
index 0000000..a40c5ca
--- /dev/null
+++ b/Source/cm_fileno.cxx
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#if !defined(_POSIX_C_SOURCE) && !defined(_WIN32) && !defined(__sun) &&       \
+  !defined(__OpenBSD__)
+/* POSIX APIs are needed */
+// NOLINTNEXTLINE(bugprone-reserved-identifier)
+#  define _POSIX_C_SOURCE 200809L
+#endif
+
+#include "cm_fileno.hxx"
+
+int cm_fileno(FILE* f)
+{
+  return fileno(f);
+}
diff --git a/Source/cm_fileno.hxx b/Source/cm_fileno.hxx
new file mode 100644
index 0000000..3abcdcf
--- /dev/null
+++ b/Source/cm_fileno.hxx
@@ -0,0 +1,7 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <cstdio>
+
+int cm_fileno(FILE* f);
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index f30d4d3..942c59b 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -23,14 +23,11 @@
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
-#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32)
-#  include <unistd.h>
-#endif
-
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/RegularExpression.hxx"
 
+#include "cm_fileno.hxx"
 #include "cm_sys_stat.h"
 
 #include "cmBuildOptions.h"
@@ -40,7 +37,11 @@
 #include "cmCommands.h"
 #ifdef CMake_ENABLE_DEBUGGER
 #  include "cmDebuggerAdapter.h"
-#  include "cmDebuggerPipeConnection.h"
+#  ifdef _WIN32
+#    include "cmDebuggerWindowsPipeConnection.h"
+#  else //!_WIN32
+#    include "cmDebuggerPosixPipeConnection.h"
+#  endif //_WIN32
 #endif
 #include "cmDocumentation.h"
 #include "cmDocumentationEntry.h"
@@ -94,7 +95,6 @@
 #    include "cmGlobalBorlandMakefileGenerator.h"
 #    include "cmGlobalJOMMakefileGenerator.h"
 #    include "cmGlobalNMakeMakefileGenerator.h"
-#    include "cmGlobalVisualStudio11Generator.h"
 #    include "cmGlobalVisualStudio12Generator.h"
 #    include "cmGlobalVisualStudio14Generator.h"
 #    include "cmGlobalVisualStudio9Generator.h"
@@ -785,6 +785,9 @@
 
       mf.SetArgcArgv(args);
     }
+    if (!cmSystemTools::FileExists(path, true)) {
+      cmSystemTools::Error("Not a file: " + path);
+    }
     if (!mf.ReadListFile(path)) {
       cmSystemTools::Error("Error processing file: " + path);
     }
@@ -2507,6 +2510,18 @@
                         "Name of generator toolset.", cmStateEnums::INTERNAL);
   }
 
+  if (!this->State->GetInitializedCacheValue(
+        "CMAKE_CROSSCOMPILING_EMULATOR")) {
+    cm::optional<std::string> emulator =
+      cmSystemTools::GetEnvVar("CMAKE_CROSSCOMPILING_EMULATOR");
+    if (emulator && !emulator->empty()) {
+      std::string message =
+        "Emulator to run executables and tests when cross compiling.";
+      this->AddCacheEntry("CMAKE_CROSSCOMPILING_EMULATOR", *emulator, message,
+                          cmStateEnums::STRING);
+    }
+  }
+
   // reset any system configuration information, except for when we are
   // InTryCompile. With TryCompile the system info is taken from the parent's
   // info to save time
@@ -2605,7 +2620,6 @@
   static VSVersionedGenerator const vsGenerators[] = {
     { "14.0", "Visual Studio 14 2015" }, //
     { "12.0", "Visual Studio 12 2013" }, //
-    { "11.0", "Visual Studio 11 2012" }, //
     { "9.0", "Visual Studio 9 2008" }
   };
   static const char* const vsEntries[] = {
@@ -2990,7 +3004,6 @@
     cmGlobalVisualStudioVersionedGenerator::NewFactory15());
   this->Generators.push_back(cmGlobalVisualStudio14Generator::NewFactory());
   this->Generators.push_back(cmGlobalVisualStudio12Generator::NewFactory());
-  this->Generators.push_back(cmGlobalVisualStudio11Generator::NewFactory());
   this->Generators.push_back(cmGlobalVisualStudio9Generator::NewFactory());
   this->Generators.push_back(cmGlobalBorlandMakefileGenerator::NewFactory());
   this->Generators.push_back(cmGlobalNMakeMakefileGenerator::NewFactory());
@@ -3254,7 +3267,7 @@
   // If any byproduct of makefile generation is missing we must re-run.
   cmList products{ mf.GetDefinition("CMAKE_MAKEFILE_PRODUCTS") };
   for (auto const& p : products) {
-    if (!(cmSystemTools::FileExists(p) || cmSystemTools::FileIsSymlink(p))) {
+    if (!cmSystemTools::PathExists(p)) {
       if (verbose) {
         cmSystemTools::Stdout(
           cmStrCat("Re-run cmake, missing byproduct: ", p, '\n'));
@@ -3904,19 +3917,15 @@
   const std::vector<std::string>& args)
 {
   cmUVProcessChainBuilder builder;
-  builder
-    .AddCommand(args)
-#  ifdef _WIN32
-    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, _fileno(stdout))
-    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, _fileno(stderr));
-#  else
-    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, STDOUT_FILENO)
-    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, STDERR_FILENO);
-#  endif
+  builder.AddCommand(args)
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                       cm_fileno(stdout))
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                       cm_fileno(stderr));
   return [builder]() -> int {
     auto chain = builder.Start();
     chain.Wait();
-    return static_cast<int>(chain.GetStatus().front()->ExitStatus);
+    return static_cast<int>(chain.GetStatus(0).ExitStatus);
   };
 }
 #endif
diff --git a/Source/cmake.h b/Source/cmake.h
index 156d061..58f90c9 100644
--- a/Source/cmake.h
+++ b/Source/cmake.h
@@ -17,8 +17,9 @@
 #include <cm/string_view>
 #include <cmext/string_view>
 
-#include "cmDocumentationEntry.h"
+#include "cmDocumentationEntry.h" // IWYU pragma: keep
 #include "cmGeneratedFileStream.h"
+#include "cmGlobalGeneratorFactory.h"
 #include "cmInstalledFile.h"
 #include "cmListFileCache.h"
 #include "cmMessageType.h"
@@ -48,7 +49,6 @@
 class cmFileAPI;
 class cmFileTimeCache;
 class cmGlobalGenerator;
-class cmGlobalGeneratorFactory;
 class cmMakefile;
 class cmMessenger;
 class cmVariableWatch;
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index 0c8d8db..3dcfbf1 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -11,8 +11,11 @@
 #include <cm3p/uv.h>
 #include <fcntl.h>
 
+#include "cm_fileno.hxx"
+
 #include "cmCommandLineArgument.h"
 #include "cmConsoleBuf.h"
+#include "cmCryptoHash.h"
 #include "cmDuration.h"
 #include "cmGlobalGenerator.h"
 #include "cmList.h"
@@ -28,6 +31,7 @@
 #include "cmSystemTools.h"
 #include "cmTransformDepfile.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmUtils.hxx"
 #include "cmValue.h"
 #include "cmVersion.h"
@@ -70,7 +74,6 @@
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
-#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/Terminal.h"
 
@@ -293,14 +296,8 @@
     }
   }
 
-  std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp(
-    cmsysProcess_New(), cmsysProcess_Delete);
-  std::vector<const char*> argv(command.size() + 1);
-  std::transform(command.begin(), command.end(), argv.begin(),
-                 [](std::string const& s) { return s.c_str(); });
-  argv.back() = nullptr;
-  cmsysProcess_SetCommand(cp.get(), argv.data());
-  cmsysProcess_SetWorkingDirectory(cp.get(), currentBinaryDir.c_str());
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir);
 
   cmsys::ofstream fout(depFile.c_str());
   if (!fout) {
@@ -311,22 +308,18 @@
   CLOutputLogger errLogger(std::cerr);
 
   // Start the process.
-  cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger);
+  auto result =
+    cmProcessTools::RunProcess(builder, &includeParser, &errLogger);
+  auto const& subStatus = result.front();
 
   int status = 0;
   // handle status of process
-  switch (cmsysProcess_GetState(cp.get())) {
-    case cmsysProcess_State_Exited:
-      status = cmsysProcess_GetExitValue(cp.get());
-      break;
-    case cmsysProcess_State_Exception:
-      status = 1;
-      break;
-    case cmsysProcess_State_Error:
-      status = 2;
-      break;
-    default:
-      break;
+  if (subStatus.SpawnResult != 0) {
+    status = 2;
+  } else if (subStatus.TermSignal != 0) {
+    status = 1;
+  } else {
+    status = static_cast<int>(subStatus.ExitStatus);
   }
 
   if (status != 0) {
@@ -1025,8 +1018,8 @@
           // Complain if the -f option was not given and
           // either file does not exist or
           // file could not be removed and still exists
-          bool file_exists_or_forced_remove = cmSystemTools::FileExists(arg) ||
-            cmSystemTools::FileIsSymlink(arg) || force;
+          bool file_exists_or_forced_remove =
+            cmSystemTools::PathExists(arg) || force;
           if (cmSystemTools::FileIsDirectory(arg)) {
             if (!cmRemoveDirectory(arg, recursive)) {
               return_value = 1;
@@ -1114,7 +1107,8 @@
 
       int ret = 0;
       auto time_start = std::chrono::steady_clock::now();
-      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
+      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr,
+                                      cmSystemTools::OUTPUT_PASSTHROUGH);
       auto time_finish = std::chrono::steady_clock::now();
 
       std::chrono::duration<double> time_elapsed = time_finish - time_start;
@@ -1245,8 +1239,7 @@
     // supporting them.
     if (args[1] == "create_symlink" && args.size() == 4) {
       std::string const& destinationFileName = args[3];
-      if ((cmSystemTools::FileExists(destinationFileName) ||
-           cmSystemTools::FileIsSymlink(destinationFileName)) &&
+      if (cmSystemTools::PathExists(destinationFileName) &&
           !cmSystemTools::RemoveFile(destinationFileName)) {
         std::string emsg = cmSystemTools::GetLastSystemError();
         std::cerr << "failed to create symbolic link '" << destinationFileName
@@ -1272,8 +1265,7 @@
         return 1;
       }
 
-      if ((cmSystemTools::FileExists(destinationFileName) ||
-           cmSystemTools::FileIsSymlink(destinationFileName)) &&
+      if (cmSystemTools::PathExists(destinationFileName) &&
           !cmSystemTools::RemoveFile(destinationFileName)) {
         std::string emsg = cmSystemTools::GetLastSystemError();
         std::cerr << "failed to create hard link '" << destinationFileName
@@ -1441,13 +1433,17 @@
     if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
       cm::string_view const infoFile = args[2];
       cm::string_view const config = args[3];
-      return cmQtAutoMocUic(infoFile, config) ? 0 : 1;
+      cm::string_view const executableConfig =
+        (args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view();
+      return cmQtAutoMocUic(infoFile, config, executableConfig) ? 0 : 1;
     }
     if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) {
       cm::string_view const infoFile = args[2];
       cm::string_view const config =
         (args.size() > 3) ? cm::string_view(args[3]) : cm::string_view();
-      return cmQtAutoRcc(infoFile, config) ? 0 : 1;
+      cm::string_view const executableConfig =
+        (args.size() >= 5) ? cm::string_view(args[4]) : cm::string_view();
+      return cmQtAutoRcc(infoFile, config, executableConfig) ? 0 : 1;
     }
 #endif
 
@@ -1691,11 +1687,8 @@
       std::cerr << "Error: " << filename << " is a directory" << std::endl;
       retval++;
     } else {
-      std::string value
-#ifndef CMAKE_BOOTSTRAP
-        = cmSystemTools::ComputeFileHash(filename, algo)
-#endif
-        ;
+      cmCryptoHash hasher(algo);
+      std::string value = hasher.HashFile(filename);
       if (value.empty()) {
         // To mimic "md5sum/shasum" behavior in a shell:
         std::cerr << filename << ": No such file or directory" << std::endl;
@@ -1755,7 +1748,7 @@
 cmsys::Status cmcmd::SymlinkInternal(std::string const& file,
                                      std::string const& link)
 {
-  if (cmSystemTools::FileExists(link) || cmSystemTools::FileIsSymlink(link)) {
+  if (cmSystemTools::PathExists(link)) {
     cmSystemTools::RemoveFile(link);
   }
   std::string linktext = cmSystemTools::GetFilenameName(file);
@@ -1893,21 +1886,6 @@
     }
   }
 
-  // Allocate a process instance.
-  cmsysProcess* cp = cmsysProcess_New();
-  if (!cp) {
-    std::cerr << "Error allocating process instance in link script."
-              << std::endl;
-    return 1;
-  }
-
-  // Children should share stdout and stderr with this process.
-  cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
-  cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
-
-  // Run the command lines verbatim.
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
-
   // Read command lines from the script.
   cmsys::ifstream fin(args[2].c_str());
   if (!fin) {
@@ -1925,9 +1903,24 @@
       continue;
     }
 
+    // Allocate a process instance.
+    cmUVProcessChainBuilder builder;
+
+    // Children should share stdout and stderr with this process.
+    builder
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
+                         cm_fileno(stdout))
+      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
+                         cm_fileno(stderr));
+
     // Setup this command line.
-    const char* cmd[2] = { command.c_str(), nullptr };
-    cmsysProcess_SetCommand(cp, cmd);
+    std::vector<std::string> args2;
+#ifdef _WIN32
+    cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2);
+#else
+    cmSystemTools::ParseUnixCommandLine(command.c_str(), args2);
+#endif
+    builder.AddCommand(args2);
 
     // Report the command if verbose output is enabled.
     if (verbose) {
@@ -1935,35 +1928,29 @@
     }
 
     // Run the command and wait for it to exit.
-    cmsysProcess_Execute(cp);
-    cmsysProcess_WaitForExit(cp, nullptr);
+    auto chain = builder.Start();
+    chain.Wait();
 
     // Report failure if any.
-    switch (cmsysProcess_GetState(cp)) {
-      case cmsysProcess_State_Exited: {
-        int value = cmsysProcess_GetExitValue(cp);
-        if (value != 0) {
-          result = value;
+    auto const& status = chain.GetStatus(0);
+    auto exception = status.GetException();
+    switch (exception.first) {
+      case cmUVProcessChain::ExceptionCode::None:
+        if (status.ExitStatus != 0) {
+          result = static_cast<int>(status.ExitStatus);
         }
-      } break;
-      case cmsysProcess_State_Exception:
-        std::cerr << "Error running link command: "
-                  << cmsysProcess_GetExceptionString(cp) << std::endl;
-        result = 1;
         break;
-      case cmsysProcess_State_Error:
-        std::cerr << "Error running link command: "
-                  << cmsysProcess_GetErrorString(cp) << std::endl;
+      case cmUVProcessChain::ExceptionCode::Spawn:
+        std::cerr << "Error running link command: " << exception.second;
         result = 2;
         break;
       default:
+        std::cerr << "Error running link command: " << exception.second;
+        result = 1;
         break;
     }
   }
 
-  // Free the process instance.
-  cmsysProcess_Delete(cp);
-
   // Return the final resulting return value.
   return result;
 }
@@ -2008,7 +1995,7 @@
     .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR)
     .AddCommand(command);
   auto process = builder.Start();
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     std::cerr << "Failed to start preprocessor.";
     return 1;
   }
@@ -2016,12 +2003,9 @@
     std::cerr << "Failed to wait for preprocessor";
     return 1;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
-    auto* errorStream = process.ErrorStream();
-    if (errorStream) {
-      std::cerr << errorStream->rdbuf();
-    }
+  if (process.GetStatus(0).ExitStatus != 0) {
+    cmUVPipeIStream errorStream(process.GetLoop(), process.ErrorStream());
+    std::cerr << errorStream.rdbuf();
 
     return 1;
   }
@@ -2130,7 +2114,7 @@
     .AddCommand(resource_compile);
   auto process = builder.Start();
   result = 0;
-  if (!process.Valid()) {
+  if (!process.Valid() || process.GetStatus(0).SpawnResult != 0) {
     std::cerr << "Failed to start resource compiler.";
     result = 1;
   } else {
@@ -2144,12 +2128,9 @@
   if (result != 0) {
     return result;
   }
-  auto status = process.GetStatus();
-  if (!status[0] || status[0]->ExitStatus != 0) {
-    auto* errorStream = process.ErrorStream();
-    if (errorStream) {
-      std::cerr << errorStream->rdbuf();
-    }
+  if (process.GetStatus(0).ExitStatus != 0) {
+    cmUVPipeIStream errorStream(process.GetLoop(), process.ErrorStream());
+    std::cerr << errorStream.rdbuf();
     return 1;
   }
 
@@ -2546,14 +2527,17 @@
   std::vector<std::string> mtCommand;
   mtCommand.push_back(this->MtPath.empty() ? "mt" : this->MtPath);
   mtCommand.emplace_back("/nologo");
-  mtCommand.emplace_back("/manifest");
 
   // add the linker generated manifest if the file exists.
   if (this->LinkGeneratesManifest &&
       cmSystemTools::FileExists(this->LinkerManifestFile)) {
+    mtCommand.emplace_back("/manifest");
     mtCommand.push_back(this->LinkerManifestFile);
   }
-  cm::append(mtCommand, this->UserManifests);
+  for (auto const& m : this->UserManifests) {
+    mtCommand.emplace_back("/manifest");
+    mtCommand.push_back(m);
+  }
   mtCommand.push_back(out);
   if (notify) {
     // Add an undocumented option that enables a special return
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index 2b8eedd..2b7f2cc 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -634,7 +634,7 @@
   Directory DynamicLoader Encoding Glob RegularExpression SystemTools
   CommandLineArguments FStream SystemInformation ConsoleBuf Status
   )
-foreach(cpp ${cppclasses})
+foreach(cpp IN LISTS cppclasses)
   if(KWSYS_USE_${cpp})
     # Use the corresponding class.
     set(KWSYS_CLASSES ${KWSYS_CLASSES} ${cpp})
@@ -647,7 +647,7 @@
 endforeach()
 
 # Add selected C components.
-foreach(c
+foreach(c IN ITEMS
     Process Base64 Encoding MD5 Terminal System String
     )
   if(KWSYS_USE_${c})
@@ -679,7 +679,7 @@
 endif()
 
 # Add selected C sources.
-foreach(c Base64 Encoding MD5 Terminal System String)
+foreach(c IN ITEMS Base64 Encoding MD5 Terminal System String)
   if(KWSYS_USE_${c})
     if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${c}C.c)
       list(APPEND KWSYS_C_SRCS ${c}C.c)
@@ -690,7 +690,7 @@
 endforeach()
 
 # Configure headers of C++ classes and construct the list of sources.
-foreach(c ${KWSYS_CLASSES})
+foreach(c IN LISTS KWSYS_CLASSES)
   # Add this source to the list of source files for the library.
   if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${c}CXX.cxx)
     list(APPEND KWSYS_CXX_SRCS ${c}CXX.cxx)
@@ -712,7 +712,7 @@
 endforeach()
 
 # Configure C headers.
-foreach(h ${KWSYS_H_FILES})
+foreach(h IN LISTS KWSYS_H_FILES)
   # Configure the header into the given directory.
   configure_file(${PROJECT_SOURCE_DIR}/${h}.h.in ${KWSYS_HEADER_DIR}/${h}.h
                  @ONLY IMMEDIATE)
@@ -727,7 +727,7 @@
 endforeach()
 
 # Configure other C++ headers.
-foreach(h ${KWSYS_HXX_FILES})
+foreach(h IN LISTS KWSYS_HXX_FILES)
   # Configure the header into the given directory.
   configure_file(${PROJECT_SOURCE_DIR}/${h}.hxx.in ${KWSYS_HEADER_DIR}/${h}.hxx
                  @ONLY IMMEDIATE)
@@ -956,9 +956,11 @@
     add_executable(${KWSYS_NAMESPACE}TestsC ${KWSYS_C_TEST_SRCS})
     set_property(TARGET ${KWSYS_NAMESPACE}TestsC PROPERTY LABELS ${KWSYS_LABELS_EXE})
     target_link_libraries(${KWSYS_NAMESPACE}TestsC ${KWSYS_TARGET_C_LINK})
-    foreach(testfile ${KWSYS_C_TESTS})
+    foreach(testfile IN LISTS KWSYS_C_TESTS)
       get_filename_component(test "${testfile}" NAME_WE)
-      add_test(kwsys.${test} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsC ${test} ${KWSYS_TEST_ARGS_${test}})
+      add_test(NAME kwsys.${test}
+        COMMAND ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsC ${test} ${KWSYS_TEST_ARGS_${test}}
+        )
       set_property(TEST kwsys.${test} PROPERTY LABELS ${KWSYS_LABELS_TEST})
     endforeach()
 
@@ -1080,9 +1082,11 @@
       -p
       some junk at the end
       )
-    foreach(testfile ${KWSYS_CXX_TESTS})
+    foreach(testfile IN LISTS KWSYS_CXX_TESTS)
       get_filename_component(test "${testfile}" NAME_WE)
-      add_test(kwsys.${test} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsCxx ${test} ${KWSYS_TEST_ARGS_${test}})
+      add_test(NAME kwsys.${test}
+        COMMAND ${EXEC_DIR}/${KWSYS_NAMESPACE}TestsCxx ${test} ${KWSYS_TEST_ARGS_${test}}
+        )
       set_property(TEST kwsys.${test} PROPERTY LABELS ${KWSYS_LABELS_TEST})
     endforeach()
 
@@ -1091,10 +1095,12 @@
     set_property(TARGET ${KWSYS_NAMESPACE}TestProcess PROPERTY LABELS ${KWSYS_LABELS_EXE})
     target_link_libraries(${KWSYS_NAMESPACE}TestProcess ${KWSYS_TARGET_C_LINK})
     #set(KWSYS_TEST_PROCESS_7 7) # uncomment to run timing-sensitive test locally
-    foreach(n 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7} 9 10)
-      add_test(kwsys.testProcess-${n} ${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n})
+    foreach(n IN ITEMS 1 2 3 4 5 6 ${KWSYS_TEST_PROCESS_7} 9 10)
+      add_test(NAME kwsys.testProcess-${n}
+        COMMAND ${EXEC_DIR}/${KWSYS_NAMESPACE}TestProcess ${n}
+        )
       set_property(TEST kwsys.testProcess-${n} PROPERTY LABELS ${KWSYS_LABELS_TEST})
-      set_tests_properties(kwsys.testProcess-${n} PROPERTIES TIMEOUT 120)
+      set_property(TEST kwsys.testProcess-${n} PROPERTY TIMEOUT 120)
     endforeach()
 
     set(testProcess_COMPILE_FLAGS "")
@@ -1121,9 +1127,9 @@
     # Configure some test properties.
     if(KWSYS_STANDALONE)
       # We expect test to fail
-      set_tests_properties(kwsys.testFail PROPERTIES WILL_FAIL ON)
+      set_property(TEST kwsys.testFail PROPERTY WILL_FAIL ON)
       get_test_property(kwsys.testFail WILL_FAIL wfv)
-      set_tests_properties(kwsys.testFail PROPERTIES MEASUREMENT "Some Key=Some Value")
+      set_property(TEST kwsys.testFail PROPERTY MEASUREMENT "Some Key=Some Value")
       message(STATUS "GET_TEST_PROPERTY returned: ${wfv}")
     endif()
 
@@ -1133,7 +1139,7 @@
 
     # Suppress known consistent failures on buggy systems.
     if(KWSYS_TEST_BOGUS_FAILURES)
-      set_tests_properties(${KWSYS_TEST_BOGUS_FAILURES} PROPERTIES WILL_FAIL ON)
+      set_property(TEST ${KWSYS_TEST_BOGUS_FAILURES} PROPERTY WILL_FAIL ON)
     endif()
 
   endif()
diff --git a/Source/kwsys/SystemInformation.cxx b/Source/kwsys/SystemInformation.cxx
index 7f8485e..369ff9a 100644
--- a/Source/kwsys/SystemInformation.cxx
+++ b/Source/kwsys/SystemInformation.cxx
@@ -3443,7 +3443,7 @@
 
   FILE* fd = fopen("/proc/cpuinfo", "r");
   if (!fd) {
-    std::cout << "Problem opening /proc/cpuinfo" << std::endl;
+    std::cerr << "Problem opening /proc/cpuinfo\n";
     return false;
   }
 
@@ -3454,7 +3454,7 @@
   }
   fclose(fd);
   if (fileSize < 2) {
-    std::cout << "No data in /proc/cpuinfo" << std::endl;
+    std::cerr << "No data in /proc/cpuinfo\n";
     return false;
   }
   buffer.resize(fileSize - 2);
@@ -4162,7 +4162,7 @@
   struct utsname unameInfo;
   int errorFlag = uname(&unameInfo);
   if (errorFlag != 0) {
-    std::cout << "Problem calling uname(): " << strerror(errno) << std::endl;
+    std::cerr << "Problem calling uname(): " << strerror(errno) << "\n";
     return false;
   }
 
@@ -4182,7 +4182,7 @@
 
   FILE* fd = fopen("/proc/meminfo", "r");
   if (!fd) {
-    std::cout << "Problem opening /proc/meminfo" << std::endl;
+    std::cerr << "Problem opening /proc/meminfo\n";
     return false;
   }
 
@@ -4221,7 +4221,7 @@
       this->TotalVirtualMemory = value[mSwapTotal] / 1024;
       this->AvailableVirtualMemory = value[mSwapFree] / 1024;
     } else {
-      std::cout << "Problem parsing /proc/meminfo" << std::endl;
+      std::cerr << "Problem parsing /proc/meminfo\n";
       fclose(fd);
       return false;
     }
@@ -4248,7 +4248,7 @@
       this->AvailablePhysicalMemory =
         (ap + buffersMem + cachedMem) >> 10 >> 10;
     } else {
-      std::cout << "Problem parsing /proc/meminfo" << std::endl;
+      std::cerr << "Problem parsing /proc/meminfo\n";
       fclose(fd);
       return false;
     }
@@ -4472,8 +4472,8 @@
   typedef BOOL(WINAPI * GetLogicalProcessorInformationType)(
     PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
   static GetLogicalProcessorInformationType pGetLogicalProcessorInformation =
-    (GetLogicalProcessorInformationType)GetProcAddress(
-      GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation");
+    reinterpret_cast<GetLogicalProcessorInformationType>(GetProcAddress(
+      GetModuleHandleW(L"kernel32"), "GetLogicalProcessorInformation"));
 
   if (!pGetLogicalProcessorInformation) {
     // Fallback to approximate implementation on ancient Windows versions.
diff --git a/Templates/MSBuild/FlagTables/v140_CL.json b/Templates/MSBuild/FlagTables/v140_CL.json
index 58e22ba..d479af2 100644
--- a/Templates/MSBuild/FlagTables/v140_CL.json
+++ b/Templates/MSBuild/FlagTables/v140_CL.json
@@ -372,7 +372,7 @@
   },
   {
     "name": "EnableEnhancedInstructionSet",
-    "switch": "",
+    "switch": "arch:NotSet",
     "comment": "Not Set",
     "value": "NotSet",
     "flags": []
diff --git a/Templates/MSBuild/FlagTables/v141_CL.json b/Templates/MSBuild/FlagTables/v141_CL.json
index 604e6b6..d79ea90 100644
--- a/Templates/MSBuild/FlagTables/v141_CL.json
+++ b/Templates/MSBuild/FlagTables/v141_CL.json
@@ -393,7 +393,7 @@
   },
   {
     "name": "EnableEnhancedInstructionSet",
-    "switch": "",
+    "switch": "arch:NotSet",
     "comment": "Not Set",
     "value": "NotSet",
     "flags": []
diff --git a/Templates/MSBuild/FlagTables/v142_CL.json b/Templates/MSBuild/FlagTables/v142_CL.json
index 4f2dce8..3c86e22 100644
--- a/Templates/MSBuild/FlagTables/v142_CL.json
+++ b/Templates/MSBuild/FlagTables/v142_CL.json
@@ -407,7 +407,7 @@
   },
   {
     "name": "EnableEnhancedInstructionSet",
-    "switch": "",
+    "switch": "arch:NotSet",
     "comment": "Not Set",
     "value": "NotSet",
     "flags": []
diff --git a/Templates/MSBuild/FlagTables/v143_CL.json b/Templates/MSBuild/FlagTables/v143_CL.json
index 119e171..a6b60aa 100644
--- a/Templates/MSBuild/FlagTables/v143_CL.json
+++ b/Templates/MSBuild/FlagTables/v143_CL.json
@@ -407,7 +407,7 @@
   },
   {
     "name": "EnableEnhancedInstructionSet",
-    "switch": "",
+    "switch": "arch:NotSet",
     "comment": "Not Set",
     "value": "NotSet",
     "flags": []
diff --git a/Tests/Assembler/CMakeLists.txt b/Tests/Assembler/CMakeLists.txt
index 4635f03..56de257 100644
--- a/Tests/Assembler/CMakeLists.txt
+++ b/Tests/Assembler/CMakeLists.txt
@@ -12,7 +12,7 @@
 # and also generate assembler files from C:
 if("${CMAKE_GENERATOR}" MATCHES "Makefile|Xcode|Ninja" AND
     NOT CMAKE_OSX_ARCHITECTURES MATCHES ";")
-  if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|LCC|Clang|AppleClang|HP|SunPro|XL)$") OR (CMAKE_C_COMPILER_ID MATCHES "Intel"  AND  UNIX)
+  if((CMAKE_C_COMPILER_ID MATCHES "^(GNU|LCC|Clang|AppleClang|HP|SunPro|XL|OrangeC)$") OR (CMAKE_C_COMPILER_ID MATCHES "Intel"  AND  UNIX)
      AND NOT (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND "x${CMAKE_C_COMPILER_FRONTEND_VARIANT}" STREQUAL "xMSVC"))
     set(C_FLAGS "${CMAKE_C_FLAGS}")
     separate_arguments(C_FLAGS)
diff --git a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt
index dd4fe02..f20eb5d 100644
--- a/Tests/CMakeCommands/target_compile_options/CMakeLists.txt
+++ b/Tests/CMakeCommands/target_compile_options/CMakeLists.txt
@@ -11,11 +11,11 @@
   "${CMAKE_CURRENT_SOURCE_DIR}/main.cpp"
 )
 target_compile_options(target_compile_options
-  PRIVATE $<$<CXX_COMPILER_ID:AppleClang,IBMClang,Clang,GNU,LCC>:-DMY_PRIVATE_DEFINE>
+  PRIVATE $<$<CXX_COMPILER_ID:AppleClang,IBMClang,CrayClang,Clang,GNU,LCC>:-DMY_PRIVATE_DEFINE>
   PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,LCC>:-DMY_PUBLIC_DEFINE>
-  PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,LCC,Clang,AppleClang,IBMClang>:-DMY_MUTLI_COMP_PUBLIC_DEFINE>
+  PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,GNU,LCC,Clang,AppleClang,CrayClang,IBMClang>:-DMY_MUTLI_COMP_PUBLIC_DEFINE>
   INTERFACE $<$<CXX_COMPILER_ID:GNU,LCC>:-DMY_INTERFACE_DEFINE>
-  INTERFACE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,IBMClang>:-DMY_MULTI_COMP_INTERFACE_DEFINE>
+  INTERFACE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,CrayClang,IBMClang>:-DMY_MULTI_COMP_INTERFACE_DEFINE>
 )
 
 if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC")
@@ -51,7 +51,7 @@
 endif()
 
 target_compile_options(consumer
-  PRIVATE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,IBMClang>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>>
+  PRIVATE $<$<CXX_COMPILER_ID:GNU,LCC,Clang,AppleClang,CrayClang,IBMClang>:$<TARGET_PROPERTY:target_compile_options,INTERFACE_COMPILE_OPTIONS>>
 )
 
 if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|LCC")
diff --git a/Tests/CMakeGUI/CMakeGUITest.cmake b/Tests/CMakeGUI/CMakeGUITest.cmake
index 2c6baf3..9e2c8cc 100644
--- a/Tests/CMakeGUI/CMakeGUITest.cmake
+++ b/Tests/CMakeGUI/CMakeGUITest.cmake
@@ -59,6 +59,12 @@
 
   set(ENV{CMake_GUI_TEST_NAME} "${name}")
   set(ENV{CMake_GUI_CONFIG_DIR} "${_workdir}/config")
+  if(DEFINED ENV{PWD})
+    set(_old_pwd "$ENV{PWD}")
+  else()
+    set(_old_pwd)
+  endif()
+  set(ENV{PWD} "${_workdir}")
   execute_process(
     COMMAND "${CMakeGUITest_COMMAND}" ${_rcgt_ARGS}
     WORKING_DIRECTORY "${_workdir}"
@@ -66,6 +72,11 @@
     OUTPUT_VARIABLE _output
     ERROR_VARIABLE _error
     )
+  if(DEFINED _old_pwd)
+    set(ENV{PWD} "${_old_pwd}")
+  else()
+    set(ENV{PWD})
+  endif()
   if(_result)
     set(_fail 1)
     string(REPLACE "\n" "\n  " _formatted_output "${_output}")
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 5c14de2..b44c8dd 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -11,6 +11,7 @@
   testCTestResourceAllocator.cxx
   testCTestResourceSpec.cxx
   testCTestResourceGroups.cxx
+  testDebug.cxx
   testGccDepfileReader.cxx
   testGeneratedFileStream.cxx
   testJSONHelpers.cxx
@@ -31,6 +32,7 @@
   testCMExtAlgorithm.cxx
   testCMExtEnumSet.cxx
   testList.cxx
+  testCMakePath.cxx
   )
 if(CMake_ENABLE_DEBUGGER)
   list(APPEND CMakeLib_TESTS
@@ -63,10 +65,18 @@
 endif()
 
 configure_file(testXMLParser.h.in testXMLParser.h @ONLY)
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/testUVProcessChainInput.txt" "HELLO WORLD!")
 
 create_test_sourcelist(CMakeLib_TEST_SRCS CMakeLibTests.cxx ${CMakeLib_TESTS})
 add_executable(CMakeLibTests ${CMakeLib_TEST_SRCS})
-target_link_libraries(CMakeLibTests CMakeLib CTestLib)
+target_link_libraries(CMakeLibTests PRIVATE CTestLib CMakeLib)
+if(CMake_BUILD_PCH)
+  target_precompile_headers(CMakeLibTests PRIVATE "<iostream>" "<cm3p/uv.h>")
+  target_compile_definitions(CMakeLibTests PRIVATE "NOMINMAX")
+endif()
+if(WIN32)
+  target_compile_definitions(CMakeLibTests PRIVATE WIN32_LEAN_AND_MEAN)
+endif()
 
 set_property(TARGET CMakeLibTests PROPERTY C_CLANG_TIDY "")
 set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "")
@@ -102,4 +112,7 @@
     add_test(NAME CMakeLib.testDebuggerNamedPipe-${case} COMMAND testDebuggerNamedPipe ${testDebuggerNamedPipe_${case}_ARGS})
     set_property(TEST CMakeLib.testDebuggerNamedPipe-${case} PROPERTY TIMEOUT 300)
   endforeach()
+  if(WIN32)
+    target_compile_definitions(testDebuggerNamedPipe PRIVATE WIN32_LEAN_AND_MEAN)
+  endif()
 endif()
diff --git a/Tests/CMakeLib/testCMExtAlgorithm.cxx b/Tests/CMakeLib/testCMExtAlgorithm.cxx
index c909f24..53b0302 100644
--- a/Tests/CMakeLib/testCMExtAlgorithm.cxx
+++ b/Tests/CMakeLib/testCMExtAlgorithm.cxx
@@ -1,6 +1,5 @@
 #include <iostream>
 #include <memory>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
diff --git a/Tests/CMakeLib/testCMakePath.cxx b/Tests/CMakeLib/testCMakePath.cxx
new file mode 100644
index 0000000..aa17e50
--- /dev/null
+++ b/Tests/CMakeLib/testCMakePath.cxx
@@ -0,0 +1,441 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include <iostream>
+#include <string>
+#include <utility>
+
+#include <cm/string_view>
+#include <cmext/string_view>
+
+#include "cmCMakePath.h"
+
+namespace {
+
+void checkResult(bool success)
+{
+  if (!success) {
+    std::cout << " => failed";
+  }
+  std::cout << std::endl;
+}
+
+bool testConstructors()
+{
+  std::cout << "testConstructors()";
+
+  bool result = true;
+
+  {
+    cmCMakePath path;
+    if (!path.String().empty() || path != cmCMakePath{}) {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path{ "aa/bb" };
+    if (path.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string s{ "aa/bb" };
+    cmCMakePath path{ s };
+    if (path.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path{ "aa/bb"_s };
+    if (path.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2("aa/bb"_s);
+
+    if (path1 != path2) {
+      result = false;
+    }
+    if (path1.String() != "aa/bb") {
+      result = false;
+    }
+    if (path1.String() != path2.String()) {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ path1 };
+
+    if (path1 != path2) {
+      result = false;
+    }
+    if (path1.String() != "aa/bb") {
+      result = false;
+    }
+    if (path1.String() != path2.String()) {
+      result = false;
+    }
+  }
+
+  checkResult(result);
+
+  return result;
+}
+
+bool testAssign()
+{
+  std::cout << "testAssign()";
+
+  bool result = true;
+
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 = path1;
+    if (path1 != path2) {
+      result = false;
+    }
+    if (path1.String() != "aa/bb") {
+      result = false;
+    }
+    if (path1.String() != path2.String()) {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 = std::move(path1);
+    if (path2.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 = path1;
+    if (path2.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 = std::move(path1);
+    if (path2.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cm::string_view path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 = path1;
+    if (path2.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    char path1[] = "aa/bb";
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 = path1;
+    if (path2.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Assign(path1);
+    if (path2.String() != path1) {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Assign(std::move(path1));
+    if (path2.String() != "aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cm::string_view path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Assign(path1);
+    if (path2.String() != path1) {
+      result = false;
+    }
+  }
+  {
+    char path1[] = "aa/bb";
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Assign(path1);
+    if (path2.String() != path1) {
+      result = false;
+    }
+  }
+
+  checkResult(result);
+
+  return result;
+}
+
+bool testConcat()
+{
+  std::cout << "testConcat()";
+
+  bool result = true;
+
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 += path1;
+
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 += std::move(path1);
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 += path1;
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 += std::move(path1);
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    cm::string_view path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 += path1;
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    char path1[] = "aa/bb";
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 += path1;
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Concat(path1);
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Concat(path1);
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Concat(std::move(path1));
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    cm::string_view path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Concat(path1);
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+  {
+    char path1[] = "aa/bb";
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Concat(path1);
+    if (path2.String() != "cc/ddaa/bb") {
+      result = false;
+    }
+  }
+
+  checkResult(result);
+
+  return result;
+}
+
+bool testAppend()
+{
+  std::cout << "testAppend()";
+
+  bool result = true;
+
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 /= path1;
+
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 /= std::move(path1);
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 /= path1;
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 /= std::move(path1);
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cm::string_view path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 /= path1;
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    char path1[] = "aa/bb";
+    cmCMakePath path2{ "cc/dd" };
+
+    path2 /= path1;
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cmCMakePath path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Append(path1);
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Append(path1);
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    std::string path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Append(std::move(path1));
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    cm::string_view path1{ "aa/bb" };
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Append(path1);
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+  {
+    char path1[] = "aa/bb";
+    cmCMakePath path2{ "cc/dd" };
+
+    path2.Append(path1);
+    if (path2.String() != "cc/dd/aa/bb") {
+      result = false;
+    }
+  }
+
+  checkResult(result);
+
+  return result;
+}
+}
+
+int testCMakePath(int /*unused*/, char* /*unused*/[])
+{
+  int result = 0;
+
+  if (!testConstructors()) {
+    result = 1;
+  }
+  if (!testAssign()) {
+    result = 1;
+  }
+  if (!testConcat()) {
+    result = 1;
+  }
+  if (!testAppend()) {
+    result = 1;
+  }
+
+  return result;
+}
diff --git a/Tests/CMakeLib/testDebug.cxx b/Tests/CMakeLib/testDebug.cxx
new file mode 100644
index 0000000..657ef50
--- /dev/null
+++ b/Tests/CMakeLib/testDebug.cxx
@@ -0,0 +1,33 @@
+#include <iostream>
+#include <string>
+
+#include "cmDebugTools.h"
+
+#define check(expr, value)                                                    \
+  do {                                                                        \
+    if (expr != value) {                                                      \
+      std::cerr << "Failed to return " #value " for " #expr << std::endl;     \
+      retval = 1;                                                             \
+    }                                                                         \
+  } while (false)
+
+int testDebug(int argc, char** const /*argv*/)
+{
+  if (argc != 1) {
+    std::cout << "Invalid arguments.\n";
+    return -1;
+  }
+
+  int retval = 0;
+  check(CM_DBG(true), true);
+  check(CM_DBG(4), 4);
+  check(CM_DBG(1.), 1.);
+  check(CM_DBG('c'), 'c');
+  check(CM_DBG("literal string"), std::string("literal string"));
+
+  std::string str = "std string";
+  check(CM_DBG(str), "std string");
+  check(CM_DBG(str.empty()), false);
+
+  return retval;
+}
diff --git a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx
index 643661d..c0f2e9b 100644
--- a/Tests/CMakeLib/testDebuggerAdapterPipe.cxx
+++ b/Tests/CMakeLib/testDebuggerAdapterPipe.cxx
@@ -19,13 +19,15 @@
 #include <cm3p/cppdap/types.h>
 
 #include "cmDebuggerAdapter.h"
-#include "cmDebuggerPipeConnection.h"
 #include "cmDebuggerProtocol.h"
 #include "cmVersionConfig.h"
 
 #ifdef _WIN32
 #  include "cmCryptoHash.h"
+#  include "cmDebuggerWindowsPipeConnection.h"
 #  include "cmSystemTools.h"
+#else
+#  include "cmDebuggerPosixPipeConnection.h"
 #endif
 
 #include "testCommon.h"
@@ -128,7 +130,7 @@
 
   auto client2Debugger =
     std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe);
-  client2Debugger->Start();
+
   client2Debugger->WaitForConnection();
   client->bind(client2Debugger, client2Debugger);
 
diff --git a/Tests/CMakeLib/testDebuggerNamedPipe.cxx b/Tests/CMakeLib/testDebuggerNamedPipe.cxx
index ec91706..1ae3f64 100644
--- a/Tests/CMakeLib/testDebuggerNamedPipe.cxx
+++ b/Tests/CMakeLib/testDebuggerNamedPipe.cxx
@@ -16,7 +16,12 @@
 
 #include "cmsys/RegularExpression.hxx"
 
-#include "cmDebuggerPipeConnection.h"
+#ifdef _WIN32
+#  include "cmDebuggerWindowsPipeConnection.h"
+#else
+#  include "cmDebuggerPosixPipeConnection.h"
+#endif
+
 #include "cmSystemTools.h"
 
 #ifdef _WIN32
@@ -104,7 +109,7 @@
       attempt++;
       try {
         client = std::make_shared<cmDebugger::cmDebuggerPipeClient>(namedPipe);
-        client->Start();
+
         client->WaitForConnection();
         std::cout << "cmDebuggerPipeClient connected.\n";
         break;
diff --git a/Tests/CMakeLib/testEncoding.cxx b/Tests/CMakeLib/testEncoding.cxx
index 4936898..460d845 100644
--- a/Tests/CMakeLib/testEncoding.cxx
+++ b/Tests/CMakeLib/testEncoding.cxx
@@ -1,4 +1,5 @@
 #include <iostream>
+#include <iterator>
 #include <string>
 
 #include "cmsys/FStream.hxx"
diff --git a/Tests/CMakeLib/testGccDepfileReader.cxx b/Tests/CMakeLib/testGccDepfileReader.cxx
index d46e8f3..fb19c14 100644
--- a/Tests/CMakeLib/testGccDepfileReader.cxx
+++ b/Tests/CMakeLib/testGccDepfileReader.cxx
@@ -1,6 +1,5 @@
 #include <cstddef> // IWYU pragma: keep
 #include <iostream>
-#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
diff --git a/Tests/CMakeLib/testList.cxx b/Tests/CMakeLib/testList.cxx
index 6d6c218..8822806 100644
--- a/Tests/CMakeLib/testList.cxx
+++ b/Tests/CMakeLib/testList.cxx
@@ -4,7 +4,6 @@
 #include <iostream>
 #include <stdexcept>
 #include <string>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
diff --git a/Tests/CMakeLib/testOptional.cxx b/Tests/CMakeLib/testOptional.cxx
index 785f031..933ab70 100644
--- a/Tests/CMakeLib/testOptional.cxx
+++ b/Tests/CMakeLib/testOptional.cxx
@@ -1,5 +1,4 @@
 #include <iostream>
-#include <type_traits>
 #include <vector>
 
 #include <cm/optional>
diff --git a/Tests/CMakeLib/testString.cxx b/Tests/CMakeLib/testString.cxx
index af34a2f..3509266 100644
--- a/Tests/CMakeLib/testString.cxx
+++ b/Tests/CMakeLib/testString.cxx
@@ -1,14 +1,12 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
 
-#include <cstddef> // IWYU pragma: keep
 #include <cstring>
 #include <iostream>
 #include <iterator>
 #include <sstream>
 #include <stdexcept>
 #include <string>
-#include <type_traits>
 #include <utility>
 
 #include <cm/string_view>
diff --git a/Tests/CMakeLib/testUTF8.cxx b/Tests/CMakeLib/testUTF8.cxx
index fc0b539..180d29d 100644
--- a/Tests/CMakeLib/testUTF8.cxx
+++ b/Tests/CMakeLib/testUTF8.cxx
@@ -1,67 +1,57 @@
 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
    file Copyright.txt or https://cmake.org/licensing for details.  */
+#include <cm/string_view>
+
 #include <stdio.h>
 
 #include <cm_utf8.h>
 
-typedef char test_utf8_char[5];
+using test_utf8_char = const cm::string_view;
 
-static void test_utf8_char_print(test_utf8_char const c)
+static void byte_array_print(test_utf8_char s)
 {
-  unsigned char const* d = reinterpret_cast<unsigned char const*>(c);
-#ifndef __clang_analyzer__ // somehow thinks arguments are not initialized
-  printf("[0x%02X,0x%02X,0x%02X,0x%02X]", static_cast<int>(d[0]),
-         static_cast<int>(d[1]), static_cast<int>(d[2]),
-         static_cast<int>(d[3]));
-#endif
-}
-
-static void byte_array_print(char const* s)
-{
-  unsigned char const* d = reinterpret_cast<unsigned char const*>(s);
   bool started = false;
   printf("[");
-  for (; *d; ++d) {
+  for (char c : s) {
     if (started) {
       printf(",");
     }
     started = true;
-    printf("0x%02X", static_cast<int>(*d));
+    printf("0x%02X", static_cast<unsigned char>(c));
   }
   printf("]");
 }
 
 struct test_utf8_entry
 {
-  int n;
   test_utf8_char str;
   unsigned int chr;
 };
 
 static test_utf8_entry const good_entry[] = {
-  { 1, "\x20\x00\x00\x00", 0x0020 },   /* Space.  */
-  { 2, "\xC2\xA9\x00\x00", 0x00A9 },   /* Copyright.  */
-  { 3, "\xE2\x80\x98\x00", 0x2018 },   /* Open-single-quote.  */
-  { 3, "\xE2\x80\x99\x00", 0x2019 },   /* Close-single-quote.  */
-  { 4, "\xF0\xA3\x8E\xB4", 0x233B4 },  /* Example from RFC 3629.  */
-  { 3, "\xED\x80\x80\x00", 0xD000 },   /* Valid 0xED prefixed codepoint.  */
-  { 4, "\xF4\x8F\xBF\xBF", 0x10FFFF }, /* Highest valid RFC codepoint. */
-  { 0, { 0, 0, 0, 0, 0 }, 0 }
+  { "\x20", 0x0020 },               /* Space.  */
+  { "\xC2\xA9", 0x00A9 },           /* Copyright.  */
+  { "\xE2\x80\x98", 0x2018 },       /* Open-single-quote.  */
+  { "\xE2\x80\x99", 0x2019 },       /* Close-single-quote.  */
+  { "\xF0\xA3\x8E\xB4", 0x233B4 },  /* Example from RFC 3629.  */
+  { "\xED\x80\x80", 0xD000 },       /* Valid 0xED prefixed codepoint.  */
+  { "\xF4\x8F\xBF\xBF", 0x10FFFF }, /* Highest valid RFC codepoint. */
+  { {}, 0 }
 };
 
 static test_utf8_char const bad_chars[] = {
-  "\x80\x00\x00\x00", /* Leading continuation byte. */
-  "\xC0\x80\x00\x00", /* Overlong encoding. */
-  "\xC1\x80\x00\x00", /* Overlong encoding. */
-  "\xC2\x00\x00\x00", /* Missing continuation byte. */
-  "\xE0\x00\x00\x00", /* Missing continuation bytes. */
-  "\xE0\x80\x80\x00", /* Overlong encoding. */
+  "\x80",             /* Leading continuation byte. */
+  "\xC0\x80",         /* Overlong encoding. */
+  "\xC1\x80",         /* Overlong encoding. */
+  "\xC2",             /* Missing continuation byte. */
+  "\xE0",             /* Missing continuation bytes. */
+  "\xE0\x80\x80",     /* Overlong encoding. */
   "\xF0\x80\x80\x80", /* Overlong encoding. */
-  "\xED\xA0\x80\x00", /* UTF-16 surrogate half. */
-  "\xED\xBF\xBF\x00", /* UTF-16 surrogate half. */
+  "\xED\xA0\x80",     /* UTF-16 surrogate half. */
+  "\xED\xBF\xBF",     /* UTF-16 surrogate half. */
   "\xF4\x90\x80\x80", /* Lowest out-of-range codepoint. */
   "\xF5\x80\x80\x80", /* Prefix forces out-of-range codepoints. */
-  { 0, 0, 0, 0, 0 }
+  {}
 };
 
 static char const* good_strings[] = { "", "ASCII", "\xC2\xA9 Kitware", 0 };
@@ -71,49 +61,50 @@
   0
 };
 
-static void report_good(bool passed, test_utf8_char const c)
+static void report_good(bool passed, test_utf8_char c)
 {
   printf("%s: decoding good ", passed ? "pass" : "FAIL");
-  test_utf8_char_print(c);
-  printf(" (%s) ", c);
+  byte_array_print(c);
+  printf(" (%s) ", c.data());
 }
 
-static void report_bad(bool passed, test_utf8_char const c)
+static void report_bad(bool passed, test_utf8_char c)
 {
   printf("%s: decoding bad  ", passed ? "pass" : "FAIL");
-  test_utf8_char_print(c);
+  byte_array_print(c);
   printf(" ");
 }
 
-static bool decode_good(test_utf8_entry const entry)
+static bool decode_good(test_utf8_entry const& entry)
 {
+  const auto& s = entry.str;
   unsigned int uc;
   if (const char* e =
-        cm_utf8_decode_character(entry.str, entry.str + 4, &uc)) {
-    int used = static_cast<int>(e - entry.str);
+        cm_utf8_decode_character(s.data(), s.data() + s.size(), &uc)) {
+    int used = static_cast<int>(e - s.data());
     if (uc != entry.chr) {
-      report_good(false, entry.str);
+      report_good(false, s);
       printf("expected 0x%04X, got 0x%04X\n", entry.chr, uc);
       return false;
     }
-    if (used != entry.n) {
-      report_good(false, entry.str);
-      printf("had %d bytes, used %d\n", entry.n, used);
+    if (used != int(s.size())) {
+      report_good(false, s);
+      printf("had %d bytes, used %d\n", int(s.size()), used);
       return false;
     }
-    report_good(true, entry.str);
+    report_good(true, s);
     printf("got 0x%04X\n", uc);
     return true;
   }
-  report_good(false, entry.str);
+  report_good(false, s);
   printf("failed\n");
   return false;
 }
 
-static bool decode_bad(test_utf8_char const s)
+static bool decode_bad(test_utf8_char s)
 {
   unsigned int uc = 0xFFFFu;
-  const char* e = cm_utf8_decode_character(s, s + 4, &uc);
+  const char* e = cm_utf8_decode_character(s.data(), s.data() + s.size(), &uc);
   if (e) {
     report_bad(false, s);
     printf("expected failure, got 0x%04X\n", uc);
@@ -124,23 +115,23 @@
   return true;
 }
 
-static void report_valid(bool passed, char const* s)
+static void report_valid(bool passed, test_utf8_char s)
 {
   printf("%s: validity good ", passed ? "pass" : "FAIL");
   byte_array_print(s);
-  printf(" (%s) ", s);
+  printf(" (%s) ", s.data());
 }
 
-static void report_invalid(bool passed, char const* s)
+static void report_invalid(bool passed, test_utf8_char s)
 {
   printf("%s: validity bad  ", passed ? "pass" : "FAIL");
   byte_array_print(s);
   printf(" ");
 }
 
-static bool is_valid(const char* s)
+static bool is_valid(test_utf8_char s)
 {
-  bool valid = cm_utf8_is_valid(s) != 0;
+  bool valid = cm_utf8_is_valid(s.data()) != 0;
   if (!valid) {
     report_valid(false, s);
     printf("expected valid, reported as invalid\n");
@@ -151,9 +142,9 @@
   return true;
 }
 
-static bool is_invalid(const char* s)
+static bool is_invalid(test_utf8_char s)
 {
-  bool valid = cm_utf8_is_valid(s) != 0;
+  bool valid = cm_utf8_is_valid(s.data()) != 0;
   if (valid) {
     report_invalid(false, s);
     printf("expected invalid, reported as valid\n");
@@ -167,7 +158,7 @@
 int testUTF8(int /*unused*/, char* /*unused*/[])
 {
   int result = 0;
-  for (test_utf8_entry const* e = good_entry; e->n; ++e) {
+  for (test_utf8_entry const* e = good_entry; !e->str.empty(); ++e) {
     if (!decode_good(*e)) {
       result = 1;
     }
@@ -175,7 +166,7 @@
       result = 1;
     }
   }
-  for (test_utf8_char const* c = bad_chars; (*c)[0]; ++c) {
+  for (test_utf8_char* c = bad_chars; !(*c).empty(); ++c) {
     if (!decode_bad(*c)) {
       result = 1;
     }
diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx
index 7027689..aab084b 100644
--- a/Tests/CMakeLib/testUVProcessChain.cxx
+++ b/Tests/CMakeLib/testUVProcessChain.cxx
@@ -1,10 +1,10 @@
 #include <algorithm>
 #include <csignal>
+#include <cstdio>
 #include <functional>
 #include <iostream>
 #include <sstream>
 #include <string>
-#include <type_traits>
 #include <utility>
 #include <vector>
 
@@ -12,15 +12,17 @@
 
 #include <cm3p/uv.h>
 
+#include "cm_fileno.hxx"
+
 #include "cmGetPipes.h"
 #include "cmStringAlgorithms.h"
 #include "cmUVHandlePtr.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmUVStreambuf.h"
 
 struct ExpectedStatus
 {
-  bool Finished;
   bool MatchExitStatus;
   bool MatchTermSignal;
   cmUVProcessChain::Status Status;
@@ -28,38 +30,6 @@
   std::string ExceptionString;
 };
 
-static const std::vector<ExpectedStatus> status1 = {
-  { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-  { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-  { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-};
-
-static const std::vector<ExpectedStatus> status2 = {
-  { true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-  { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-  { false, false, false, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-};
-
-static const std::vector<ExpectedStatus> status3 = {
-  { true, true, true, { 0, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-  { true, true, true, { 1, 0 }, cmUVProcessChain::ExceptionCode::None, "" },
-#ifdef _WIN32
-  { true,
-    true,
-    true,
-    { STATUS_ACCESS_VIOLATION, 0 },
-    cmUVProcessChain::ExceptionCode::Fault,
-    "Access violation" },
-#else
-  { true,
-    false,
-    true,
-    { 0, SIGABRT },
-    cmUVProcessChain::ExceptionCode::Other,
-    "Subprocess aborted" },
-#endif
-};
-
 static const char* ExceptionCodeToString(cmUVProcessChain::ExceptionCode code)
 {
   switch (code) {
@@ -73,6 +43,8 @@
       return "Interrupt";
     case cmUVProcessChain::ExceptionCode::Numerical:
       return "Numerical";
+    case cmUVProcessChain::ExceptionCode::Spawn:
+      return "Spawn";
     case cmUVProcessChain::ExceptionCode::Other:
       return "Other";
     default:
@@ -83,9 +55,10 @@
 bool operator==(const cmUVProcessChain::Status* actual,
                 const ExpectedStatus& expected)
 {
-  if (!expected.Finished) {
-    return !actual;
-  } else if (!actual) {
+  if (expected.Status.SpawnResult != actual->SpawnResult) {
+    return false;
+  }
+  if (expected.Status.Finished != actual->Finished) {
     return false;
   }
   if (expected.MatchExitStatus &&
@@ -96,7 +69,7 @@
       expected.Status.TermSignal != actual->TermSignal) {
     return false;
   }
-  if (expected.Finished &&
+  if (expected.Status.Finished &&
       std::make_pair(expected.ExceptionCode, expected.ExceptionString) !=
         actual->GetException()) {
     return false;
@@ -150,39 +123,96 @@
 {
   std::cout << "Expected: " << std::endl;
   for (auto const& e : expected) {
-    if (e.Finished) {
-      std::cout << "  ExitStatus: "
-                << printExpected(e.MatchExitStatus, e.Status.ExitStatus)
-                << ", TermSignal: "
-                << printExpected(e.MatchTermSignal, e.Status.TermSignal)
-                << ", ExceptionCode: "
-                << printExpected(e.Finished,
-                                 ExceptionCodeToString(e.ExceptionCode))
-                << ", ExceptionString: \""
-                << printExpected(e.Finished, e.ExceptionString) << '"'
-                << std::endl;
-    } else {
-      std::cout << "  null" << std::endl;
-    }
+    std::cout << "  SpawnResult: " << e.Status.SpawnResult
+              << ", Finished: " << e.Status.Finished << ", ExitStatus: "
+              << printExpected(e.MatchExitStatus, e.Status.ExitStatus)
+              << ", TermSignal: "
+              << printExpected(e.MatchTermSignal, e.Status.TermSignal)
+              << ", ExceptionCode: "
+              << printExpected(e.Status.Finished,
+                               ExceptionCodeToString(e.ExceptionCode))
+              << ", ExceptionString: \""
+              << printExpected(e.Status.Finished, e.ExceptionString) << '"'
+              << std::endl;
   }
   std::cout << "Actual:" << std::endl;
   for (auto const& a : actual) {
-    if (a) {
-      auto exception = a->GetException();
-      std::cout << "  ExitStatus: " << a->ExitStatus
-                << ", TermSignal: " << a->TermSignal << ", ExceptionCode: "
-                << ExceptionCodeToString(exception.first)
-                << ", ExceptionString: \"" << exception.second << '"'
-                << std::endl;
-    } else {
-      std::cout << "  null" << std::endl;
-    }
+    auto exception = a->GetException();
+    std::cout << "  SpawnResult: " << a->SpawnResult
+              << ", Finished: " << a->Finished
+              << ", ExitStatus: " << a->ExitStatus
+              << ", TermSignal: " << a->TermSignal
+              << ", ExceptionCode: " << ExceptionCodeToString(exception.first)
+              << ", ExceptionString: \"" << exception.second << '"'
+              << std::endl;
   }
 }
 
 static bool checkExecution(cmUVProcessChainBuilder& builder,
                            std::unique_ptr<cmUVProcessChain>& chain)
 {
+  static const std::vector<ExpectedStatus> status1 = {
+    { false,
+      false,
+      { 0, false, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+    { false,
+      false,
+      { 0, false, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+    { false,
+      false,
+      { 0, false, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+  };
+
+  static const std::vector<ExpectedStatus> status2 = {
+    { true,
+      true,
+      { 0, true, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+    { false,
+      false,
+      { 0, false, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+    { false,
+      false,
+      { 0, false, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+  };
+
+  static const std::vector<ExpectedStatus> status3 = {
+    { true,
+      true,
+      { 0, true, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+    { true,
+      true,
+      { 0, true, 1, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+#ifdef _WIN32
+    { true,
+      true,
+      { 0, true, STATUS_ACCESS_VIOLATION, 0 },
+      cmUVProcessChain::ExceptionCode::Fault,
+      "Access violation" },
+#else
+    { false,
+      true,
+      { 0, true, 0, SIGABRT },
+      cmUVProcessChain::ExceptionCode::Other,
+      "Subprocess aborted" },
+#endif
+  };
+
   std::vector<const cmUVProcessChain::Status*> status;
 
   chain = cm::make_unique<cmUVProcessChain>(builder.Start());
@@ -201,7 +231,7 @@
     return false;
   }
 
-  if (chain->Wait(6000)) {
+  if (chain->Wait(9000)) {
     std::cout << "Wait() returned true, should be false" << std::endl;
     return false;
   }
@@ -273,16 +303,19 @@
     return false;
   }
 
-  if (!chain->OutputStream()) {
-    std::cout << "OutputStream() was null, expecting not null" << std::endl;
+  if (chain->OutputStream() < 0) {
+    std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
     return false;
   }
-  if (!chain->ErrorStream()) {
-    std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+  if (chain->ErrorStream() < 0) {
+    std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
     return false;
   }
 
-  if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) {
+  cmUVPipeIStream output(chain->GetLoop(), chain->OutputStream());
+  cmUVPipeIStream error(chain->GetLoop(), chain->ErrorStream());
+
+  if (!checkOutput(output, error)) {
     return false;
   }
 
@@ -302,12 +335,12 @@
     return false;
   }
 
-  if (!chain->OutputStream()) {
-    std::cout << "OutputStream() was null, expecting not null" << std::endl;
+  if (chain->OutputStream() < 0) {
+    std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
     return false;
   }
-  if (!chain->ErrorStream()) {
-    std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+  if (chain->ErrorStream() < 0) {
+    std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
     return false;
   }
   if (chain->OutputStream() != chain->ErrorStream()) {
@@ -316,7 +349,9 @@
     return false;
   }
 
-  std::string merged = getInput(*chain->OutputStream());
+  cmUVPipeIStream mergedStream(chain->GetLoop(), chain->OutputStream());
+
+  std::string merged = getInput(mergedStream);
   auto qemuErrorPos = merged.find("qemu:");
   if (qemuErrorPos != std::string::npos) {
     merged.resize(qemuErrorPos);
@@ -370,12 +405,12 @@
     return false;
   }
 
-  if (chain->OutputStream()) {
-    std::cout << "OutputStream() was not null, expecting null" << std::endl;
+  if (chain->OutputStream() >= 0) {
+    std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
     return false;
   }
-  if (chain->ErrorStream()) {
-    std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+  if (chain->ErrorStream() >= 0) {
+    std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
     return false;
   }
 
@@ -418,12 +453,12 @@
     return false;
   }
 
-  if (chain->OutputStream()) {
-    std::cout << "OutputStream() was not null, expecting null" << std::endl;
+  if (chain->OutputStream() >= 0) {
+    std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
     return false;
   }
-  if (chain->ErrorStream()) {
-    std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+  if (chain->ErrorStream() >= 0) {
+    std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
     return false;
   }
 
@@ -445,7 +480,8 @@
     return false;
   }
 
-  auto cwd = getInput(*chain.OutputStream());
+  cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
+  auto cwd = getInput(output);
   if (!cmHasLiteralSuffix(cwd, "/Tests/CMakeLib")) {
     std::cout << "Working directory was \"" << cwd
               << "\", expected to end in \"/Tests/CMakeLib\"" << std::endl;
@@ -471,7 +507,8 @@
     return false;
   }
 
-  auto cwd = getInput(*chain.OutputStream());
+  cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
+  auto cwd = getInput(output);
   if (!cmHasLiteralSuffix(cwd, "/Tests")) {
     std::cout << "Working directory was \"" << cwd
               << "\", expected to end in \"/Tests\"" << std::endl;
@@ -481,6 +518,156 @@
   return true;
 }
 
+bool testUVProcessChainSpawnFail(const char* helperCommand)
+{
+  static const std::vector<ExpectedStatus> status1 = {
+    { false,
+      false,
+      { 0, false, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+    { false,
+      false,
+      { UV_ENOENT, true, 0, 0 },
+      cmUVProcessChain::ExceptionCode::Spawn,
+      uv_strerror(UV_ENOENT) },
+#ifdef _WIN32
+    { true,
+      true,
+      { 0, true, STATUS_ACCESS_VIOLATION, 0 },
+      cmUVProcessChain::ExceptionCode::Fault,
+      "Access violation" },
+#else
+    { false,
+      true,
+      { 0, true, 0, SIGABRT },
+      cmUVProcessChain::ExceptionCode::Other,
+      "Subprocess aborted" },
+#endif
+  };
+
+  static const std::vector<ExpectedStatus> status2 = {
+#ifdef _WIN32
+    { true,
+      true,
+      { 0, true, 0, 0 },
+      cmUVProcessChain::ExceptionCode::None,
+      "" },
+#else
+    { false,
+      true,
+      { 0, true, 0, SIGPIPE },
+      cmUVProcessChain::ExceptionCode::Other,
+      "SIGPIPE" },
+#endif
+    { false,
+      false,
+      { UV_ENOENT, true, 0, 0 },
+      cmUVProcessChain::ExceptionCode::Spawn,
+      uv_strerror(UV_ENOENT) },
+#ifdef _WIN32
+    { true,
+      true,
+      { 0, true, STATUS_ACCESS_VIOLATION, 0 },
+      cmUVProcessChain::ExceptionCode::Fault,
+      "Access violation" },
+#else
+    { false,
+      true,
+      { 0, true, 0, SIGABRT },
+      cmUVProcessChain::ExceptionCode::Other,
+      "Subprocess aborted" },
+#endif
+  };
+
+  std::vector<const cmUVProcessChain::Status*> status;
+
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand({ helperCommand, "echo" })
+    .AddCommand({ "this_command_is_for_cmake_and_should_never_exist" })
+    .AddCommand({ helperCommand, "dedup" })
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+
+  auto chain = builder.Start();
+  if (!chain.Valid()) {
+    std::cout << "Valid() returned false, should be true" << std::endl;
+    return false;
+  }
+
+  // Some platforms, like Solaris 10, take a long time to report a trapped
+  // subprocess to the parent process (about 1.7 seconds in the case of
+  // Solaris 10.) Wait 3 seconds to give it enough time.
+  if (chain.Wait(3000)) {
+    std::cout << "Wait() did not time out" << std::endl;
+    return false;
+  }
+
+  status = chain.GetStatus();
+  if (!resultsMatch(status, status1)) {
+    std::cout << "GetStatus() did not produce expected output" << std::endl;
+    printResults(status, status1);
+    return false;
+  }
+
+  if (!chain.Wait()) {
+    std::cout << "Wait() timed out" << std::endl;
+    return false;
+  }
+
+  status = chain.GetStatus();
+  if (!resultsMatch(status, status2)) {
+    std::cout << "GetStatus() did not produce expected output" << std::endl;
+    printResults(status, status2);
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVProcessChainInputFile(const char* helperCommand)
+{
+  std::unique_ptr<FILE, int (*)(FILE*)> f(
+    fopen("testUVProcessChainInput.txt", "rb"), fclose);
+
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand({ helperCommand, "dedup" })
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
+                       cm_fileno(f.get()))
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+  auto chain = builder.Start();
+
+  if (!chain.Wait()) {
+    std::cout << "Wait() timed out" << std::endl;
+    return false;
+  }
+
+  cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream());
+  std::string output = getInput(stream);
+  if (output != "HELO WRD!") {
+    std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVProcessChainWait0(const char* helperCommand)
+{
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand({ helperCommand, "echo" });
+
+  auto chain = builder.Start();
+  if (!chain.Wait(0)) {
+    std::cout << "Wait(0) returned false, should be true" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
 int testUVProcessChain(int argc, char** const argv)
 {
   if (argc < 2) {
@@ -518,5 +705,20 @@
     return -1;
   }
 
+  if (!testUVProcessChainSpawnFail(argv[1])) {
+    std::cout << "While executing testUVProcessChainSpawnFail().\n";
+    return -1;
+  }
+
+  if (!testUVProcessChainInputFile(argv[1])) {
+    std::cout << "While executing testUVProcessChainInputFile().\n";
+    return -1;
+  }
+
+  if (!testUVProcessChainWait0(argv[1])) {
+    std::cout << "While executing testUVProcessChainWait0().\n";
+    return -1;
+  }
+
   return 0;
 }
diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx
index 99743e7..b53cac4 100644
--- a/Tests/CMakeLib/testUVProcessChainHelper.cxx
+++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx
@@ -7,10 +7,6 @@
 #include <string>
 #include <thread>
 
-#ifdef _WIN32
-#  include <windows.h>
-#endif
-
 #include "cmSystemTools.h"
 
 static std::string getStdin()
@@ -32,13 +28,13 @@
 
   std::string command = argv[1];
   if (command == "echo") {
-    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+    std::this_thread::sleep_for(std::chrono::milliseconds(6000));
     std::cout << "HELLO world!" << std::flush;
     std::cerr << "1" << std::flush;
     return 0;
   }
   if (command == "capitalize") {
-    std::this_thread::sleep_for(std::chrono::milliseconds(9000));
+    std::this_thread::sleep_for(std::chrono::milliseconds(12000));
     std::string input = getStdin();
     for (auto& c : input) {
       c = static_cast<char>(std::toupper(c));
diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx
index f9ed6af..af06a8e 100644
--- a/Tests/CMakeLib/testUVStreambuf.cxx
+++ b/Tests/CMakeLib/testUVStreambuf.cxx
@@ -3,11 +3,14 @@
 #include <string>
 #include <vector>
 
+#include <cmext/algorithm>
+
 #include <cm3p/uv.h>
 #include <stdint.h>
 
 #include "cmGetPipes.h"
 #include "cmUVHandlePtr.h"
+#include "cmUVStream.h"
 #include "cmUVStreambuf.h"
 
 #define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
@@ -437,6 +440,139 @@
   return success;
 }
 
+bool testUVPipeIStream()
+{
+  int pipe[] = { -1, -1 };
+  if (cmGetPipes(pipe) < 0) {
+    std::cout << "cmGetPipes() returned an error" << std::endl;
+    return false;
+  }
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+  cm::uv_pipe_ptr pipeSink;
+  pipeSink.init(*loop, 0);
+  uv_pipe_open(pipeSink, pipe[1]);
+
+  std::string str = "Hello world!\n";
+  uv_write_t writeReq;
+  uv_buf_t buf;
+  buf.base = &str.front();
+  buf.len = str.length();
+  uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  cmUVPipeIStream pin(*loop, pipe[0]);
+  std::string line;
+  std::getline(pin, line);
+  if (line != "Hello world!") {
+    std::cout << "Line was \"" << line << "\", should be \"Hello world!\""
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVStreamRead()
+{
+  int pipe[] = { -1, -1 };
+  if (cmGetPipes(pipe) < 0) {
+    std::cout << "cmGetPipes() returned an error" << std::endl;
+    return false;
+  }
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+  cm::uv_pipe_ptr pipeSink;
+  pipeSink.init(*loop, 0);
+  uv_pipe_open(pipeSink, pipe[1]);
+
+  std::string str = "Hello world!";
+  uv_write_t writeReq;
+  uv_buf_t buf;
+  buf.base = &str.front();
+  buf.len = str.length();
+  uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+  uv_run(loop, UV_RUN_DEFAULT);
+  pipeSink.reset();
+
+  cm::uv_pipe_ptr pipeSource;
+  pipeSource.init(*loop, 0);
+  uv_pipe_open(pipeSource, pipe[0]);
+
+  std::string output;
+  bool finished = false;
+  auto handle = cmUVStreamRead(
+    pipeSource,
+    [&output](std::vector<char> data) { cm::append(output, data); },
+    [&output, &finished]() {
+      if (output != "Hello world!") {
+        std::cout << "Output was \"" << output
+                  << "\", should be \"Hello world!\"" << std::endl;
+        return;
+      }
+      finished = true;
+    });
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  if (!finished) {
+    std::cout << "finished was not set" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVStreamReadLeak()
+{
+  int pipe[] = { -1, -1 };
+  if (cmGetPipes(pipe) < 0) {
+    std::cout << "cmGetPipes() returned an error" << std::endl;
+    return false;
+  }
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+  cm::uv_pipe_ptr pipeSink;
+  pipeSink.init(*loop, 0);
+  uv_pipe_open(pipeSink, pipe[1]);
+
+  std::string str = "Hello world!";
+  uv_write_t writeReq;
+  uv_buf_t buf;
+  buf.base = &str.front();
+  buf.len = str.length();
+  uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+  uv_run(loop, UV_RUN_DEFAULT);
+  pipeSink.reset();
+
+  cm::uv_pipe_ptr pipeSource;
+  pipeSource.init(*loop, 0);
+  uv_pipe_open(pipeSource, pipe[0]);
+
+  std::string output;
+  bool finished = false;
+  auto handle = cmUVStreamRead(
+    pipeSource,
+    [&output](std::vector<char> data) { cm::append(output, data); },
+    [&output, &finished]() {
+      if (output != "Hello world!") {
+        std::cout << "Output was \"" << output
+                  << "\", should be \"Hello world!\"" << std::endl;
+        return;
+      }
+      finished = true;
+    });
+
+  if (finished) {
+    std::cout << "finished was set" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
 int testUVStreambuf(int argc, char** const argv)
 {
   if (argc < 2) {
@@ -454,5 +590,20 @@
     return -1;
   }
 
+  if (!testUVPipeIStream()) {
+    std::cout << "While executing testUVPipeIStream().\n";
+    return -1;
+  }
+
+  if (!testUVStreamRead()) {
+    std::cout << "While executing testUVStreamRead().\n";
+    return -1;
+  }
+
+  if (!testUVStreamReadLeak()) {
+    std::cout << "While executing testUVStreamReadLeak().\n";
+    return -1;
+  }
+
   return 0;
 }
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 19dea8f..4c61eab 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -42,8 +42,8 @@
 # Suppress generator deprecation warnings in test suite.
 if(CMAKE_GENERATOR MATCHES "^Visual Studio 9 2008")
   set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS9} OFF)")
-elseif(CMAKE_GENERATOR MATCHES "^Visual Studio 11 2012")
-  set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS11} OFF)")
+elseif(CMAKE_GENERATOR MATCHES "^Visual Studio 12 2013")
+  set(TEST_WARN_VS_CODE "set(ENV{CMAKE_WARN_VS12} OFF)")
 else()
   set(TEST_WARN_VS_CODE "")
 endif()
@@ -117,8 +117,8 @@
   if(WIN32 OR CYGWIN)
     set(CMake_TEST_RESOURCES TRUE)
   endif()
-  # for borland and watcom there is no resource support
-  if(WATCOM OR BORLAND)
+  # For some Windows toolchains there is no resource support.
+  if(WATCOM OR BORLAND OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
     set(CMake_TEST_RESOURCES FALSE)
   endif()
 
@@ -142,6 +142,10 @@
       )
   endif()
 
+  if(_isMultiConfig)
+    set(test_options -C Debug)
+  endif()
+
   # Look for git to use for tests.
   find_program(GIT_EXECUTABLE NAMES git)
 
@@ -435,6 +439,10 @@
         set(CMAKE_SKIP_VSGNUFortran TRUE)
       endif()
     endif()
+    if(CMAKE_Fortran_COMPILER_ID STREQUAL LLVMFlang)
+      # No DLLEXPORT for 'Tests/VSGNUFortran/subdir/fortran/world.f'.
+      set(CMAKE_SKIP_VSGNUFortran TRUE)
+    endif()
     if(CMAKE_Fortran_COMPILER_ID STREQUAL IntelLLVM)
       message(STATUS "Skip VSGNUFortran for ifx until DLLEXPORT support is implemented")
       set(CMAKE_SKIP_VSGNUFortran TRUE)
@@ -1540,10 +1548,40 @@
     add_subdirectory(GoogleTest)
   endif()
 
-  if(CMake_TEST_FindPython OR CMake_TEST_FindPython_SABIModule OR CMake_TEST_FindPython_NumPy
-      OR CMake_TEST_FindPython_Conda OR CMake_TEST_FindPython_IronPython OR CMake_TEST_FindPython_PyPy)
-    if (CMake_TEST_FindPython AND CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin")
-      set(CMake_TEST_FindPython_SABIModule TRUE)
+  if(CMake_TEST_FindPython)
+    set(CMake_TEST_FindPython2 TRUE)
+    set(CMake_TEST_FindPython3 TRUE)
+  endif()
+  if(CMake_TEST_FindPython_SABIMOdule)
+    set(CMake_TEST_FindPython2_SABIModule TRUE)
+    set(CMake_TEST_FindPython3_SABIModule TRUE)
+  endif()
+  if(CMake_TEST_FindPython_NumPy)
+    set(CMake_TEST_FindPython2_NumPyy TRUE)
+    set(CMake_TEST_FindPython3_NumPy TRUE)
+  endif()
+  if(CMake_TEST_FindPython_Conda)
+    set(CMake_TEST_FindPython3_Conda TRUE)
+  endif()
+  if(CMake_TEST_FindPython_IronPython)
+    set(CMake_TEST_FindPython2_IronPython TRUE)
+    set(CMake_TEST_FindPython3_IronPython TRUE)
+  endif()
+  if(CMake_TEST_FindPython_PyPy)
+    set(CMake_TEST_FindPython2_PyPy TRUE)
+    set(CMake_TEST_FindPython3_PyPy TRUE)
+  endif()
+  if(CMake_TEST_FindPython2 OR CMake_TEST_FindPython3
+      OR CMake_TEST_FindPython2_SABIModule OR CMake_TEST_FindPython3_SABIModule
+      OR CMake_TEST_FindPython2_NumPy OR CMake_TEST_FindPython3_NumPy
+      OR CMake_TEST_FindPython3_Conda
+      OR CMake_TEST_FindPython2_IronPython OR CMake_TEST_FindPython3_IronPython
+      OR CMake_TEST_FindPython2_PyPy OR CMake_TEST_FindPython3_PyPy)
+    if (CMake_TEST_FindPython2 AND CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin")
+      set(CMake_TEST_FindPython2_SABIModule TRUE)
+    endif()
+    if (CMake_TEST_FindPython3 AND CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin")
+      set(CMake_TEST_FindPython3_SABIModule TRUE)
     endif()
     add_subdirectory(FindPython)
   endif()
@@ -2060,7 +2098,10 @@
       )
   endif()
 
-  if(MAKE_SUPPORTS_SPACES AND NOT CMAKE_GENERATOR STREQUAL "Xcode" AND NOT CMAKE_GENERATOR STREQUAL "Watcom WMake")
+  if(MAKE_SUPPORTS_SPACES
+      AND NOT CMAKE_GENERATOR STREQUAL "Xcode"
+      AND NOT CMAKE_GENERATOR STREQUAL "Watcom WMake"
+      )
     add_test(SubDirSpaces ${CMAKE_CTEST_COMMAND}
       --build-and-test
       "${CMake_SOURCE_DIR}/Tests/SubDirSpaces"
@@ -2113,7 +2154,10 @@
     endif()
 
     set(MSVCDebugInformationFormat_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
-    if(CMAKE_Fortran_COMPILER)
+    if(CMAKE_Fortran_COMPILER
+        # FIXME(LLVMFlang): It does not provide debug information format flags or predefines.
+        AND NOT CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang"
+        )
       list(APPEND MSVCDebugInformationFormat_BUILD_OPTIONS -DCMake_TEST_Fortran=1)
     endif()
     ADD_TEST_MACRO(MSVCDebugInformationFormat)
@@ -2279,11 +2323,6 @@
       list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSWinStorePhone/${name}")
     endmacro()
 
-    if(vs11 AND ws80)
-      add_test_VSWinStorePhone(vs11-store80-X86 "Visual Studio 11 2012" WindowsStore 8.0 Win32)
-      add_test_VSWinStorePhone(vs11-store80-ARM "Visual Studio 11 2012" WindowsStore 8.0 ARM)
-      add_test_VSWinStorePhone(vs11-store80-X64 "Visual Studio 11 2012" WindowsStore 8.0 x64)
-    endif()
     if(vs12 AND ws81)
       add_test_VSWinStorePhone(vs12-store81-X86 "Visual Studio 12 2013" WindowsStore 8.1 Win32)
       add_test_VSWinStorePhone(vs12-store81-ARM "Visual Studio 12 2013" WindowsStore 8.1 ARM)
@@ -2311,10 +2350,6 @@
       add_test_VSWinStorePhone(vs14-store10_0-ARM "Visual Studio 14 2015" WindowsStore 10.0 ARM)
       add_test_VSWinStorePhone(vs14-store10_0-X64 "Visual Studio 14 2015" WindowsStore 10.0 x64)
     endif()
-    if(vs11 AND wp80)
-      add_test_VSWinStorePhone(vs11-phone80-X86 "Visual Studio 11 2012" WindowsPhone 8.0 Win32)
-      add_test_VSWinStorePhone(vs11-phone80-ARM "Visual Studio 11 2012" WindowsPhone 8.0 ARM)
-    endif()
     if(vs12 AND wp81)
       add_test_VSWinStorePhone(vs12-phone81-X86 "Visual Studio 12 2013" WindowsPhone 8.1 Win32)
       add_test_VSWinStorePhone(vs12-phone81-ARM "Visual Studio 12 2013" WindowsPhone 8.1 ARM)
@@ -2341,10 +2376,6 @@
       endforeach()
     endmacro()
 
-    if(vs11)
-      add_test_VSWinCE(vs11-ce80-ARM "Visual Studio 11 2012" WindowsCE 8.0 ${wince_sdk})
-    endif()
-
     if(vs12)
       add_test_VSWinCE(vs12-ce80-ARM "Visual Studio 12 2013" WindowsCE 8.0 ${wince_sdk})
     endif()
@@ -2471,9 +2502,6 @@
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/VSAndroid/${name}")
   endmacro()
   if(tegra AND NOT "${CMake_SOURCE_DIR};${CMake_BINARY_DIR}" MATCHES " ")
-    if(vs11)
-      add_test_VSAndroid(vs11 "Visual Studio 11 2012" "Tegra-Android")
-    endif()
     if(vs12)
       add_test_VSAndroid(vs12 "Visual Studio 12 2013" "Tegra-Android")
     endif()
@@ -2751,29 +2779,6 @@
     list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/${CTestUpdateP4_DIR}")
   endif()
 
-  configure_file(
-    "${CMake_SOURCE_DIR}/Tests/CTestTestFailure/testNoBuild.cmake.in"
-    "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoBuild.cmake"
-    @ONLY ESCAPE_QUOTES)
-  add_test(CTestTestNoBuild ${CMAKE_CTEST_COMMAND}
-    -S "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoBuild.cmake" -V
-    --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testOut1.log"
-    )
-  set_tests_properties(CTestTestNoBuild PROPERTIES
-    FAIL_REGULAR_EXPRESSION "Error" WILL_FAIL true)
-
-  configure_file(
-    "${CMake_SOURCE_DIR}/Tests/CTestTestFailure/testNoExe.cmake.in"
-    "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoExe.cmake"
-    @ONLY ESCAPE_QUOTES)
-  add_test(CTestTestNoExe ${CMAKE_CTEST_COMMAND}
-    -S "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testNoExe.cmake" -V
-    --output-log "${CMake_BINARY_DIR}/Tests/CTestTestFailure/testOut2.log"
-    )
-  set_tests_properties(CTestTestNoExe PROPERTIES DEPENDS CTestTestNoBuild
-    PASS_REGULAR_EXPRESSION "Could not find executable"
-    FAIL_REGULAR_EXPRESSION "SegFault")
-
   if(NOT CMake_TEST_NO_NETWORK)
     configure_file(
       "${CMake_SOURCE_DIR}/Tests/CTestTestUpload/test.cmake.in"
@@ -3005,8 +3010,8 @@
     -S "${CMake_BINARY_DIR}/Tests/CTestTestCrash/test.cmake" -V
     --output-log "${CMake_BINARY_DIR}/Tests/CTestTestCrash/testOutput.log"
     )
-  # with watcom the SEGFAULT is not found, it just fails
-  if(CMAKE_GENERATOR MATCHES "Watcom WMake")
+  # With Watcom and OrangeC the SEGFAULT is not found, it just fails
+  if(CMAKE_GENERATOR MATCHES "Watcom WMake" OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
     set_tests_properties(CTestTestCrash PROPERTIES
       PASS_REGULAR_EXPRESSION "Failed")
   else()
@@ -3264,7 +3269,7 @@
     if(NOT CMake_TEST_EXTERNAL_CMAKE)
       configure_file("${CMake_SOURCE_DIR}/Tests/CTestTest2/test.cmake.in"
         "${CMake_BINARY_DIR}/Tests/CTestTest2/test.cmake" @ONLY ESCAPE_QUOTES)
-      add_test(CTestTest2 ${CMAKE_CTEST_COMMAND}
+      add_test(NAME CTestTest2 COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG>
         -S "${CMake_BINARY_DIR}/Tests/CTestTest2/test.cmake" -V
         --output-log "${CMake_BINARY_DIR}/Tests/CTestTest2/testOutput.log"
         )
diff --git a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt
index e6ed559..0f3bd4c 100644
--- a/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckCXXCompilerFlag/CMakeLists.txt
@@ -20,7 +20,7 @@
 endmacro()
 
 if(CMAKE_COMPILER_IS_GNUCXX)
-  exec_program(${CMAKE_C_COMPILER} ARGS --version OUTPUT_VARIABLE     _gcc_version_info)
+  execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE     _gcc_version_info)
   string (REGEX MATCH "[345]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
   # gcc on mac just reports: "gcc (GCC) 3.3 20030304 ..." without the
   # patch level, handle this here:
@@ -30,12 +30,12 @@
 endif()
 
 if(CMAKE_CXX_COMPILER_ID MATCHES Clang)
-  exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE     _clang_version_info)
+  execute_process(COMMAND ${CMAKE_CXX_COMPILER} --version OUTPUT_VARIABLE     _clang_version_info)
   string (REGEX REPLACE ".*version ([0-9]\\.[0-9]).*" "\\1" _clang_version "${_clang_version_info}")
 endif()
 
 if(CMAKE_CXX_COMPILER_ID MATCHES Intel)
-  exec_program(${CMAKE_CXX_COMPILER} ARGS -V OUTPUT_VARIABLE     _intel_version_info)
+  execute_process(COMMAND ${CMAKE_CXX_COMPILER} -V OUTPUT_VARIABLE     _intel_version_info)
   string (REGEX REPLACE ".*Version ([0-9]+(\\.[0-9]+)+).*" "\\1" _intel_version "${_intel_version_info}")
 endif()
 
diff --git a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
index 0c76158..f005843 100644
--- a/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
+++ b/Tests/CMakeOnly/CheckLanguage/CMakeLists.txt
@@ -13,7 +13,7 @@
 unset(expect_Fortran)
 set(expect_NoSuchLanguage 0)
 
-set(LANGUAGES C CXX Fortran CUDA NoSuchLanguage)
+set(LANGUAGES C CXX Fortran CUDA HIP NoSuchLanguage)
 if(APPLE)
   list(APPEND LANGUAGES OBJC OBJCXX)
 endif()
diff --git a/Tests/CTestTestFailure/CMakeLists.txt b/Tests/CTestTestFailure/CMakeLists.txt
deleted file mode 100644
index b6c1e7a..0000000
--- a/Tests/CTestTestFailure/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-cmake_minimum_required (VERSION 3.5)
-project(CTestTestFailure)
-include(CTest)
-
-add_executable (NoBuild badCode.cxx)
-target_link_libraries (NoBuild ${EXTRA_LIBS})
-
-add_test (TestNoExe NoBuild)
diff --git a/Tests/CTestTestFailure/CTestConfig.cmake b/Tests/CTestTestFailure/CTestConfig.cmake
deleted file mode 100644
index 5bc1e9e..0000000
--- a/Tests/CTestTestFailure/CTestConfig.cmake
+++ /dev/null
@@ -1,4 +0,0 @@
-set (CTEST_NIGHTLY_START_TIME "21:00:00 EDT")
-set(CTEST_DROP_METHOD "http")
-set(CTEST_DROP_SITE "open.cdash.org")
-set(CTEST_DROP_LOCATION "/submit.php?project=PublicDashboard")
diff --git a/Tests/CTestTestFailure/testNoBuild.cmake.in b/Tests/CTestTestFailure/testNoBuild.cmake.in
deleted file mode 100644
index 505916e..0000000
--- a/Tests/CTestTestFailure/testNoBuild.cmake.in
+++ /dev/null
@@ -1,23 +0,0 @@
-cmake_minimum_required(VERSION 3.5)
-
-# Settings:
-set(CTEST_DASHBOARD_ROOT                "@CMake_BINARY_DIR@/Tests/CTestTest")
-set(CTEST_SITE                          "@SITE@")
-set(CTEST_BUILD_NAME                    "CTestTest-@BUILDNAME@-NoBuild")
-
-set(CTEST_SOURCE_DIRECTORY              "@CMake_SOURCE_DIR@/Tests/CTestTestFailure")
-set(CTEST_BINARY_DIRECTORY              "@CMake_BINARY_DIR@/Tests/CTestTestFailure")
-set(CTEST_CVS_COMMAND                   "@CVSCOMMAND@")
-set(CTEST_CMAKE_GENERATOR               "@CMAKE_GENERATOR@")
-set(CTEST_CMAKE_GENERATOR_PLATFORM      "@CMAKE_GENERATOR_PLATFORM@")
-set(CTEST_CMAKE_GENERATOR_TOOLSET       "@CMAKE_GENERATOR_TOOLSET@")
-set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
-set(CTEST_COVERAGE_COMMAND              "@COVERAGE_COMMAND@")
-set(CTEST_NOTES_FILES                   "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")
-
-#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY})
-
-CTEST_START(Experimental)
-#CTEST_UPDATE(SOURCE "${CTEST_SOURCE_DIRECTORY}" RETURN_VALUE res)
-CTEST_CONFIGURE(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
-CTEST_BUILD(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
diff --git a/Tests/CTestTestFailure/testNoExe.cmake.in b/Tests/CTestTestFailure/testNoExe.cmake.in
deleted file mode 100644
index e3d7742..0000000
--- a/Tests/CTestTestFailure/testNoExe.cmake.in
+++ /dev/null
@@ -1,21 +0,0 @@
-cmake_minimum_required(VERSION 3.5)
-
-# Settings:
-set(CTEST_DASHBOARD_ROOT                "@CMake_BINARY_DIR@/Tests/CTestTest")
-set(CTEST_SITE                          "@SITE@")
-set(CTEST_BUILD_NAME                    "CTestTest-@BUILDNAME@-NoExe")
-
-set(CTEST_SOURCE_DIRECTORY              "@CMake_SOURCE_DIR@/Tests/CTestTestFailure")
-set(CTEST_BINARY_DIRECTORY              "@CMake_BINARY_DIR@/Tests/CTestTestFailure")
-set(CTEST_CVS_COMMAND                   "@CVSCOMMAND@")
-set(CTEST_CMAKE_GENERATOR               "@CMAKE_GENERATOR@")
-set(CTEST_CMAKE_GENERATOR_PLATFORM      "@CMAKE_GENERATOR_PLATFORM@")
-set(CTEST_CMAKE_GENERATOR_TOOLSET       "@CMAKE_GENERATOR_TOOLSET@")
-set(CTEST_BUILD_CONFIGURATION           "$ENV{CMAKE_CONFIG_TYPE}")
-set(CTEST_COVERAGE_COMMAND              "@COVERAGE_COMMAND@")
-set(CTEST_NOTES_FILES                   "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")
-
-#CTEST_EMPTY_BINARY_DIRECTORY(${CTEST_BINARY_DIRECTORY})
-
-CTEST_START(Experimental)
-CTEST_TEST(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
diff --git a/Tests/CommandLineTest/CMakeLists.txt b/Tests/CommandLineTest/CMakeLists.txt
index 96aac10..a44fb39 100644
--- a/Tests/CommandLineTest/CMakeLists.txt
+++ b/Tests/CommandLineTest/CMakeLists.txt
@@ -2,58 +2,58 @@
 project(CommandLineTest)
 
 get_filename_component(CMAKE_BIN_DIR ${CMAKE_COMMAND} PATH)
-macro(EXEC_CMAKE_COMMAND CMAKE_ARGS)
-  exec_program("${CMAKE_COMMAND}" ARGS "${CMAKE_ARGS}" RETURN_VALUE RET)
+macro(EXEC_CMAKE_COMMAND)
+  execute_process(COMMAND "${CMAKE_COMMAND}" ${ARGN} RESULT_VARIABLE RET)
   if(${RET})
-    message(SEND_ERROR "CMake command failed with arguments \"${CMAKE_ARGS}\"")
+    message(SEND_ERROR "CMake command failed with arguments \"${ARGN}\"")
   endif()
 endmacro()
 
-EXEC_CMAKE_COMMAND("-E chdir \"${CMAKE_CURRENT_SOURCE_DIR}\" \"${CMAKE_COMMAND}\" -E echo \"Hello World\"")
-EXEC_CMAKE_COMMAND("-E time \"${CMAKE_COMMAND} -N -L ${CommandLineTest_SOURCE_DIR}\"")
-EXEC_CMAKE_COMMAND("-E time \"${CMAKE_COMMAND} -N -LA ${CommandLineTest_SOURCE_DIR}\"")
-EXEC_CMAKE_COMMAND("-E time \"${CMAKE_COMMAND} -N -LH ${CommandLineTest_SOURCE_DIR}\"")
-EXEC_CMAKE_COMMAND("-E time \"${CMAKE_COMMAND} -N -LAH ${CommandLineTest_SOURCE_DIR}\"")
-EXEC_CMAKE_COMMAND("--help")
-EXEC_CMAKE_COMMAND("--help-command-list")
-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("--version \"${CMAKE_CURRENT_BINARY_DIR}/version.txt\"")
+EXEC_CMAKE_COMMAND(-E chdir "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_COMMAND}" -E echo "Hello World")
+EXEC_CMAKE_COMMAND(-E time "${CMAKE_COMMAND} -N -L ${CommandLineTest_SOURCE_DIR}")
+EXEC_CMAKE_COMMAND(-E time "${CMAKE_COMMAND} -N -LA ${CommandLineTest_SOURCE_DIR}")
+EXEC_CMAKE_COMMAND(-E time "${CMAKE_COMMAND} -N -LH ${CommandLineTest_SOURCE_DIR}")
+EXEC_CMAKE_COMMAND(-E time "${CMAKE_COMMAND} -N -LAH ${CommandLineTest_SOURCE_DIR}")
+EXEC_CMAKE_COMMAND(--help)
+EXEC_CMAKE_COMMAND(--help-command-list)
+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(--version "${CMAKE_CURRENT_BINARY_DIR}/version.txt")
 
 add_executable(CommandLineTest CommandLineTest.cxx)
 
 get_filename_component(CMAKE_COMMAND_PATH "${CMAKE_COMMAND}" PATH)
 set(CTEST_COMMAND "${CMAKE_COMMAND_PATH}/ctest")
-macro(EXEC_CTEST_COMMAND CMAKE_ARGS)
-  exec_program("${CTEST_COMMAND}" ARGS "${CMAKE_ARGS}" RETURN_VALUE RET)
+macro(EXEC_CTEST_COMMAND)
+  execute_process(COMMAND "${CTEST_COMMAND}" ${ARGN} RESULT_VARIABLE RET)
   if(${RET})
     message(SEND_ERROR "CTest command failed with arguments \"${CMAKE_ARGS}\"")
   endif()
 endmacro()
-macro(EXEC_CTEST_COMMAND_WITH_DIR DIR CMAKE_ARGS)
-  exec_program("${CTEST_COMMAND}" "${DIR}" ARGS "${CMAKE_ARGS}" RETURN_VALUE RET)
+macro(EXEC_CTEST_COMMAND_WITH_DIR DIR)
+  execute_process(COMMAND "${CTEST_COMMAND}" ${ARGN} WORKING_DIRECTORY "${DIR}" RESULT_VARIABLE RET)
   if(${RET})
     message(SEND_ERROR "CTest command failed with arguments \"${CMAKE_ARGS}\"")
   endif()
 endmacro()
 
-EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  "-N")
-EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  "-R complex -N")
-EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  "-E Simple -N")
-EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  "-E Simple -N")
-EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  "-N -I -10")
-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("--version")
+EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -N)
+EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -R complex -N)
+EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -E Simple -N)
+EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -E Simple -N)
+EXEC_CTEST_COMMAND_WITH_DIR("${CMAKE_CURRENT_BINARY_DIR}/../.."  -N -I -10)
+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(--version)
 
 if(THIS_SHOULD_BE_SET)
   message(STATUS "***************************")
diff --git a/Tests/CompileFeatures/CMakeLists.txt b/Tests/CompileFeatures/CMakeLists.txt
index 17f4408..d9bb447 100644
--- a/Tests/CompileFeatures/CMakeLists.txt
+++ b/Tests/CompileFeatures/CMakeLists.txt
@@ -18,7 +18,7 @@
   endif()
 endmacro()
 
-if(NOT CMAKE_C_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$")
+if(NOT CMAKE_C_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$")
   get_property(c_features GLOBAL PROPERTY CMAKE_C_KNOWN_FEATURES)
   list(FILTER c_features EXCLUDE REGEX "^c_std_[0-9][0-9]")
   foreach(feature ${c_features})
@@ -26,7 +26,7 @@
   endforeach()
 endif()
 
-if(NOT CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$")
+if(NOT CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$")
   get_property(cxx_features GLOBAL PROPERTY CMAKE_CXX_KNOWN_FEATURES)
   list(FILTER cxx_features EXCLUDE REGEX "^cxx_std_[0-9][0-9]")
   foreach(feature ${cxx_features})
diff --git a/Tests/Complex/CMakeLists.txt b/Tests/Complex/CMakeLists.txt
index 9493a2f..fcc2471 100644
--- a/Tests/Complex/CMakeLists.txt
+++ b/Tests/Complex/CMakeLists.txt
@@ -244,6 +244,12 @@
   ${Complex_SOURCE_DIR}/Library/dummy
   ${Complex_BINARY_DIR}/Library/dummylib.lib
   COPYONLY)
+if(CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  configure_file(
+    ${Complex_SOURCE_DIR}/Library/dummy
+    ${Complex_BINARY_DIR}/Library/dummylib.l
+    COPYONLY)
+endif()
 foreach (ext ${CMAKE_SHLIB_SUFFIX};.so;.a;.sl
          ${CMAKE_SHARED_LIBRARY_SUFFIX}.2
          ${CMAKE_STATIC_LIBRARY_SUFFIX}.2)
@@ -327,12 +333,12 @@
       ${Complex_SOURCE_DIR}/Library/dummy
       "${dir}/${file}"
       COPYONLY)
-    exec_program(${CMAKE_COMMAND} ARGS "-E write_regv \"${hkey}\" \"${dir}\"")
+    execute_process(COMMAND ${CMAKE_COMMAND} -E write_regv "${hkey}" "${dir}")
     find_path(REGISTRY_TEST_PATH
       ${file}
       "[${hkey}]" DOC "Registry_Test_Path")
-    exec_program(${CMAKE_COMMAND} ARGS "-E delete_regv \"${hkey}\"")
-    exec_program(${CMAKE_COMMAND} ARGS "-E rm -f \"${dir}/${file}\"")
+    execute_process(COMMAND ${CMAKE_COMMAND} -E delete_regv "${hkey}")
+    execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f "${dir}/${file}")
   endif ()
 endif ()
 
diff --git a/Tests/Complex/Executable/CMakeLists.txt b/Tests/Complex/Executable/CMakeLists.txt
index 2a79629..c8668b9 100644
--- a/Tests/Complex/Executable/CMakeLists.txt
+++ b/Tests/Complex/Executable/CMakeLists.txt
@@ -45,12 +45,8 @@
 
 if (UNIX)
   target_link_libraries(complex ${CMAKE_DL_LIBS})
-else()
-  if (NOT BORLAND)
-    if(NOT MINGW)
-      target_link_libraries(complex rpcrt4.lib)
-    endif()
-  endif()
+elseif(NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  target_link_libraries(complex rpcrt4.lib)
 endif ()
 
 # Test linking to static lib when a shared lib has the same name.
diff --git a/Tests/Complex/Library/CMakeLists.txt b/Tests/Complex/Library/CMakeLists.txt
index df874ef..d216486 100644
--- a/Tests/Complex/Library/CMakeLists.txt
+++ b/Tests/Complex/Library/CMakeLists.txt
@@ -23,19 +23,9 @@
 remove(LibrarySources create_file.cxx GENERATED nonexisting_file)
 add_library(CMakeTestLibrary ${LibrarySources})
 
-if(WIN32)
-  if(NOT CYGWIN)
-    if(NOT BORLAND)
-      if(NOT MINGW)
-        target_link_libraries(CMakeTestLibrary
-          debug
-          user32.lib)
-        target_link_libraries(CMakeTestLibrary
-          optimized
-          kernel32.lib)
-      endif()
-    endif()
-  endif()
+if(WIN32 AND NOT CYGWIN AND NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  target_link_libraries(CMakeTestLibrary debug user32.lib)
+  target_link_libraries(CMakeTestLibrary optimized kernel32.lib)
 endif()
 
 #
diff --git a/Tests/ComplexOneConfig/CMakeLists.txt b/Tests/ComplexOneConfig/CMakeLists.txt
index e4fdc68..e4ae6ba 100644
--- a/Tests/ComplexOneConfig/CMakeLists.txt
+++ b/Tests/ComplexOneConfig/CMakeLists.txt
@@ -231,6 +231,12 @@
   ${Complex_SOURCE_DIR}/Library/dummy
   ${Complex_BINARY_DIR}/Library/dummylib.lib
   COPYONLY)
+if(CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  configure_file(
+    ${Complex_SOURCE_DIR}/Library/dummy
+    ${Complex_BINARY_DIR}/Library/dummylib.l
+    COPYONLY)
+endif()
 foreach (ext ${CMAKE_SHLIB_SUFFIX};.so;.a;.sl)
   configure_file(
     ${Complex_SOURCE_DIR}/Library/dummy
@@ -284,12 +290,12 @@
       ${Complex_SOURCE_DIR}/Library/dummy
       "${dir}/${file}"
       COPYONLY)
-    exec_program(${CMAKE_COMMAND} ARGS "-E write_regv \"${hkey}\" \"${dir}\"")
+    execute_process(COMMAND ${CMAKE_COMMAND} -E write_regv "${hkey}" "${dir}")
     find_path(REGISTRY_TEST_PATH
       ${file}
       "[${hkey}]" DOC "Registry_Test_Path")
-    exec_program(${CMAKE_COMMAND} ARGS "-E delete_regv \"${hkey}\"")
-    exec_program(${CMAKE_COMMAND} ARGS "-E rm -f \"${dir}/${file}\"")
+    execute_process(COMMAND ${CMAKE_COMMAND} -E delete_regv "${hkey}")
+    execute_process(COMMAND ${CMAKE_COMMAND} -E rm -f "${dir}/${file}")
   endif ()
 endif ()
 
diff --git a/Tests/ComplexOneConfig/Executable/CMakeLists.txt b/Tests/ComplexOneConfig/Executable/CMakeLists.txt
index f935aed..a8ab17b 100644
--- a/Tests/ComplexOneConfig/Executable/CMakeLists.txt
+++ b/Tests/ComplexOneConfig/Executable/CMakeLists.txt
@@ -45,12 +45,8 @@
 
 if (UNIX)
   target_link_libraries(complex ${CMAKE_DL_LIBS})
-else()
-  if (NOT BORLAND)
-    if(NOT MINGW)
-      target_link_libraries(complex rpcrt4.lib)
-    endif()
-  endif()
+elseif(NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  target_link_libraries(complex rpcrt4.lib)
 endif ()
 
 # Test linking to static lib when a shared lib has the same name.
diff --git a/Tests/ComplexOneConfig/Library/CMakeLists.txt b/Tests/ComplexOneConfig/Library/CMakeLists.txt
index df874ef..d216486 100644
--- a/Tests/ComplexOneConfig/Library/CMakeLists.txt
+++ b/Tests/ComplexOneConfig/Library/CMakeLists.txt
@@ -23,19 +23,9 @@
 remove(LibrarySources create_file.cxx GENERATED nonexisting_file)
 add_library(CMakeTestLibrary ${LibrarySources})
 
-if(WIN32)
-  if(NOT CYGWIN)
-    if(NOT BORLAND)
-      if(NOT MINGW)
-        target_link_libraries(CMakeTestLibrary
-          debug
-          user32.lib)
-        target_link_libraries(CMakeTestLibrary
-          optimized
-          kernel32.lib)
-      endif()
-    endif()
-  endif()
+if(WIN32 AND NOT CYGWIN AND NOT BORLAND AND NOT MINGW AND NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  target_link_libraries(CMakeTestLibrary debug user32.lib)
+  target_link_libraries(CMakeTestLibrary optimized kernel32.lib)
 endif()
 
 #
diff --git a/Tests/Cuda/CMakeLists.txt b/Tests/Cuda/CMakeLists.txt
index 0041b07..c737bcc 100644
--- a/Tests/Cuda/CMakeLists.txt
+++ b/Tests/Cuda/CMakeLists.txt
@@ -13,6 +13,7 @@
 add_cuda_test_macro(Cuda.MixedStandardLevels5 MixedStandardLevels5)
 add_cuda_test_macro(Cuda.NotEnabled CudaNotEnabled)
 add_cuda_test_macro(Cuda.SeparableCompCXXOnly SeparableCompCXXOnly)
+add_cuda_test_macro(Cuda.StubRPATH StubRPATH)
 add_cuda_test_macro(Cuda.Toolkit Toolkit)
 add_cuda_test_macro(Cuda.IncludePathNoToolkit IncludePathNoToolkit)
 add_cuda_test_macro(Cuda.SharedRuntimePlusToolkit SharedRuntimePlusToolkit)
diff --git a/Tests/Cuda/StubRPATH/CMakeLists.txt b/Tests/Cuda/StubRPATH/CMakeLists.txt
new file mode 100644
index 0000000..93643c5
--- /dev/null
+++ b/Tests/Cuda/StubRPATH/CMakeLists.txt
@@ -0,0 +1,21 @@
+cmake_minimum_required(VERSION 3.18)
+project(StubRPATH CXX)
+
+#Verify that linking to a stub library doesn't cause an `-rpath` entry
+
+# Needed for `CUDAToolkit_LIBRARY_SEARCH_DIRS`
+find_package(CUDAToolkit REQUIRED)
+
+find_library(CUDA_DRIVER_STUB_LIBRARY
+  NAMES cuda
+  HINTS ${CUDAToolkit_LIBRARY_SEARCH_DIRS}
+        ENV CUDA_PATH
+  PATH_SUFFIXES lib64/stubs lib/x64/stubs lib/stubs stubs
+)
+add_library(imported_stub IMPORTED SHARED)
+set_target_properties(imported_stub PROPERTIES IMPORTED_IMPLIB "${CUDA_DRIVER_STUB_LIBRARY}")
+set_target_properties(imported_stub PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CUDAToolkit_INCLUDE_DIRS}")
+
+set(CMAKE_CXX_STANDARD 11)
+add_executable(StubRPATH main.cxx)
+target_link_libraries(StubRPATH PRIVATE imported_stub)
diff --git a/Tests/Cuda/StubRPATH/main.cxx b/Tests/Cuda/StubRPATH/main.cxx
new file mode 100644
index 0000000..877856e
--- /dev/null
+++ b/Tests/Cuda/StubRPATH/main.cxx
@@ -0,0 +1,17 @@
+
+#include <iostream>
+
+#include <cuda.h>
+
+int main(int argc, char** argv)
+{
+  int nDevices = 0;
+  cuInit(0);
+  auto err = cuDeviceGetCount(&nDevices);
+  if (err != CUDA_SUCCESS) {
+    std::cerr << "Failed to retrieve the number of CUDA enabled devices "
+              << err << std::endl;
+    return 1;
+  }
+  return 0;
+}
diff --git a/Tests/Cuda/Toolkit/CMakeLists.txt b/Tests/Cuda/Toolkit/CMakeLists.txt
index 8432b71..c2989f0 100644
--- a/Tests/Cuda/Toolkit/CMakeLists.txt
+++ b/Tests/Cuda/Toolkit/CMakeLists.txt
@@ -26,9 +26,11 @@
   CUDAToolkit_LIBRARY_ROOT
   )
 foreach (cuda_loc_var IN LISTS should_exist)
-  if(NOT EXISTS "${${cuda_loc_var}}")
-    message(FATAL_ERROR "${cuda_loc_var} variable is expected to be set to valid path")
-  endif()
+  foreach (entry IN LISTS ${cuda_loc_var})
+    if(NOT EXISTS "${entry}")
+      message(FATAL_ERROR "${cuda_loc_var} variable is expected to be set to valid path")
+    endif()
+  endforeach()
 endforeach()
 
 
diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt
index aa25c4c..b7ce5a1 100644
--- a/Tests/CudaOnly/CMakeLists.txt
+++ b/Tests/CudaOnly/CMakeLists.txt
@@ -20,6 +20,7 @@
 add_cuda_test_macro(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols)
 add_cuda_test_macro(CudaOnly.SeparateCompilation main/CudaOnlySeparateCompilation)
 add_cuda_test_macro(CudaOnly.SeparateCompilationPTX CudaOnlySeparateCompilationPTX)
+add_cuda_test_macro(CudaOnly.SeparateCompilationTargetObjects CudaOnlySeparateCompilationTargetObjects)
 
 if(CMake_TEST_CUDA AND NOT CMake_TEST_CUDA STREQUAL "Clang")
   # Clang doesn't have flags for selecting the runtime.
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilationTargetObjects/CMakeLists.txt
new file mode 100644
index 0000000..7dbc0d5
--- /dev/null
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.25.5)
+
+project(SeparateCompilationObjects LANGUAGES CUDA)
+
+add_library(foo OBJECT foo.cu)
+set_target_properties(foo PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
+
+add_library(bar OBJECT bar.cu)
+set_target_properties(bar PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
+
+add_executable(CudaOnlySeparateCompilationTargetObjects main.cu)
+set_target_properties(CudaOnlySeparateCompilationTargetObjects PROPERTIES CUDA_SEPARABLE_COMPILATION ON)
+target_link_libraries(CudaOnlySeparateCompilationTargetObjects PRIVATE $<TARGET_OBJECTS:foo> bar)
+
+if(APPLE)
+  # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime.
+  set_property(TARGET CudaOnlySeparateCompilationTargetObjects PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES})
+endif()
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu
new file mode 100644
index 0000000..234586f
--- /dev/null
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/bar.cu
@@ -0,0 +1,18 @@
+
+#include <iostream>
+
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT __attribute__((__visibility__("default")))
+#endif
+
+__global__ void b1()
+{
+}
+
+EXPORT int bar()
+{
+  b1<<<1, 1>>>();
+  return 0;
+}
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu
new file mode 100644
index 0000000..75c04af
--- /dev/null
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/foo.cu
@@ -0,0 +1,18 @@
+
+#include <iostream>
+
+#ifdef _WIN32
+#  define EXPORT __declspec(dllexport)
+#else
+#  define EXPORT __attribute__((__visibility__("default")))
+#endif
+
+__global__ void k1()
+{
+}
+
+EXPORT int foo()
+{
+  k1<<<1, 1>>>();
+  return 0;
+}
diff --git a/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu b/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu
new file mode 100644
index 0000000..78b10b1
--- /dev/null
+++ b/Tests/CudaOnly/SeparateCompilationTargetObjects/main.cu
@@ -0,0 +1,16 @@
+// main.cu
+#include <iostream>
+
+#ifdef _WIN32
+#  define IMPORT __declspec(dllimport)
+#else
+#  define IMPORT
+#endif
+
+IMPORT int foo();
+IMPORT int bar();
+
+int main(int argc, char**)
+{
+  return foo() && bar();
+}
diff --git a/Tests/CudaOnly/Toolkit/CMakeLists.txt b/Tests/CudaOnly/Toolkit/CMakeLists.txt
index e0801a3..506fc9f 100644
--- a/Tests/CudaOnly/Toolkit/CMakeLists.txt
+++ b/Tests/CudaOnly/Toolkit/CMakeLists.txt
@@ -1,5 +1,5 @@
 cmake_minimum_required(VERSION 3.15)
-project(CudaOnlyToolkit CUDA)
+project(CudaOnlyToolkit LANGUAGES CUDA)
 find_package(CUDAToolkit REQUIRED)
 
 if(NOT DEFINED CUDAToolkit_VERSION)
@@ -13,8 +13,22 @@
 message(STATUS "CUDAToolkit_BIN_DIR: ${CUDAToolkit_BIN_DIR}")
 message(STATUS "CUDAToolkit_INCLUDE_DIRS: ${CUDAToolkit_INCLUDE_DIRS}")
 message(STATUS "CUDAToolkit_LIBRARY_DIR: ${CUDAToolkit_LIBRARY_DIR}")
+message(STATUS "CUDAToolkit_LIBRARY_ROOT: ${CUDAToolkit_LIBRARY_ROOT}")
 message(STATUS "CUDAToolkit_NVCC_EXECUTABLE ${CUDAToolkit_NVCC_EXECUTABLE}")
 
+set(should_exist
+  CUDAToolkit_BIN_DIR
+  CUDAToolkit_INCLUDE_DIRS
+  CUDAToolkit_LIBRARY_DIR
+  CUDAToolkit_LIBRARY_ROOT
+  )
+foreach (cuda_loc_var IN LISTS should_exist)
+  foreach (entry IN LISTS ${cuda_loc_var})
+    if(NOT EXISTS "${entry}")
+      message(FATAL_ERROR "${cuda_loc_var}[${entry}] variable is expected to be set to valid path")
+    endif()
+  endforeach()
+endforeach()
 
 set(cuda_libs cudart cuda_driver cublas cufft cufftw curand cusolver cusparse)
 if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 10.1)
diff --git a/Tests/ExportImport/Import/A/CMakeLists.txt b/Tests/ExportImport/Import/A/CMakeLists.txt
index 5bafdf8..2c5662d 100644
--- a/Tests/ExportImport/Import/A/CMakeLists.txt
+++ b/Tests/ExportImport/Import/A/CMakeLists.txt
@@ -462,7 +462,7 @@
 
     unset(EXP_ERROR_VARIABLE CACHE)
     try_compile(EXP_ERROR_VARIABLE
-      "${CMAKE_CURRENT_SOURCE_DIR}/test_system"
+      "${CMAKE_CURRENT_BINARY_DIR}/test_system"
       "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
       COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable"
       LINK_LIBRARIES exp_systemlib
@@ -476,7 +476,7 @@
       unset(EXP_RUN_VAR CACHE)
       unset(EXP_COMPILE_VAR CACHE)
       try_run(EXP_RUN_VAR EXP_COMPILE_VAR
-        "${CMAKE_CURRENT_SOURCE_DIR}/test_system"
+        "${CMAKE_CURRENT_BINARY_DIR}/test_system"
         "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
         COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable"
         LINK_LIBRARIES exp_systemlib
@@ -493,7 +493,7 @@
 
     unset(BLD_ERROR_VARIABLE CACHE)
     try_compile(BLD_ERROR_VARIABLE
-      "${CMAKE_CURRENT_SOURCE_DIR}/test_system"
+      "${CMAKE_CURRENT_BINARY_DIR}/test_system"
       "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
       COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable"
       LINK_LIBRARIES bld_systemlib
@@ -507,7 +507,7 @@
       unset(BLD_RUN_VAR CACHE)
       unset(BLD_COMPILE_VAR CACHE)
       try_run(BLD_RUN_VAR BLD_COMPILE_VAR
-        "${CMAKE_CURRENT_SOURCE_DIR}/test_system"
+        "${CMAKE_CURRENT_BINARY_DIR}/test_system"
         "${CMAKE_CURRENT_SOURCE_DIR}/test_system.cpp"
         COMPILE_DEFINITIONS "-Wunused-variable -Werror=unused-variable"
         LINK_LIBRARIES bld_systemlib
diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake
index 6a34697..447a5b6 100644
--- a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake
+++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake
@@ -3,7 +3,7 @@
 if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
   check_installed([[^lib;lib/libdep8\.so;lib/liblib\.so;subdir;subdir/bin;subdir/bin/exe1;subdir/bin/exe2;subdir/lib;subdir/lib/libdep10\.so;subdir/lib/libdep11\.so;subdir/lib/libdep2\.so\.1;subdir/lib/libdep2\.so\.1\.2\.3;subdir/lib/libdep3\.so;subdir/lib/libdep5\.so;subdir/lib/libdep6\.so;subdir/lib/libdep8\.so;subdir/lib/libdep9\.so;subdir/lib/liblib\.so;subdir/lib/libmod\.so;subdir/lib/libsublib1\.so$]])
 elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
-  set(_msvc_check [[bin;bin/dep8\.dll;bin/lib\.dll;lib;lib/lib\.lib;subdir;subdir/bin;subdir/bin/dep10\.dll;subdir/bin/dep11\.dll;subdir/bin/dep2\.dll;subdir/bin/dep3\.dll;subdir/bin/dep5\.dll;subdir/bin/dep6\.dll;subdir/bin/dep8\.dll;subdir/bin/dep9\.dll;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/lib\.dll;subdir/bin/sublib1\.dll;subdir/lib;subdir/lib/mod\.dll]])
+  set(_msvc_check [[bin;bin/dep8\.dll;bin/lib\.dll;lib;lib/lib\.(lib|l);subdir;subdir/bin;subdir/bin/dep10\.dll;subdir/bin/dep11\.dll;subdir/bin/dep2\.dll;subdir/bin/dep3\.dll;subdir/bin/dep5\.dll;subdir/bin/dep6\.dll;subdir/bin/dep8\.dll;subdir/bin/dep9\.dll;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/lib\.dll;subdir/bin/sublib1\.dll;subdir/lib;subdir/lib/mod\.dll]])
   set(_mingw_check [[bin;bin/libdep8\.dll;bin/liblib\.dll;lib;lib/liblib\.dll\.a;lib/liblib\.lib;subdir;subdir/bin;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/libdep10\.dll;subdir/bin/libdep11\.dll;subdir/bin/libdep2\.dll;subdir/bin/libdep3\.dll;subdir/bin/libdep5\.dll;subdir/bin/libdep6\.dll;subdir/bin/libdep8\.dll;subdir/bin/libdep9\.dll;subdir/bin/liblib\.dll;subdir/bin/libsublib1\.dll;subdir/lib;subdir/lib/libmod\.dll]])
   check_installed("^(${_msvc_check}|${_mingw_check})$")
 elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
diff --git a/Tests/ExternalOBJ/CMakeLists.txt b/Tests/ExternalOBJ/CMakeLists.txt
index 4ff75b8..141977c 100644
--- a/Tests/ExternalOBJ/CMakeLists.txt
+++ b/Tests/ExternalOBJ/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(APPLE)
   # set _CMAKE_OSX_MACHINE to umame -m
-  exec_program(uname ARGS -m OUTPUT_VARIABLE _CMAKE_OSX_MACHINE)
+  execute_process(COMMAND uname -m OUTPUT_VARIABLE _CMAKE_OSX_MACHINE OUTPUT_STRIP_TRAILING_WHITESPACE)
   # check for Power PC and change to ppc
   if("${_CMAKE_OSX_MACHINE}" MATCHES "Power")
     set(_CMAKE_OSX_MACHINE ppc)
diff --git a/Tests/ExternalOBJ/Object/CMakeLists.txt b/Tests/ExternalOBJ/Object/CMakeLists.txt
index dbfe09e..a886da0 100644
--- a/Tests/ExternalOBJ/Object/CMakeLists.txt
+++ b/Tests/ExternalOBJ/Object/CMakeLists.txt
@@ -2,7 +2,7 @@
 project(Object)
 if(APPLE)
   # set _CMAKE_OSX_MACHINE to umame -m
-  exec_program(uname ARGS -m OUTPUT_VARIABLE _CMAKE_OSX_MACHINE)
+  execute_process(COMMAND uname -m OUTPUT_VARIABLE _CMAKE_OSX_MACHINE OUTPUT_STRIP_TRAILING_WHITESPACE)
   # check for Power PC and change to ppc
   if("${_CMAKE_OSX_MACHINE}" MATCHES "Power")
     set(_CMAKE_OSX_MACHINE ppc)
diff --git a/Tests/FindGLUT/CMakeLists.txt b/Tests/FindGLUT/CMakeLists.txt
index e75ec40..fc9ed08 100644
--- a/Tests/FindGLUT/CMakeLists.txt
+++ b/Tests/FindGLUT/CMakeLists.txt
@@ -5,5 +5,5 @@
   ${build_generator_args}
   --build-project TestFindGLUT
   --build-options ${build_options}
-  --test-command ${CMAKE_CTEST_COMMAND} -V
+  --test-command ${CMAKE_CTEST_COMMAND} -V ${test_options}
   )
diff --git a/Tests/FindGSL/CMakeLists.txt b/Tests/FindGSL/CMakeLists.txt
index 45a3471..594a34e 100644
--- a/Tests/FindGSL/CMakeLists.txt
+++ b/Tests/FindGSL/CMakeLists.txt
@@ -5,5 +5,5 @@
   ${build_generator_args}
   --build-project FindGSL_rng
   --build-options ${build_options}
-  --test-command ${CMAKE_CTEST_COMMAND} -V
+  --test-command ${CMAKE_CTEST_COMMAND} -V ${test_options}
   )
diff --git a/Tests/FindOpenSSL/CMakeLists.txt b/Tests/FindOpenSSL/CMakeLists.txt
index 66b3fb2..9a7a61c 100644
--- a/Tests/FindOpenSSL/CMakeLists.txt
+++ b/Tests/FindOpenSSL/CMakeLists.txt
@@ -5,5 +5,5 @@
   ${build_generator_args}
   --build-project FindOpenSSL_rand
   --build-options ${build_options}
-  --test-command ${CMAKE_CTEST_COMMAND} -V
+  --test-command ${CMAKE_CTEST_COMMAND} -V ${test_options}
   )
diff --git a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
index 99823a6..18f8fda 100644
--- a/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
+++ b/Tests/FindPython/ArtifactsInteractive/CMakeLists.txt
@@ -3,7 +3,7 @@
 project(TestArtifactsInteractive LANGUAGES C)
 
 set (components Interpreter Development)
-if (CMake_TEST_FindPython_NumPy)
+if (CMake_TEST_FindPython3_NumPy)
   list (APPEND components NumPy)
 endif()
 
@@ -12,13 +12,13 @@
 if (Python3_ARTIFACTS_INTERACTIVE)
   if (NOT DEFINED CACHE{Python3_EXECUTABLE}
       OR NOT DEFINED CACHE{Python3_LIBRARY} OR NOT DEFINED CACHE{Python3_INCLUDE_DIR}
-      OR (CMake_TEST_FindPython_NumPy AND NOT DEFINED CACHE{Python3_NumPy_INCLUDE_DIR}))
+      OR (CMake_TEST_FindPython3_NumPy AND NOT DEFINED CACHE{Python3_NumPy_INCLUDE_DIR}))
     message (FATAL_ERROR "Python3_ARTIFACTS_INTERACTIVE=ON Failed.")
   endif()
 else()
   if (DEFINED CACHE{Python3_EXECUTABLE}
       OR DEFINED CACHE{Python3_LIBRARY} OR DEFINED CACHE{Python3_INCLUDE_DIR}
-      OR (CMake_TEST_FindPython_NumPy AND DEFINED CACHE{Python3_NumPy_INCLUDE_DIR}))
+      OR (CMake_TEST_FindPython3_NumPy AND DEFINED CACHE{Python3_NumPy_INCLUDE_DIR}))
     message (FATAL_ERROR "Python3_ARTIFACTS_INTERACTIVE=OFF Failed.")
   endif()
 endif()
diff --git a/Tests/FindPython/CMakeLists.txt b/Tests/FindPython/CMakeLists.txt
index b6942c9..636a7b0 100644
--- a/Tests/FindPython/CMakeLists.txt
+++ b/Tests/FindPython/CMakeLists.txt
@@ -1,4 +1,4 @@
-if(CMake_TEST_FindPython)
+if(CMake_TEST_FindPython2)
   add_test(NAME FindPython.Python2.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -44,6 +44,139 @@
   set_tests_properties(FindPython.Python2Fail PROPERTIES
     PASS_REGULAR_EXPRESSION "Could NOT find Python2 \\(missing: foobar\\)")
 
+  add_test(NAME FindPython.Python.V2.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.LOCATION"
+    ${build_generator_args}
+    --build-project TestPython
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.Python.V2.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.VERSION"
+    ${build_generator_args}
+    --build-project TestPython
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  add_test(NAME FindPython.Python2.ExactVersion.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.ExactVersion.LOCATION"
+    ${build_generator_args}
+    --build-project TestExactVersion
+    --build-options ${build_options} -DPython_MAJOR_VERSION=2
+                                     -DPython_REQUESTED_VERSION=2.1.2
+                                     -DPython2_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.Python2.ExactVersion.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.ExactVersion.VERSION"
+    ${build_generator_args}
+    --build-project TestExactVersion
+    --build-options ${build_options} -DPython_MAJOR_VERSION=2
+                                     -DPython_REQUESTED_VERSION=2.1.2
+                                     -DPython2_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  add_test(NAME FindPython.Python.V2.ExactVersion.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.ExactVersion.LOCATION"
+    ${build_generator_args}
+    --build-project TestExactVersion
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=2.1.2
+                                     -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.Python.V2.ExactVersion.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.ExactVersion.VERSION"
+    ${build_generator_args}
+    --build-project TestExactVersion
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=2.1.2
+                                     -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  add_test(NAME FindPython.Python2.VersionRange.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.VersionRange.LOCATION"
+    ${build_generator_args}
+    --build-project TestVersionRange
+    --build-options ${build_options} -DPython=Python2 -DPython_REQUESTED_VERSION=2
+                                     -DPython2_FIND_STRATEGY=LOCATION
+    )
+  add_test(NAME FindPython.Python2.VersionRange.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.VersionRange.VERSION"
+    ${build_generator_args}
+    --build-project TestVersionRange
+    --build-options ${build_options} -DPython=Python2 -DPython_REQUESTED_VERSION=2
+                                     -DPython2_FIND_STRATEGY=VERSION
+    )
+  add_test(NAME FindPython.Python.V2.VersionRange.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.VersionRange.LOCATION"
+    ${build_generator_args}
+    --build-project TestVersionRange
+    --build-options ${build_options} -DPython=Python -DPython_REQUESTED_VERSION=2
+                                     -DPython_FIND_STRATEGY=LOCATION
+    )
+  add_test(NAME FindPython.Python.V2.VersionRange.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.VersionRange.VERSION"
+    ${build_generator_args}
+    --build-project TestVersionRange
+    --build-options ${build_options} -DPython=Python -DPython_REQUESTED_VERSION=2
+                                     -DPython_FIND_STRATEGY=VERSION
+    )
+
+  add_test(NAME FindPython.Python2Embedded COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python2Embedded"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python2Embedded"
+    ${build_generator_args}
+    --build-project TestPython2Embedded
+    --build-options ${build_options}
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  set_property(TEST FindPython.Python2.LOCATION FindPython.Python2.VERSION
+                    FindPython.Python2.Development.Module FindPython.Python2Fail
+                    FindPython.Python.V2.LOCATION FindPython.Python.V2.VERSION
+                    FindPython.Python2.ExactVersion.LOCATION FindPython.Python2.ExactVersion.VERSION
+                    FindPython.Python.V2.ExactVersion.LOCATION FindPython.Python.V2.ExactVersion.VERSION
+                    FindPython.Python2.VersionRange.LOCATION FindPython.Python2.VersionRange.VERSION
+                    FindPython.Python.V2.VersionRange.LOCATION FindPython.Python.V2.VersionRange.VERSION
+                    FindPython.Python2Embedded
+               APPEND PROPERTY LABELS Python2)
+endif()
+
+if(CMake_TEST_FindPython3)
   add_test(NAME FindPython.Python3.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -89,46 +222,6 @@
   set_tests_properties(FindPython.Python3Fail PROPERTIES
     PASS_REGULAR_EXPRESSION "Could NOT find Python3 \\(missing: foobar\\)")
 
-  add_test(NAME FindPython.Python.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.LOCATION"
-    ${build_generator_args}
-    --build-project TestPython
-    --build-options ${build_options} -DPython_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.Python.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.VERSION"
-    ${build_generator_args}
-    --build-project TestPython
-    --build-options ${build_options} -DPython_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.Python.V2.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.LOCATION"
-    ${build_generator_args}
-    --build-project TestPython
-    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.Python.V2.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.VERSION"
-    ${build_generator_args}
-    --build-project TestPython
-    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
   add_test(NAME FindPython.Python.V3.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -150,30 +243,6 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
-  add_test(NAME FindPython.Python2.ExactVersion.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.ExactVersion.LOCATION"
-    ${build_generator_args}
-    --build-project TestExactVersion
-    --build-options ${build_options} -DPython_MAJOR_VERSION=2
-                                     -DPython_REQUESTED_VERSION=2.1.2
-                                     -DPython2_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.Python2.ExactVersion.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.ExactVersion.VERSION"
-    ${build_generator_args}
-    --build-project TestExactVersion
-    --build-options ${build_options} -DPython_MAJOR_VERSION=2
-                                     -DPython_REQUESTED_VERSION=2.1.2
-                                     -DPython2_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
   add_test(NAME FindPython.Python3.ExactVersion.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -198,28 +267,6 @@
                                      -DPython3_FIND_STRATEGY=VERSION
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
-  add_test(NAME FindPython.Python.V2.ExactVersion.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.ExactVersion.LOCATION"
-    ${build_generator_args}
-    --build-project TestExactVersion
-    --build-options ${build_options} -DPython_REQUESTED_VERSION=2.1.2
-                                     -DPython_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.Python.V2.ExactVersion.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/ExactVersion"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.ExactVersion.VERSION"
-    ${build_generator_args}
-    --build-project TestExactVersion
-    --build-options ${build_options} -DPython_REQUESTED_VERSION=2.1.2
-                                     -DPython_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
   add_test(NAME FindPython.Python.V3.ExactVersion.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -263,46 +310,6 @@
     --build-options ${build_options} -DPython=Python3 -DPython_REQUESTED_VERSION=3
                                      -DPython3_FIND_STRATEGY=VERSION
     )
-  add_test(NAME FindPython.Python2.VersionRange.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.VersionRange.LOCATION"
-    ${build_generator_args}
-    --build-project TestVersionRange
-    --build-options ${build_options} -DPython=Python2 -DPython_REQUESTED_VERSION=2
-                                     -DPython2_FIND_STRATEGY=LOCATION
-    )
-  add_test(NAME FindPython.Python2.VersionRange.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python2.VersionRange.VERSION"
-    ${build_generator_args}
-    --build-project TestVersionRange
-    --build-options ${build_options} -DPython=Python2 -DPython_REQUESTED_VERSION=2
-                                     -DPython2_FIND_STRATEGY=VERSION
-    )
-  add_test(NAME FindPython.Python.V2.VersionRange.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.VersionRange.LOCATION"
-    ${build_generator_args}
-    --build-project TestVersionRange
-    --build-options ${build_options} -DPython=Python -DPython_REQUESTED_VERSION=2
-                                     -DPython_FIND_STRATEGY=LOCATION
-    )
-  add_test(NAME FindPython.Python.V2.VersionRange.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python.V2.VersionRange.VERSION"
-    ${build_generator_args}
-    --build-project TestVersionRange
-    --build-options ${build_options} -DPython=Python -DPython_REQUESTED_VERSION=2
-                                     -DPython_FIND_STRATEGY=VERSION
-    )
   add_test(NAME FindPython.Python.V3.VersionRange.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -324,17 +331,6 @@
                                      -DPython_FIND_STRATEGY=VERSION
     )
 
-  add_test(NAME FindPython.MultiplePackages COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/MultiplePackages"
-    "${CMake_BINARY_DIR}/Tests/FindPython/MultiplePackages"
-    ${build_generator_args}
-    --build-project TestMultiplePackages
-    --build-options ${build_options}
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-
   add_test(NAME FindPython.VirtualEnv COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -346,16 +342,6 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
-  add_test(NAME FindPython.Python2Embedded COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/Python2Embedded"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Python2Embedded"
-    ${build_generator_args}
-    --build-project TestPython2Embedded
-    --build-options ${build_options}
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
   add_test(NAME FindPython.Python3Embedded COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -377,7 +363,7 @@
     --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
     "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
     "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
-    "-DCMake_TEST_FindPython_SABIModule=${CMake_TEST_FindPython_SABIModule}"
+    "-DCMake_TEST_FindPython3_SABIModule=${CMake_TEST_FindPython3_SABIModule}"
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
@@ -391,7 +377,7 @@
     --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
     "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
     "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
-    "-DCMake_TEST_FindPython_NumPy=${CMake_TEST_FindPython_NumPy}"
+    "-DCMake_TEST_FindPython3_NumPy=${CMake_TEST_FindPython3_NumPy}"
     "-DPython3_ARTIFACTS_INTERACTIVE=ON"
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
@@ -405,7 +391,7 @@
     --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
     "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
     "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
-    "-DCMake_TEST_FindPython_NumPy=${CMake_TEST_FindPython_NumPy}"
+    "-DCMake_TEST_FindPython3_NumPy=${CMake_TEST_FindPython3_NumPy}"
     "-DPython3_ARTIFACTS_INTERACTIVE=OFF"
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
@@ -420,7 +406,7 @@
     --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
     "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
     "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
-    "-DCMake_TEST_FindPython_NumPy=${CMake_TEST_FindPython_NumPy}"
+    "-DCMake_TEST_FindPython3_NumPy=${CMake_TEST_FindPython3_NumPy}"
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
@@ -437,6 +423,54 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
+  set_property(TEST FindPython.Python3.LOCATION FindPython.Python3.VERSION
+                    FindPython.Python3.Development.Module FindPython.Python3Fail
+                    FindPython.Python.V3.LOCATION FindPython.Python.V3.VERSION
+                    FindPython.Python3.ExactVersion.LOCATION FindPython.Python3.ExactVersion.VERSION
+                    FindPython.Python.V3.ExactVersion.LOCATION FindPython.Python.V3.ExactVersion.VERSION
+                    FindPython.Python3.VersionRange.LOCATION FindPython.Python3.VersionRange.VERSION
+                    FindPython.Python.V3.VersionRange.LOCATION FindPython.Python.V3.VersionRange.VERSION
+                    FindPython.VirtualEnv FindPython.Python3Embedded FindPython.RequiredArtifacts
+                    FindPython.ArtifactsInteractive.ON FindPython.ArtifactsInteractive.OFF
+                    FindPython.CustomFailureMessage FindPython.DifferentComponents
+               APPEND PROPERTY LABELS Python3)
+
+  if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    add_test(NAME FindPython.UnversionedNames COMMAND
+      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/FindPython/UnversionedNames"
+      "${CMake_BINARY_DIR}/Tests/FindPython/UnversionedNames"
+      ${build_generator_args}
+      --build-project UnversionedNames
+      --build-options ${build_options}
+    )
+    set_property(TEST FindPython.UnversionedNames APPEND PROPERTY LABELS Python3)
+  endif()
+endif()
+
+if(CMake_TEST_FindPython2 OR CMake_TEST_FindPython3)
+  add_test(NAME FindPython.Python.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.LOCATION"
+    ${build_generator_args}
+    --build-project TestPython
+    --build-options ${build_options} -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.Python.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Python"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Python.VERSION"
+    ${build_generator_args}
+    --build-project TestPython
+    --build-options ${build_options} -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
   if (CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin")
     add_test(NAME FindPython.Interpreter.SOABI COMMAND
       ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
@@ -464,22 +498,28 @@
       "-DCMake_TEST_FindPython_COMPONENT=Development"
       --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
       )
+
+    set_property(TEST FindPython.Interpreter.SOABI FindPython.Development.SOABI
+                 APPEND PROPERTY LABELS Python2 Python3)
   endif()
 
-  if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
-    add_test(NAME FindPython.UnversionedNames COMMAND
-      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-      --build-and-test
-      "${CMake_SOURCE_DIR}/Tests/FindPython/UnversionedNames"
-      "${CMake_BINARY_DIR}/Tests/FindPython/UnversionedNames"
-      ${build_generator_args}
-      --build-project UnversionedNames
-      --build-options ${build_options}
+  add_test(NAME FindPython.MultiplePackages COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/MultiplePackages"
+    "${CMake_BINARY_DIR}/Tests/FindPython/MultiplePackages"
+    ${build_generator_args}
+    --build-project TestMultiplePackages
+    --build-options ${build_options}
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
-  endif()
+
+  set_property(TEST FindPython.Python.LOCATION FindPython.Python.VERSION FindPython.MultiplePackages
+               APPEND PROPERTY LABELS Python2 Python3)
 endif()
 
-if(CMake_TEST_FindPython_SABIModule)
+
+if(CMake_TEST_FindPython2_SABIModule)
   add_test(NAME FindPython.Python2.Development.SABIModule COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -490,9 +530,12 @@
     --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
-    set_tests_properties(FindPython.Python2.Development.SABIModule PROPERTIES
-      PASS_REGULAR_EXPRESSION "Could NOT find Python2 \\(missing: .*Development\\.SABIModule")
+  set_tests_properties(FindPython.Python2.Development.SABIModule PROPERTIES
+    PASS_REGULAR_EXPRESSION "Could NOT find Python2 \\(missing: .*Development\\.SABIModule")
+  set_property(TEST FindPython.Python2.Development.SABIModule APPEND PROPERTY LABELS Python2)
+endif()
 
+if(CMake_TEST_FindPython3_SABIModule)
   # Use exclusively Release configuration because Debug is, on Windows with MSVC,
   # unusable with SABI: Python force link with debug version of full versioned library rather than
   # the stable ABI one.
@@ -506,9 +549,10 @@
     --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V -C Release
     )
+  set_property(TEST FindPython.Python3.Development.SABIModule APPEND PROPERTY LABELS Python3)
 endif()
 
-if(CMake_TEST_FindPython_NumPy)
+if(CMake_TEST_FindPython2_NumPy OR CMake_TEST_FindPython3_NumPy)
   add_test(NAME FindPython.NumPy COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -528,9 +572,11 @@
     --build-project TestNumPyOnly
     --build-options ${build_options}
   )
+
+  set_property(TEST FindPython.NumPy FindPython.NumPyOnly APPEND PROPERTY LABELS Python2 Python3)
 endif()
 
-if(CMake_TEST_FindPython_Conda)
+if(CMake_TEST_FindPython3_Conda)
   add_test(NAME FindPython.VirtualEnvConda COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -541,32 +587,62 @@
     --build-options ${build_options}
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
+  set_property(TEST FindPython.VirtualEnvConda APPEND PROPERTY LABELS Python3)
 endif()
 
-if (CMake_TEST_FindPython AND CMake_TEST_FindPython_IronPython)
-  add_test(NAME FindPython.Implementation.CPython COMMAND
+if (CMake_TEST_FindPython2 AND CMake_TEST_FindPython2_IronPython)
+  add_test(NAME FindPython.Implementation.CPython2 COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
     "${CMake_SOURCE_DIR}/Tests/FindPython/Implementation"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Implementation.CPython"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Implementation.CPython2"
     ${build_generator_args}
     --build-project TestImplementationCPython
     --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_REQUESTED_IMPLEMENTATIONS=CPython
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
-  add_test(NAME FindPython.Implementation.IronPython COMMAND
+  add_test(NAME FindPython.Implementation.IronPython2 COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
     "${CMake_SOURCE_DIR}/Tests/FindPython/Implementation"
-    "${CMake_BINARY_DIR}/Tests/FindPython/Implementation.IronPython"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Implementation.IronPython2"
     ${build_generator_args}
     --build-project TestImplementationIronPython
     --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_REQUESTED_IMPLEMENTATION=IronPython
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
+
+  set_property(TEST FindPython.Implementation.CPython2 FindPython.Implementation.IronPython2
+               APPEND PROPERTY LABELS Python2)
 endif()
 
-if(CMake_TEST_FindPython_IronPython)
+if (CMake_TEST_FindPython3 AND CMake_TEST_FindPython3_IronPython)
+  add_test(NAME FindPython.Implementation.CPython3 COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Implementation"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Implementation.CPython3"
+    ${build_generator_args}
+    --build-project TestImplementationCPython
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=3 -DPython_REQUESTED_IMPLEMENTATIONS=CPython
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.Implementation.IronPython3 COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/Implementation"
+    "${CMake_BINARY_DIR}/Tests/FindPython/Implementation.IronPython3"
+    ${build_generator_args}
+    --build-project TestImplementationIronPython
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=3 -DPython_REQUESTED_IMPLEMENTATION=IronPython
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  set_property(TEST FindPython.Implementation.CPython3 FindPython.Implementation.IronPython3
+               APPEND PROPERTY LABELS Python3)
+endif()
+
+if(CMake_TEST_FindPython2_IronPython)
   add_test(NAME FindPython.IronPython2.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -588,26 +664,6 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
-  add_test(NAME FindPython.IronPython.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython"
-    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython.LOCATION"
-    ${build_generator_args}
-    --build-project TestIronPython
-    --build-options ${build_options} -DPython_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.IronPython.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython"
-    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython.VERSION"
-    ${build_generator_args}
-    --build-project TestIronPython
-    --build-options ${build_options} -DPython_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
   add_test(NAME FindPython.IronPython.V2.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -651,9 +707,112 @@
                                      -DPython2_FIND_IMPLEMENTATIONS=IronPython
                                      -DPython2_FIND_STRATEGY=VERSION
     )
+
+  set_property(TEST FindPython.IronPython2.LOCATION FindPython.IronPython2.VERSION
+                    FindPython.IronPython.V2.LOCATION FindPython.IronPython.V2.VERSION
+                    FindPython.IronPython2.VersionRange.LOCATION FindPython.IronPython2.VersionRange.VERSION
+               APPEND PROPERTY LABELS Python2)
 endif()
 
-if(CMake_TEST_FindPython_PyPy)
+if(CMake_TEST_FindPython3_IronPython)
+  add_test(NAME FindPython.IronPython3.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython3"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython3.LOCATION"
+    ${build_generator_args}
+    --build-project TestIronPython3
+    --build-options ${build_options} -DPython3_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.IronPython3.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython3"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython3.VERSION"
+    ${build_generator_args}
+    --build-project TestIronPython3
+    --build-options ${build_options} -DPython3_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  add_test(NAME FindPython.IronPython.V3.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython.V3.LOCATION"
+    ${build_generator_args}
+    --build-project TestIronPython
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=3 -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.IronPython.V3.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython.V3.VERSION"
+    ${build_generator_args}
+    --build-project TestIronPython
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=3 -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  add_test(NAME FindPython.IronPython3.VersionRange.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython3.VersionRange.LOCATION"
+    ${build_generator_args}
+    --build-project TestVersionRange
+    --build-options ${build_options} -DPython=Python3 -DPython_REQUESTED_VERSION=3
+                                     -DPython3_FIND_IMPLEMENTATIONS=IronPython
+                                     -DPython3_FIND_STRATEGY=LOCATION
+    )
+  add_test(NAME FindPython.IronPython3.VersionRange.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/VersionRange"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython3.VersionRange.VERSION"
+    ${build_generator_args}
+    --build-project TestVersionRange
+    --build-options ${build_options} -DPython=Python3 -DPython_REQUESTED_VERSION=3
+                                     -DPython3_FIND_IMPLEMENTATIONS=IronPython
+                                     -DPython3_FIND_STRATEGY=VERSION
+    )
+
+  set_property(TEST FindPython.IronPython3.LOCATION FindPython.IronPython3.VERSION
+                    FindPython.IronPython.V3.LOCATION FindPython.IronPython.V3.VERSION
+                    FindPython.IronPython3.VersionRange.LOCATION FindPython.IronPython3.VersionRange.VERSION
+               APPEND PROPERTY LABELS Python3)
+endif()
+
+if(CMake_TEST_FindPython2_IronPython OR CMake_TEST_FindPython3_IronPython)
+  add_test(NAME FindPython.IronPython.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython.LOCATION"
+    ${build_generator_args}
+    --build-project TestIronPython
+    --build-options ${build_options} -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.IronPython.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/IronPython"
+    "${CMake_BINARY_DIR}/Tests/FindPython/IronPython.VERSION"
+    ${build_generator_args}
+    --build-project TestIronPython
+    --build-options ${build_options} -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  set_property(TEST FindPython.IronPython.LOCATION FindPython.IronPython.VERSION
+               APPEND PROPERTY LABELS Python2 Python3)
+endif()
+
+if(CMake_TEST_FindPython2_PyPy)
   add_test(NAME FindPython.PyPy2.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -675,6 +834,33 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
+  add_test(NAME FindPython.PyPy.V2.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
+    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.V2.LOCATION"
+    ${build_generator_args}
+    --build-project TestPyPy
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.PyPy.V2.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
+    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.V2.VERSION"
+    ${build_generator_args}
+    --build-project TestPyPy
+    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  set_property(TEST FindPython.PyPy2.LOCATION FindPython.PyPy2.VERSION
+                    FindPython.PyPy.V2.LOCATION FindPython.PyPy.V2.VERSION
+               APPEND PROPERTY LABELS Python2)
+endif()
+
+if(CMake_TEST_FindPython3_PyPy)
   add_test(NAME FindPython.PyPy3.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -696,46 +882,6 @@
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
-  add_test(NAME FindPython.PyPy.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
-    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.LOCATION"
-    ${build_generator_args}
-    --build-project TestPyPy
-    --build-options ${build_options} -DPython_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.PyPy.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
-    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.VERSION"
-    ${build_generator_args}
-    --build-project TestPyPy
-    --build-options ${build_options} -DPython_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.PyPy.V2.LOCATION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
-    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.V2.LOCATION"
-    ${build_generator_args}
-    --build-project TestPyPy
-    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=LOCATION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
-  add_test(NAME FindPython.PyPy.V2.VERSION COMMAND
-    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-    --build-and-test
-    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
-    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.V2.VERSION"
-    ${build_generator_args}
-    --build-project TestPyPy
-    --build-options ${build_options} -DPython_REQUESTED_VERSION=2 -DPython_FIND_STRATEGY=VERSION
-    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-    )
   add_test(NAME FindPython.PyPy.V3.LOCATION COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -756,4 +902,34 @@
     --build-options ${build_options} -DPython_REQUESTED_VERSION=3 -DPython_FIND_STRATEGY=VERSION
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
+
+  set_property(TEST FindPython.PyPy3.LOCATION FindPython.PyPy3.VERSION
+                    FindPython.PyPy.V3.LOCATION FindPython.PyPy.V3.VERSION
+               APPEND PROPERTY LABELS Python3)
+endif()
+
+if(CMake_TEST_FindPython2_PyPy OR CMake_TEST_FindPython3_PyPy)
+  add_test(NAME FindPython.PyPy.LOCATION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
+    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.LOCATION"
+    ${build_generator_args}
+    --build-project TestPyPy
+    --build-options ${build_options} -DPython_FIND_STRATEGY=LOCATION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+  add_test(NAME FindPython.PyPy.VERSION COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/FindPython/PyPy"
+    "${CMake_BINARY_DIR}/Tests/FindPython/PyPy.VERSION"
+    ${build_generator_args}
+    --build-project TestPyPy
+    --build-options ${build_options} -DPython_FIND_STRATEGY=VERSION
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+    )
+
+  set_property(TEST FindPython.PyPy.LOCATION FindPython.PyPy.VERSION
+               APPEND PROPERTY LABELS Python2 Python3)
 endif()
diff --git a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
index 283aeec..e0148f3 100644
--- a/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
+++ b/Tests/FindPython/CustomFailureMessage/CMakeLists.txt
@@ -62,7 +62,7 @@
   PASS_REGULAR_EXPRESSION "Reason given by package:.+Interpreter: Cannot run the interpreter.+Development: Cannot find the library")
 
 
-if (CMake_TEST_FindPython_NumPy)
+if (CMake_TEST_FindPython3_NumPy)
   add_test(NAME FindPython.CustomFailureMessage.NumPy COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
diff --git a/Tests/FindPython/IronPython3/CMakeLists.txt b/Tests/FindPython/IronPython3/CMakeLists.txt
new file mode 100644
index 0000000..b09097a
--- /dev/null
+++ b/Tests/FindPython/IronPython3/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.5)
+
+project(TestIronPython3 LANGUAGES NONE)
+
+set (Python3_FIND_IMPLEMENTATIONS "IronPython")
+
+find_package(Python3 COMPONENTS Interpreter Compiler)
+if (NOT Python3_FOUND)
+  message (FATAL_ERROR "Failed to find Python 3")
+endif()
+
+if (NOT Python3_Interpreter_FOUND)
+  message (FATAL_ERROR "Failed to find Python 3 Interpreter")
+endif()
+if (NOT Python3_INTERPRETER_ID STREQUAL "IronPython")
+  message (FATAL_ERROR "Erroneous interpreter ID (${Python3_INTERPRETER_ID})")
+endif()
+
+if (NOT Python3_Compiler_FOUND)
+  message (FATAL_ERROR "Failed to find Python 3 Compiler")
+endif()
+if (NOT Python3_COMPILER_ID STREQUAL "IronPython")
+  message (FATAL_ERROR "Erroneous compiler ID (${Python3_COMPILER_ID})")
+endif()
+
+if(NOT TARGET Python3::Interpreter)
+  message(SEND_ERROR "Python3::Interpreter not found")
+endif()
+if(NOT TARGET Python3::Compiler)
+  message(SEND_ERROR "Python3::Compiler not found")
+endif()
diff --git a/Tests/FindPython/MultiplePackages/CMakeLists.txt b/Tests/FindPython/MultiplePackages/CMakeLists.txt
index 4845035..352a2f6f 100644
--- a/Tests/FindPython/MultiplePackages/CMakeLists.txt
+++ b/Tests/FindPython/MultiplePackages/CMakeLists.txt
@@ -2,32 +2,44 @@
 
 project(TestMultiplePackages C)
 
-find_package (Python2 REQUIRED COMPONENTS Interpreter Development)
-find_package (Python3 REQUIRED COMPONENTS Interpreter Development)
-
-# Must find Python 3
 find_package (Python REQUIRED)
 
-if (NOT Python3_EXECUTABLE STREQUAL Python_EXECUTABLE)
-  message (FATAL_ERROR
-    "Python interpreters do not match:\n"
-    "  Python_EXECUTABLE='${Python_EXECUTABLE}'\n"
-    "  Python3_EXECUTABLE='${Python3_EXECUTABLE}'\n"
+if (CMake_TEST_FindPython2)
+  find_package (Python2 REQUIRED COMPONENTS Interpreter Development)
+
+  if (NOT CMake_TEST_FindPython3 AND NOT Python2_EXECUTABLE STREQUAL Python_EXECUTABLE)
+    message (FATAL_ERROR
+      "Python interpreters do not match:\n"
+      "  Python_EXECUTABLE='${Python_EXECUTABLE}'\n"
+      "  Python2_EXECUTABLE='${Python3_EXECUTABLE}'\n"
     )
+  endif()
+
+  Python2_add_library (spam2 MODULE ../spam.c)
+  target_compile_definitions (spam2 PRIVATE PYTHON2)
+
+  add_test (NAME python2_spam2
+            COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
+            "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")")
+
 endif()
 
+if (CMake_TEST_FindPython3)
+  find_package (Python3 REQUIRED COMPONENTS Interpreter Development)
 
-Python2_add_library (spam2 MODULE ../spam.c)
-target_compile_definitions (spam2 PRIVATE PYTHON2)
+  if (NOT Python3_EXECUTABLE STREQUAL Python_EXECUTABLE)
+    message (FATAL_ERROR
+      "Python interpreters do not match:\n"
+      "  Python_EXECUTABLE='${Python_EXECUTABLE}'\n"
+      "  Python3_EXECUTABLE='${Python3_EXECUTABLE}'\n"
+    )
+  endif()
 
-Python3_add_library (spam3 MODULE ../spam.c)
-target_compile_definitions (spam3 PRIVATE PYTHON3)
+  Python3_add_library (spam3 MODULE ../spam.c)
+  target_compile_definitions (spam3 PRIVATE PYTHON3)
 
+  add_test (NAME python3_spam3
+            COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
+            "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
 
-add_test (NAME python2_spam2
-          COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-          "${Python2_EXECUTABLE}" -c "import spam2; spam2.system(\"cd\")")
-
-add_test (NAME python3_spam3
-          COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:spam3>"
-          "${Python3_EXECUTABLE}" -c "import spam3; spam3.system(\"cd\")")
+endif()
diff --git a/Tests/FindPython/NumPy/CMakeLists.txt b/Tests/FindPython/NumPy/CMakeLists.txt
index 9920336..336bb83 100644
--- a/Tests/FindPython/NumPy/CMakeLists.txt
+++ b/Tests/FindPython/NumPy/CMakeLists.txt
@@ -2,21 +2,30 @@
 
 project(TestNumPy LANGUAGES C)
 
-find_package (Python2 REQUIRED COMPONENTS Interpreter Development NumPy)
-find_package (Python3 REQUIRED COMPONENTS Interpreter Development NumPy)
+if(CMake_TEST_FindPython2_NumPy)
 
-Python2_add_library (arraytest2 MODULE arraytest.c)
-target_compile_definitions (arraytest2 PRIVATE PYTHON2)
-target_link_libraries (arraytest2 PRIVATE Python2::NumPy)
+  find_package (Python2 REQUIRED COMPONENTS Interpreter Development NumPy)
 
-Python3_add_library (arraytest3 MODULE arraytest.c)
-target_compile_definitions (arraytest3 PRIVATE PYTHON3)
-target_link_libraries (arraytest3 PRIVATE Python3::NumPy)
+  Python2_add_library (arraytest2 MODULE arraytest.c)
+  target_compile_definitions (arraytest2 PRIVATE PYTHON2)
+  target_link_libraries (arraytest2 PRIVATE Python2::NumPy)
 
-add_test (NAME python2_arraytest
-          COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest2>"
-          "${Python2_EXECUTABLE}" -c "import numpy; import arraytest2; arraytest2.vecsq(numpy.array([1, 2, 3]));")
+  add_test (NAME python2_arraytest
+    COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest2>"
+    "${Python2_EXECUTABLE}" -c "import numpy; import arraytest2; arraytest2.vecsq(numpy.array([1, 2, 3]));")
 
-add_test (NAME python3_arraytest
-          COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest3>"
-          "${Python3_EXECUTABLE}" -c "import numpy; import arraytest3; arraytest3.vecsq(numpy.array([1, 2, 3]));")
+endif()
+
+if(CMake_TEST_FindPython3_NumPy)
+
+  find_package (Python3 REQUIRED COMPONENTS Interpreter Development NumPy)
+
+  Python3_add_library (arraytest3 MODULE arraytest.c)
+  target_compile_definitions (arraytest3 PRIVATE PYTHON3)
+  target_link_libraries (arraytest3 PRIVATE Python3::NumPy)
+
+  add_test (NAME python3_arraytest
+    COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:arraytest3>"
+    "${Python3_EXECUTABLE}" -c "import numpy; import arraytest3; arraytest3.vecsq(numpy.array([1, 2, 3]));")
+
+endif()
diff --git a/Tests/FindPython/NumPyOnly/CMakeLists.txt b/Tests/FindPython/NumPyOnly/CMakeLists.txt
index 9aa1bcf..115cf2b 100644
--- a/Tests/FindPython/NumPyOnly/CMakeLists.txt
+++ b/Tests/FindPython/NumPyOnly/CMakeLists.txt
@@ -2,13 +2,23 @@
 
 project(TestNumPyOnly LANGUAGES C)
 
-find_package(Python2 REQUIRED COMPONENTS NumPy)
-find_package(Python3 REQUIRED COMPONENTS NumPy)
+if(CMake_TEST_FindPython2_NumPy)
 
-Python2_add_library (arraytest2 MODULE ../NumPy/arraytest.c)
-target_compile_definitions (arraytest2 PRIVATE PYTHON2)
-target_link_libraries (arraytest2 PRIVATE Python2::NumPy)
+  find_package(Python2 REQUIRED COMPONENTS NumPy)
+
+  Python2_add_library (arraytest2 MODULE ../NumPy/arraytest.c)
+  target_compile_definitions (arraytest2 PRIVATE PYTHON2)
+  target_link_libraries (arraytest2 PRIVATE Python2::NumPy)
+
+endif()
+
+
+if(CMake_TEST_FindPython3_NumPy)
+
+find_package(Python3 REQUIRED COMPONENTS NumPy)
 
 Python3_add_library (arraytest3 MODULE ../NumPy/arraytest.c)
 target_compile_definitions (arraytest3 PRIVATE PYTHON3)
 target_link_libraries (arraytest3 PRIVATE Python3::NumPy)
+
+endif()
diff --git a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
index cb9d4d3..eec28a5 100644
--- a/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
+++ b/Tests/FindPython/RequiredArtifacts/CMakeLists.txt
@@ -4,13 +4,20 @@
 
 include(CTest)
 
-find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
-if (NOT Python2_FOUND)
-  message (FATAL_ERROR "Failed to find Python 2")
+if(CMake_TEST_FindPython2)
+  find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
+  if (NOT Python2_FOUND)
+    message (FATAL_ERROR "Failed to find Python 2")
+  endif()
+  set(USER_LIBRARY "${Python2_LIBRARY_RELEASE}")
+  set(USER_INCLUDE_DIR "${Python2_INCLUDE_DIRS}")
+else()
+  set(USER_LIBRARY "/path/to/invalid${CMAKE_C_LINK_LIBRARY_SUFFIX}")
+  set(USER_INCLUDE_DIR "/path/to/invalid/dir")
 endif()
 
 set(components Interpreter Development)
-if (CMake_TEST_FindPython_SABIModule AND WIN32)
+if (CMake_TEST_FindPython3_SABIModule AND WIN32)
   list (APPEND components Development.SABIModule)
 endif()
 find_package(Python3 REQUIRED COMPONENTS ${components})
@@ -61,7 +68,7 @@
   ${build_generator_args}
   --build-project TestRequiredArtifacts.Check
   --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_LIBRARY=ON
-                  "-DPython3_LIBRARY=${Python2_LIBRARY_RELEASE}"
+                  "-DPython3_LIBRARY=${USER_LIBRARY}"
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
 
@@ -84,7 +91,7 @@
   ${build_generator_args}
   --build-project TestRequiredArtifacts.Check
   --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_INCLUDE=ON
-                  "-DPython3_INCLUDE_DIR=${Python2_INCLUDE_DIRS}"
+                  "-DPython3_INCLUDE_DIR=${USER_INCLUDE_DIR}"
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
 
@@ -97,7 +104,7 @@
   --build-project TestRequiredArtifacts.Check
   --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_INTERPRETER=ON -DCHECK_LIBRARY=ON
                   "-DPython3_EXECUTABLE=${Python3_EXECUTABLE}"
-                  "-DPython3_LIBRARY=${Python2_LIBRARY_RELEASE}"
+                  "-DPython3_LIBRARY=${USER_LIBRARY}"
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
 
@@ -110,11 +117,11 @@
   --build-project TestRequiredArtifacts.Check
   --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_LIBRARY=ON -DCHECK_INCLUDE=ON
                   "-DPython3_LIBRARY=${Python3_LIBRARY_RELEASE}"
-                  "-DPython3_INCLUDE_DIR=${Python2_INCLUDE_DIRS}"
+                  "-DPython3_INCLUDE_DIR=${USER_INCLUDE_DIR}"
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
 
-if (CMake_TEST_FindPython_SABIModule AND WIN32)
+if (CMake_TEST_FindPython3_SABIModule AND WIN32)
   add_test(NAME FindPython.RequiredArtifacts.SABILibrary.VALID COMMAND
     ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
     --build-and-test
@@ -134,7 +141,7 @@
     ${build_generator_args}
     --build-project TestRequiredArtifacts.Check
     --build-options -DPYTHON_IS_FOUND=FALSE -DCHECK_SABI_LIBRARY=ON
-    "-DPython3_SABI_LIBRARY=${Python2_LIBRARY_RELEASE}"
+    "-DPython3_SABI_LIBRARY=${USER_LIBRARY}"
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
 endif()
diff --git a/Tests/FindPython/SOABI/CMakeLists.txt b/Tests/FindPython/SOABI/CMakeLists.txt
index 60399d3..6c0e9a9 100644
--- a/Tests/FindPython/SOABI/CMakeLists.txt
+++ b/Tests/FindPython/SOABI/CMakeLists.txt
@@ -2,37 +2,40 @@
 
 project(TestSOABI LANGUAGES C)
 
-find_package(Python3 COMPONENTS ${CMake_TEST_FindPython_COMPONENT})
-if (NOT Python3_FOUND)
-  message (FATAL_ERROR "Failed to find Python 3")
-endif()
+if(CMake_TEST_FindPython3)
+  find_package(Python3 COMPONENTS ${CMake_TEST_FindPython_COMPONENT})
+  if (NOT Python3_FOUND)
+    message (FATAL_ERROR "Failed to find Python 3")
+  endif()
 
-if(NOT DEFINED Python3_SOABI)
-  message(FATAL_ERROR "Python3_SOABI for ${CMake_TEST_FindPython_COMPONENT} not found")
-endif()
+  if(NOT DEFINED Python3_SOABI)
+    message(FATAL_ERROR "Python3_SOABI for ${CMake_TEST_FindPython_COMPONENT} not found")
+  endif()
 
-if (Python3_Development_FOUND AND Python3_SOABI)
-  Python3_add_library (spam3 MODULE WITH_SOABI ../spam.c)
-  target_compile_definitions (spam3 PRIVATE PYTHON3)
+  if (Python3_Development_FOUND AND Python3_SOABI)
+    Python3_add_library (spam3 MODULE WITH_SOABI ../spam.c)
+    target_compile_definitions (spam3 PRIVATE PYTHON3)
 
-  get_property (suffix TARGET spam3 PROPERTY SUFFIX)
-  if (NOT suffix MATCHES "^.${Python3_SOABI}")
-    message(FATAL_ERROR "Module suffix do not include Python3_SOABI")
+    get_property (suffix TARGET spam3 PROPERTY SUFFIX)
+    if (NOT suffix MATCHES "^.${Python3_SOABI}")
+      message(FATAL_ERROR "Module suffix do not include Python3_SOABI")
+    endif()
   endif()
 endif()
 
+if(CMake_TEST_FindPython2)
+  find_package(Python2 COMPONENTS ${CMake_TEST_FindPython_COMPONENT})
+  if(NOT DEFINED Python2_SOABI)
+    message(FATAL_ERROR "Python2_SOABI for ${CMake_TEST_FindPython_COMPONENT} not found")
+  endif()
 
-find_package(Python2 COMPONENTS ${CMake_TEST_FindPython_COMPONENT})
-if(NOT DEFINED Python2_SOABI)
-  message(FATAL_ERROR "Python2_SOABI for ${CMake_TEST_FindPython_COMPONENT} not found")
-endif()
+  if (Python2_Development_FOUND AND Python2_SOABI)
+    Python2_add_library (spam2 MODULE WITH_SOABI ../spam.c)
+    target_compile_definitions (spam2 PRIVATE PYTHON2)
 
-if (Python2_Development_FOUND AND Python2_SOABI)
-  Python2_add_library (spam2 MODULE WITH_SOABI ../spam.c)
-  target_compile_definitions (spam2 PRIVATE PYTHON2)
-
-  get_property (suffix TARGET spam2 PROPERTY SUFFIX)
-  if (NOT suffix MATCHES "^.${Python2_SOABI}")
-    message(FATAL_ERROR "Module suffix do not include Python2_SOABI")
+    get_property (suffix TARGET spam2 PROPERTY SUFFIX)
+    if (NOT suffix MATCHES "^.${Python2_SOABI}")
+      message(FATAL_ERROR "Module suffix do not include Python2_SOABI")
+    endif()
   endif()
 endif()
diff --git a/Tests/FindPython/VirtualEnv/CMakeLists.txt b/Tests/FindPython/VirtualEnv/CMakeLists.txt
index e2e5bd2..ea742ea 100644
--- a/Tests/FindPython/VirtualEnv/CMakeLists.txt
+++ b/Tests/FindPython/VirtualEnv/CMakeLists.txt
@@ -27,21 +27,23 @@
                  "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
                  -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvDefault.cmake")
 
-add_test(NAME FindPython3.VirtualEnvOnly
-         COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
-                                           --unset=CONDA_PREFIX
-                                           "VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
-                 "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
-                 -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
-add_test(NAME FindPython3.UnsetVirtualEnvOnly
-         COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
-                                           --unset=VIRTUAL_ENV
-                                           --unset=CONDA_PREFIX
-                 "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
-
 add_test(NAME FindPython3.VirtualEnvStandard
          COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
                                            --unset=CONDA_PREFIX
                                            "VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
                  "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
                  -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvStandard.cmake")
+
+if(CMake_TEST_FindPython2)
+  add_test(NAME FindPython3.VirtualEnvOnly
+           COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
+                                             --unset=CONDA_PREFIX
+                                             "VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
+                   "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
+                   -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
+  add_test(NAME FindPython3.UnsetVirtualEnvOnly
+           COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
+                                             --unset=VIRTUAL_ENV
+                                             --unset=CONDA_PREFIX
+                   "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
+endif()
diff --git a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
index 2f7c0db..3a64c31 100644
--- a/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
+++ b/Tests/FindPython/VirtualEnvConda/CMakeLists.txt
@@ -26,21 +26,23 @@
                  "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
                  -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvDefault.cmake")
 
-add_test(NAME FindPython3.VirtualEnvOnlyConda
-         COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
-                                           --unset=VIRTUAL_ENV
-                                           "CONDA_PREFIX=${Python3_VIRTUAL_ENV}"
-                 "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
-                 -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
-add_test(NAME FindPython3.UnsetVirtualEnvOnlyConda
-         COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
-                                           --unset=CONDA_PREFIX
-                                           --unset=VIRTUAL_ENV
-                 "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
-
 add_test(NAME FindPython3.VirtualEnvStandardConda
          COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
                                            --unset=VIRTUAL_ENV
                                            "CONDA_PREFIX=${Python3_VIRTUAL_ENV}"
                  "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
                  -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvStandard.cmake")
+
+if(Cmake_TEST_FindPython2)
+  add_test(NAME FindPython3.VirtualEnvOnlyConda
+           COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
+                                             --unset=VIRTUAL_ENV
+                                             "CONDA_PREFIX=${Python3_VIRTUAL_ENV}"
+                   "${CMAKE_COMMAND}" "-DPYTHON3_VIRTUAL_ENV=${Python3_VIRTUAL_ENV}"
+                   -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
+  add_test(NAME FindPython3.UnsetVirtualEnvOnlyConda
+           COMMAND "${CMAKE_COMMAND}" -E env --unset=PYTHONHOME
+                                             --unset=CONDA_PREFIX
+                                             --unset=VIRTUAL_ENV
+                   "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_LIST_DIR}/VirtualEnvOnly.cmake")
+endif()
diff --git a/Tests/Fortran/CMakeLists.txt b/Tests/Fortran/CMakeLists.txt
index 30ab16b..41d3b4e 100644
--- a/Tests/Fortran/CMakeLists.txt
+++ b/Tests/Fortran/CMakeLists.txt
@@ -29,7 +29,7 @@
 # Pick a module .def file with the properly mangled symbol name.
 set(world_def "")
 if(WIN32 AND NOT CYGWIN)
-  if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU|LCC")
+  if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU|LCC|LLVMFlang")
     set(world_def world_gnu.def)
   elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Intel" OR
       CMAKE_GENERATOR MATCHES "Visual Studio") # Intel plugin
diff --git a/Tests/FortranOnly/CMakeLists.txt b/Tests/FortranOnly/CMakeLists.txt
index 02bf2b8..73312a0 100644
--- a/Tests/FortranOnly/CMakeLists.txt
+++ b/Tests/FortranOnly/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.5...3.25) # Enable CMP0091 and CMP0141.
 project(FortranOnly Fortran)
 message("CTEST_FULL_OUTPUT ")
 
diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt
index ef115e6..df7cda0 100644
--- a/Tests/GeneratorExpression/CMakeLists.txt
+++ b/Tests/GeneratorExpression/CMakeLists.txt
@@ -214,6 +214,22 @@
 set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_NOCONFIG SPECIAL "")
 set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_DEBUG SPECIAL "")
 set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_RELEASE SPECIAL "")
+set_property(TARGET importedFallback2 PROPERTY MAP_IMPORTED_CONFIG_RELWITHDEBINFO SPECIAL "")
+
+add_library(importedFallback3 SHARED IMPORTED)
+set_property(TARGET importedFallback3 PROPERTY IMPORTED_LOCATION_DEBUG debug_loc)
+set_property(TARGET importedFallback3 PROPERTY IMPORTED_LOCATION_RELEASE release_loc)
+set_property(TARGET importedFallback3 PROPERTY IMPORTED_LOCATION fallback_loc)
+set_property(TARGET importedFallback3 PROPERTY IMPORTED_IMPLIB imp_loc)
+set_property(TARGET importedFallback3 PROPERTY MAP_IMPORTED_CONFIG_DEBUG "" DEBUG)
+set_property(TARGET importedFallback3 PROPERTY MAP_IMPORTED_CONFIG_RELEASE "")
+
+add_library(importedFallback4 SHARED IMPORTED)
+set_property(TARGET importedFallback4 PROPERTY IMPORTED_LOCATION fallback_loc)
+set_property(TARGET importedFallback4 PROPERTY IMPORTED_IMPLIB imp_loc)
+
+add_library(importedFallback5 SHARED IMPORTED)
+set_property(TARGET importedFallback5 PROPERTY IMPORTED_IMPLIB imp_loc)
 
 add_library(importedFallback_genex STATIC IMPORTED)
 set_property(TARGET importedFallback_genex PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
@@ -232,7 +248,10 @@
     -Dconfig=$<CONFIGURATION>
     -Dtest_imported_includes=$<TARGET_PROPERTY:imported4,INCLUDE_DIRECTORIES>
     -Dtest_imported_fallback=$<STREQUAL:$<TARGET_FILE_NAME:importedFallback>,fallback_loc>
-    -Dtest_imported_fallback2=$<IF:$<OR:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<AND:$<PLATFORM_ID:Darwin>,$<BOOL:${CMAKE_TAPI}>>>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,special_imp>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,fallback_loc>>
+    -Dtest_imported_fallback2=$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback2>,special_imp>
+    -Dtest_imported_fallback3=$<IF:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback3>,imp_loc>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback3>,fallback_loc>>
+    -Dtest_imported_fallback4=$<IF:$<PLATFORM_ID:Windows,CYGWIN,MSYS>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback4>,imp_loc>,$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback4>,fallback_loc>>
+    -Dtest_imported_fallback5=$<STREQUAL:$<TARGET_LINKER_FILE_NAME:importedFallback5>,imp_loc>
     -Dtest_imported_fallback_genex=$<STREQUAL:$<TARGET_PROPERTY:importedFallback_genex,INTERFACE_COMPILE_DEFINITIONS>,FOOBAR=1>
     -Dtest_alias_file_exe=$<STREQUAL:$<TARGET_FILE:Alias::SomeExe>,$<TARGET_FILE:someexe>>
     -Dtest_alias_file_lib=$<STREQUAL:$<TARGET_FILE:Alias::SomeLib>,$<TARGET_FILE:empty1>>
diff --git a/Tests/GeneratorExpression/check-part3.cmake b/Tests/GeneratorExpression/check-part3.cmake
index 7bb0d85..eda3bc1 100644
--- a/Tests/GeneratorExpression/check-part3.cmake
+++ b/Tests/GeneratorExpression/check-part3.cmake
@@ -20,6 +20,9 @@
 
 check(test_imported_fallback "1")
 check(test_imported_fallback2 "1")
+check(test_imported_fallback3 "1")
+check(test_imported_fallback4 "1")
+check(test_imported_fallback5 "1")
 check(test_imported_fallback_genex "1")
 
 check(test_alias_file_exe "1")
diff --git a/Tests/HIP/ArchSpecial/CMakeLists.txt b/Tests/HIP/ArchSpecial/CMakeLists.txt
new file mode 100644
index 0000000..2bc6bd2
--- /dev/null
+++ b/Tests/HIP/ArchSpecial/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.27)
+project(ArchSpecial HIP)
+
+if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA" AND
+   CMAKE_HIP_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)
+  set(compile_options -Wno-deprecated-gpu-targets)
+endif()
+
+function(verify_output flag)
+  string(REPLACE "-" "_" architectures "${flag}")
+  string(TOUPPER "${architectures}" architectures)
+  set(architectures "${CMAKE_HIP_ARCHITECTURES_${architectures}}")
+  list(TRANSFORM architectures REPLACE "-real" "")
+
+  if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+    set(match_regex "-arch compute_([0-9]+)")
+  endif()
+
+  string(REGEX MATCHALL "${match_regex}" target_cpus "${output}")
+
+  foreach(cpu ${target_cpus})
+    string(REGEX MATCH "${match_regex}" dont_care "${cpu}")
+    list(APPEND command_archs "${CMAKE_MATCH_1}")
+  endforeach()
+
+  list(SORT command_archs)
+  list(REMOVE_DUPLICATES command_archs)
+  if(NOT "${command_archs}" STREQUAL "${architectures}")
+    message(FATAL_ERROR "Architectures used for \"${flag}\" don't match the reference (\"${command_archs}\" != \"${architectures}\").")
+  endif()
+endfunction()
+
+set(try_compile_flags -v ${compile_options})
+
+set(CMAKE_HIP_ARCHITECTURES all)
+try_compile(all_archs_compiles
+  ${CMAKE_CURRENT_BINARY_DIR}/try_compile/all_archs_compiles
+  ${CMAKE_CURRENT_SOURCE_DIR}/main.hip
+  COMPILE_DEFINITIONS ${try_compile_flags}
+  OUTPUT_VARIABLE output
+  )
+verify_output(all)
+
+set(CMAKE_HIP_ARCHITECTURES all-major)
+try_compile(all_major_archs_compiles
+  ${CMAKE_CURRENT_BINARY_DIR}/try_compile/all_major_archs_compiles
+  ${CMAKE_CURRENT_SOURCE_DIR}/main.hip
+  COMPILE_DEFINITIONS ${try_compile_flags}
+  OUTPUT_VARIABLE output
+  )
+verify_output(all-major)
+
+set(CMAKE_HIP_ARCHITECTURES native)
+try_compile(native_archs_compiles
+  ${CMAKE_CURRENT_BINARY_DIR}/try_compile/native_archs_compiles
+  ${CMAKE_CURRENT_SOURCE_DIR}/main.hip
+  COMPILE_DEFINITIONS ${try_compile_flags}
+  OUTPUT_VARIABLE output
+  )
+verify_output(native)
+
+if(all_archs_compiles AND all_major_archs_compiles AND native_archs_compiles)
+  set(CMAKE_HIP_ARCHITECTURES all)
+  add_executable(HIPArchSpecial main.hip)
+  target_compile_options(HIPArchSpecial PRIVATE ${compile_options})
+endif()
diff --git a/Tests/HIP/ArchSpecial/main.hip b/Tests/HIP/ArchSpecial/main.hip
new file mode 100644
index 0000000..5047a34
--- /dev/null
+++ b/Tests/HIP/ArchSpecial/main.hip
@@ -0,0 +1,3 @@
+int main()
+{
+}
diff --git a/Tests/HIP/ArchitectureOff/CMakeLists.txt b/Tests/HIP/ArchitectureOff/CMakeLists.txt
index bccb3b4..18f3a1e 100644
--- a/Tests/HIP/ArchitectureOff/CMakeLists.txt
+++ b/Tests/HIP/ArchitectureOff/CMakeLists.txt
@@ -2,7 +2,17 @@
 project(HIPArchitecture HIP)
 
 # Make sure CMake doesn't pass architectures if HIP_ARCHITECTURES is OFF.
-string(APPEND CMAKE_HIP_FLAGS " --offload-arch=gfx908")
+set(CMAKE_HIP_ARCHITECTURES OFF)
+
+# Pass our own architecture flags instead.
+if(CMAKE_HIP_PLATFORM STREQUAL "amd")
+  string(APPEND CMAKE_HIP_FLAGS " --offload-arch=gfx908")
+elseif(CMAKE_HIP_PLATFORM STREQUAL "nvidia")
+  string(APPEND CMAKE_HIP_FLAGS " -arch=sm_52")
+endif()
 
 add_executable(HIPOnlyArchitectureOff main.hip)
-set_property(TARGET HIPOnlyArchitectureOff PROPERTY HIP_ARCHITECTURES OFF)
+get_property(hip_archs TARGET HIPOnlyArchitectureOff PROPERTY HIP_ARCHITECTURES)
+if(NOT hip_archs STREQUAL "OFF")
+  message(FATAL_ERROR "CMAKE_HIP_ARCHITECTURES did not initialize HIP_ARCHITECTURES")
+endif()
diff --git a/Tests/HIP/CMakeLists.txt b/Tests/HIP/CMakeLists.txt
index 9499be8..f1e2e51 100644
--- a/Tests/HIP/CMakeLists.txt
+++ b/Tests/HIP/CMakeLists.txt
@@ -5,11 +5,17 @@
 endmacro ()
 
 add_hip_test_macro(HIP.ArchitectureOff HIPOnlyArchitectureOff)
+if(CMake_TEST_HIP STREQUAL "nvidia")
+  add_hip_test_macro(HIP.ArchSpecial HIPArchSpecial)
+endif()
 add_hip_test_macro(HIP.CompileFlags HIPOnlyCompileFlags)
 add_hip_test_macro(HIP.EnableStandard HIPEnableStandard)
 add_hip_test_macro(HIP.InferHipLang1 HIPInferHipLang1)
 add_hip_test_macro(HIP.InferHipLang2 HIPInferHipLang2)
-add_hip_test_macro(HIP.MathFunctions HIPOnlyMathFunctions)
+if(CMake_TEST_HIP STREQUAL "amd")
+  # The NVIDIA CUDA compiler cannot handle device lambda markup.
+  add_hip_test_macro(HIP.MathFunctions HIPOnlyMathFunctions)
+endif()
 add_hip_test_macro(HIP.MixedLanguage HIPMixedLanguage)
 add_hip_test_macro(HIP.TryCompile HIPOnlyTryCompile)
 add_hip_test_macro(HIP.WithDefs HIPOnlyWithDefs)
diff --git a/Tests/HIP/CompileFlags/CMakeLists.txt b/Tests/HIP/CompileFlags/CMakeLists.txt
index c808313..a3adb7b 100644
--- a/Tests/HIP/CompileFlags/CMakeLists.txt
+++ b/Tests/HIP/CompileFlags/CMakeLists.txt
@@ -3,6 +3,11 @@
 
 add_executable(HIPOnlyCompileFlags main.hip)
 
-set_property(TARGET HIPOnlyCompileFlags PROPERTY HIP_ARCHITECTURES gfx803)
+if(CMAKE_HIP_PLATFORM STREQUAL "amd")
+  set(hip_archs gfx803)
+elseif(CMAKE_HIP_PLATFORM STREQUAL "nvidia")
+  set(hip_archs 52)
+endif()
+set_property(TARGET HIPOnlyCompileFlags PROPERTY HIP_ARCHITECTURES ${hip_archs})
 
 target_compile_options(HIPOnlyCompileFlags PRIVATE -DALWAYS_DEFINE)
diff --git a/Tests/HIP/MathFunctions/CMakeLists.txt b/Tests/HIP/MathFunctions/CMakeLists.txt
index 81e3ddb..7e768e8 100644
--- a/Tests/HIP/MathFunctions/CMakeLists.txt
+++ b/Tests/HIP/MathFunctions/CMakeLists.txt
@@ -14,5 +14,9 @@
 #    that hip needs that inject support for __half support
 #
 add_executable(HIPOnlyMathFunctions main.hip)
-target_compile_options(HIPOnlyMathFunctions PRIVATE -Werror)
+if(CMAKE_HIP_COMPILER_ID STREQUAL "NVIDIA")
+  target_compile_options(HIPOnlyMathFunctions PRIVATE "SHELL:-Werror all-warnings")
+elseif(CMAKE_HIP_COMPILER_ID STREQUAL "Clang")
+  target_compile_options(HIPOnlyMathFunctions PRIVATE "-Werror")
+endif()
 target_compile_features(HIPOnlyMathFunctions PRIVATE hip_std_14)
diff --git a/Tests/HIP/MixedLanguage/CMakeLists.txt b/Tests/HIP/MixedLanguage/CMakeLists.txt
index 4f6dd3b..af58ade 100644
--- a/Tests/HIP/MixedLanguage/CMakeLists.txt
+++ b/Tests/HIP/MixedLanguage/CMakeLists.txt
@@ -17,3 +17,7 @@
 
 add_executable(HIPMixedLanguage main.cxx)
 target_link_libraries(HIPMixedLanguage PRIVATE MixedStaticLib MixedSharedLib)
+
+add_executable(HIPMixedLanguageCXX main.cxx)
+target_link_libraries(HIPMixedLanguageCXX PRIVATE MixedStaticLib MixedSharedLib)
+set_property(TARGET HIPMixedLanguageCXX PROPERTY LINKER_LANGUAGE CXX)
diff --git a/Tests/HIP/TryCompile/CMakeLists.txt b/Tests/HIP/TryCompile/CMakeLists.txt
index 92a834c..1022a58 100644
--- a/Tests/HIP/TryCompile/CMakeLists.txt
+++ b/Tests/HIP/TryCompile/CMakeLists.txt
@@ -4,7 +4,12 @@
 #Goal for this example:
 # Verify try_compile with HIP language works
 set(CMAKE_HIP_STANDARD 14)
-set(CMAKE_HIP_ARCHITECTURES gfx803 gfx900)
+
+if(CMAKE_HIP_PLATFORM STREQUAL "amd")
+  set(CMAKE_HIP_ARCHITECTURES gfx803 gfx900)
+elseif(CMAKE_HIP_PLATFORM STREQUAL "nvidia")
+  set(CMAKE_HIP_ARCHITECTURES 52)
+endif()
 
 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
 try_compile(result "${CMAKE_CURRENT_BINARY_DIR}"
diff --git a/Tests/HIP/WithDefs/CMakeLists.txt b/Tests/HIP/WithDefs/CMakeLists.txt
index 270f957..5602111 100644
--- a/Tests/HIP/WithDefs/CMakeLists.txt
+++ b/Tests/HIP/WithDefs/CMakeLists.txt
@@ -2,12 +2,11 @@
 cmake_minimum_required(VERSION 3.18)
 project (WithDefs HIP)
 
-set(CMAKE_HIP_ARCHITECTURES OFF)
 set(release_compile_defs DEFREL)
 
 #Goal for this example:
-#build a executable that needs to be passed a complex define through add_definitions
-#this verifies we can pass C++ style attributes to hipcc
+#Build an executable that needs to be passed a complex define through add_definitions.
+#Verify we can pass C++ style attributes to the HIP compiler.
 add_definitions("-DPACKED_DEFINE=[[gnu::packed]]")
 
 add_executable(HIPOnlyWithDefs main.hip.cpp)
@@ -17,9 +16,8 @@
 
 target_compile_options(HIPOnlyWithDefs
   PRIVATE
-    --offload-arch=gfx900
     -DFLAG_COMPILE_LANG_$<COMPILE_LANGUAGE>
-    $<$<HIP_COMPILER_ID:Clang>:-DFLAG_LANG_IS_HIP=$<COMPILE_LANGUAGE:HIP>> # Host-only defines are possible only on NVCC.
+    -DFLAG_LANG_IS_HIP=$<COMPILE_LANGUAGE:HIP>
   )
 
 target_compile_definitions(HIPOnlyWithDefs
diff --git a/Tests/HIP/WithDefs/main.hip.cpp b/Tests/HIP/WithDefs/main.hip.cpp
index a8f2d18..b69fa02 100644
--- a/Tests/HIP/WithDefs/main.hip.cpp
+++ b/Tests/HIP/WithDefs/main.hip.cpp
@@ -51,6 +51,10 @@
 #  undef PACKED_DEFINE
 #  define PACKED_DEFINE
 #endif
+#ifdef __NVCC__
+#  undef PACKED_DEFINE
+#  define PACKED_DEFINE
+#endif
 struct PACKED_DEFINE result_type
 {
   bool valid;
diff --git a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
index 41bd6f5..2a8a152 100644
--- a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
+++ b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
@@ -21,6 +21,13 @@
     endforeach()
   endforeach()
 endforeach()
+if(CMAKE_Fortran_COMPILER_ID STREQUAL "LLVMFlang")
+  # LLVMFlang does not actually define these, so inject them
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "-D_MT")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "-D_MT;-D_DLL")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "-D_MT;-D_DEBUG")
+  set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "-D_MT;-D_DEBUG;-D_DLL")
+endif()
 string(APPEND CMAKE_Fortran_FLAGS " -w")
 
 function(verify_combinations threads lang src)
diff --git a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt
index 0df05b9..0f2067a 100644
--- a/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt
+++ b/Tests/Module/WriteCompilerDetectionHeader/CMakeLists.txt
@@ -55,7 +55,7 @@
 # detailed features tables, not just meta-features
 
 if (CMAKE_C_COMPILE_FEATURES)
-  if (NOT CMAKE_C_COMPILER_ID MATCHES "^(LCC|Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$")
+  if (NOT CMAKE_C_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$")
     set(C_expected_features ${CMAKE_C_COMPILE_FEATURES})
     list(FILTER C_expected_features EXCLUDE REGEX "^c_std_[0-9][0-9]")
   endif()
@@ -98,7 +98,7 @@
 endif()
 
 if (CMAKE_CXX_COMPILE_FEATURES)
-  if (NOT CMAKE_CXX_COMPILER_ID MATCHES "^(LCC|Cray|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang)$")
+  if (NOT CMAKE_CXX_COMPILER_ID MATCHES "^(LCC|Cray|CrayClang|PGI|NVHPC|XL|XLClang|IBMClang|IntelLLVM|Fujitsu|FujitsuClang|OrangeC)$")
     set(CXX_expected_features ${CMAKE_CXX_COMPILE_FEATURES})
     list(FILTER CXX_expected_features EXCLUDE REGEX "^cxx_std_[0-9][0-9]")
   endif()
diff --git a/Tests/OutDir/OutDir.cmake b/Tests/OutDir/OutDir.cmake
index 2a003b8..c42dc49 100644
--- a/Tests/OutDir/OutDir.cmake
+++ b/Tests/OutDir/OutDir.cmake
@@ -1,5 +1,5 @@
 set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
-set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a" ".so" ".sl" ".dylib" ".dll.a")
+set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".a" ".so" ".sl" ".dylib" ".dll.a" ".l")
 
 find_library(TESTC1_LIB
   NAMES testc1 testc1_test_debug_postfix
diff --git a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
index 75130f2..9f65219 100644
--- a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
+++ b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
@@ -10,6 +10,9 @@
 if(RunCMake_GENERATOR MATCHES "Watcom WMake|Borland Makefiles")
   return()
 endif()
+if(CMAKE_CXX_COMPILER_ID STREQUAL "OrangeC")
+  return()
+endif()
 if(RunCMake_GENERATOR MATCHES "Ninja|Visual Studio" AND
    CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
   set(EXPORTS TRUE)
diff --git a/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake b/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake
new file mode 100644
index 0000000..3ee9be9
--- /dev/null
+++ b/Tests/RunCMake/Autogen/AutoMocExecutableConfig.cmake
@@ -0,0 +1,15 @@
+include(MocExample.cmake)
+
+if(NOT TARGET Qt${with_qt_version}::moc)
+  message(FATAL_ERROR "Qt${with_qt_version}::moc not found")
+endif()
+
+get_target_property(moc_location Qt${with_qt_version}::moc IMPORTED_LOCATION)
+set_target_properties(dummy PROPERTIES AUTOMOC_MOC_OPTIONS "EXE_PATH=${moc_location}")
+
+add_executable(mymoc $<$<CONFIG:Debug>:exe_debug.cpp>
+                     $<$<CONFIG:Release>:exe_release.cpp>
+                     $<$<CONFIG:RelWithDebInfo>:exe_relwithdebinfo.cpp>
+)
+
+set_target_properties(dummy PROPERTIES AUTOMOC_EXECUTABLE $<TARGET_FILE:mymoc>)
diff --git a/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake b/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake
new file mode 100644
index 0000000..0e46420
--- /dev/null
+++ b/Tests/RunCMake/Autogen/AutoRccExecutableConfig.cmake
@@ -0,0 +1,15 @@
+include(RccExample.cmake)
+
+if(NOT TARGET Qt${with_qt_version}::rcc)
+  message(FATAL_ERROR "Qt${with_qt_version}::rcc not found")
+endif()
+
+get_target_property(rcc_location Qt${with_qt_version}::rcc IMPORTED_LOCATION)
+set_target_properties(dummy PROPERTIES AUTORCC_OPTIONS "EXE_PATH=${rcc_location}")
+
+add_executable(myrcc $<$<CONFIG:Debug>:exe_debug.cpp>
+                     $<$<CONFIG:Release>:exe_release.cpp>
+                     $<$<CONFIG:RelWithDebInfo>:exe_relwithdebinfo.cpp>
+)
+
+set_target_properties(dummy PROPERTIES AUTORCC_EXECUTABLE $<TARGET_FILE:myrcc>)
diff --git a/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake b/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake
new file mode 100644
index 0000000..55b88b8
--- /dev/null
+++ b/Tests/RunCMake/Autogen/AutoUicExecutableConfig.cmake
@@ -0,0 +1,15 @@
+include(UicExample.cmake)
+
+if(NOT TARGET Qt${with_qt_version}::uic)
+  message(FATAL_ERROR "Qt${with_qt_version}::uic not found")
+endif()
+
+get_target_property(uic_location Qt${with_qt_version}::uic IMPORTED_LOCATION)
+set_target_properties(dummy PROPERTIES AUTOUIC_OPTIONS "EXE_PATH=${uic_location}")
+
+add_executable(myuic $<$<CONFIG:Debug>:exe_debug.cpp>
+                     $<$<CONFIG:Release>:exe_release.cpp>
+                     $<$<CONFIG:RelWithDebInfo>:exe_relwithdebinfo.cpp>
+)
+
+set_target_properties(dummy PROPERTIES AUTOUIC_EXECUTABLE $<TARGET_FILE:myuic>)
diff --git a/Tests/RunCMake/Autogen/MocExample.cmake b/Tests/RunCMake/Autogen/MocExample.cmake
new file mode 100644
index 0000000..f06f8f6
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MocExample.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui)
+
+add_library(dummy STATIC example.cpp)
+target_link_libraries(dummy Qt${with_qt_version}::Core
+                            Qt${with_qt_version}::Widgets
+                            Qt${with_qt_version}::Gui)
+
+set_target_properties(dummy PROPERTIES AUTOMOC ON)
diff --git a/Tests/RunCMake/Autogen/MyWindow.cpp b/Tests/RunCMake/Autogen/MyWindow.cpp
new file mode 100644
index 0000000..d87c2e9
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MyWindow.cpp
@@ -0,0 +1,7 @@
+#include "MyWindow.h"
+
+MyWindow::MyWindow(QWidget* parent)
+  : QWidget(parent)
+{
+  this->m_ui.setupUi(this);
+}
diff --git a/Tests/RunCMake/Autogen/MyWindow.h b/Tests/RunCMake/Autogen/MyWindow.h
new file mode 100644
index 0000000..c267610
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MyWindow.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include <QWidget>
+
+#include "ui_MyWindow.h"
+
+class MyWindow : public QWidget
+{
+  Q_OBJECT
+
+public:
+  explicit MyWindow(QWidget* parent = nullptr);
+
+private:
+  Ui::MyWindow m_ui;
+};
diff --git a/Tests/RunCMake/Autogen/MyWindow.ui b/Tests/RunCMake/Autogen/MyWindow.ui
new file mode 100644
index 0000000..fbf294c
--- /dev/null
+++ b/Tests/RunCMake/Autogen/MyWindow.ui
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MyWindow</class>
+ <widget class="QWidget" name="MyWindow"/>
+</ui>
diff --git a/Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt b/Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt
new file mode 100644
index 0000000..6024984
--- /dev/null
+++ b/Tests/RunCMake/Autogen/QtAutoMocDeps-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at QtSubDir1/CMakeLists\.txt:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0116 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.$
diff --git a/Tests/RunCMake/Autogen/QtAutoMocDeps.cmake b/Tests/RunCMake/Autogen/QtAutoMocDeps.cmake
new file mode 100644
index 0000000..fc3b2f2
--- /dev/null
+++ b/Tests/RunCMake/Autogen/QtAutoMocDeps.cmake
@@ -0,0 +1,23 @@
+enable_language(CXX)
+
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui)
+
+set(CMAKE_AUTOMOC ON)
+
+add_library(simple_lib SHARED simple_lib.cpp)
+add_executable(app_with_qt app.cpp app_qt.cpp)
+target_link_libraries(app_with_qt PRIVATE simple_lib Qt${with_qt_version}::Core)
+
+if(${with_qt_version}Widgets_DIR)
+  if(with_qt_version STREQUAL 5)
+    qt5_wrap_ui(_headers MyWindow.ui)
+  else()
+    qt_wrap_ui(_headers MyWindow.ui)
+  endif()
+  add_executable(app_with_widget app.cpp MyWindow.cpp ${_headers})
+  target_link_libraries(app_with_widget PRIVATE Qt${with_qt_version}::Widgets)
+  target_include_directories(app_with_widget PRIVATE "${CMAKE_BINARY_DIR}")
+endif()
+add_subdirectory(QtSubDir1)
+add_subdirectory(QtSubDir2)
+add_subdirectory(QtSubDir3)
diff --git a/Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt b/Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt
new file mode 100644
index 0000000..f215b9e
--- /dev/null
+++ b/Tests/RunCMake/Autogen/QtSubDir1/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_policy(SET CMP0116 OLD)
+
+add_executable(sub_exe_1 ../app.cpp)
+target_link_libraries(sub_exe_1 PRIVATE Qt${with_qt_version}::Core)
diff --git a/Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt b/Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt
new file mode 100644
index 0000000..cccb1e0
--- /dev/null
+++ b/Tests/RunCMake/Autogen/QtSubDir2/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_policy(SET CMP0116 NEW)
+
+add_executable(sub_exe_2 ../app.cpp)
+target_link_libraries(sub_exe_2 PRIVATE Qt${with_qt_version}::Core)
diff --git a/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt b/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt
new file mode 100644
index 0000000..d268bfe
--- /dev/null
+++ b/Tests/RunCMake/Autogen/QtSubDir3/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_executable(sub_exe_3 ../app.cpp)
+target_link_libraries(sub_exe_3 PRIVATE Qt${with_qt_version}::Core)
diff --git a/Tests/RunCMake/Autogen/RccExample.cmake b/Tests/RunCMake/Autogen/RccExample.cmake
new file mode 100644
index 0000000..4554eb0
--- /dev/null
+++ b/Tests/RunCMake/Autogen/RccExample.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui)
+
+add_library(dummy STATIC example.cpp data.qrc)
+target_link_libraries(dummy Qt${with_qt_version}::Core
+                            Qt${with_qt_version}::Widgets
+                            Qt${with_qt_version}::Gui)
+
+set_target_properties(dummy PROPERTIES AUTORCC ON)
diff --git a/Tests/RunCMake/Autogen/RunCMakeTest.cmake b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
index 4fe9406..38987b9 100644
--- a/Tests/RunCMake/Autogen/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Autogen/RunCMakeTest.cmake
@@ -122,4 +122,336 @@
       endblock()
     endif()
   endif()
+
+  if(RunCMake_GENERATOR MATCHES "Make|Ninja")
+    block()
+      if(QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
+        if (RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
+          set(config_list Debug Release RelWithDebInfo)
+        else()
+          set(config_list single-config)
+        endif()
+        foreach(config IN ITEMS ${config_list})
+          block()
+            if (config STREQUAL "single-config")
+              set(config_suffix "")
+            else()
+              set(config_suffix "_${config}")
+            endif()
+            set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps${config_suffix}-build)
+            run_cmake(QtAutoMocDeps)
+            set(RunCMake_TEST_NO_CLEAN 1)
+            # Build the project.
+            if (config STREQUAL "single-config")
+              set(config_param "")
+            else()
+              set(config_param "--config ${config}")
+            endif()
+            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
+            # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
+            # for app_with_qt target.
+            file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
+            set(RunCMake_TEST_NOT_EXPECT_stdout "Automatic MOC for target app_with_qt|\
+Automatic MOC for target sub_exe_1|\
+Automatic MOC for target sub_exe_2")
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't execute AUTOMOC for 'app_with_qt', 'sub_exe_1' and 'sub_exe_2'")
+            # Build and assert that AUTOMOC was not run for app_with_qt, sub_exe_1 and sub_exe_2.
+            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose ${config_param})
+            unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+            unset(RunCMake_TEST_NOT_EXPECT_stdout)
+
+            macro(check_file_exists file)
+              if (EXISTS "${file}")
+                set(check_result "PASSED")
+                set(message_type "STATUS")
+              else()
+                set(check_result "FAILED")
+                set(message_type "FATAL_ERROR")
+              endif()
+
+              message(${message_type} "QtAutoMocDeps-build-\"${file}\" was generated - ${check_result}")
+            endmacro()
+
+            check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/deps${config_suffix}")
+            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/deps${config_suffix}")
+            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/deps${config_suffix}")
+
+            check_file_exists("${RunCMake_TEST_BINARY_DIR}/app_with_qt_autogen/timestamp${config_suffix}")
+            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir1/sub_exe_1_autogen/timestamp${config_suffix}")
+            check_file_exists("${RunCMake_TEST_BINARY_DIR}/QtSubDir2/sub_exe_2_autogen/timestamp${config_suffix}")
+
+            # Touch a header file to make sure an automoc dependency cycle is not introduced.
+            file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-First build after touch to detect dependency cycle")
+            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
+            # Need to run a second time to hit the dependency cycle.
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-Don't hit dependency cycle")
+            run_cmake_command(QtAutoMocDeps-build ${CMAKE_COMMAND} --build . --verbose)
+          endblock()
+        endforeach()
+      endif()
+    endblock()
+  endif()
+
+  function(run_make_program dir)
+    execute_process(
+      COMMAND "${RunCMake_MAKE_PROGRAM}" ${ARGN}
+      WORKING_DIRECTORY "${dir}"
+      OUTPUT_VARIABLE make_program_stdout
+      ERROR_VARIABLE make_program_stderr
+      RESULT_VARIABLE make_program_result
+      )
+      if (NOT DEFINED RunMakeProgram_expected_result)
+        set(RunMakeProgram_expected_result 0)
+      endif()
+      if(NOT "${make_program_result}" MATCHES "${RunMakeProgram_expected_result}")
+        message(STATUS "
+============ beginning of ${RunCMake_MAKE_PROGRAM}'s stdout ============
+${make_program_stdout}
+=============== end of ${RunCMake_MAKE_PROGRAM}'s stdout ===============
+")
+        message(STATUS "
+============ beginning of ${RunCMake_MAKE_PROGRAM}'s stderr ============
+${make_program_stderr}
+=============== end of ${RunCMake_MAKE_PROGRAM}'s stderr ===============
+")
+        message(FATAL_ERROR
+                "top ${RunCMake_MAKE_PROGRAM} build failed exited with status ${make_program_result}")
+    endif()
+    set(make_program_stdout "${make_program_stdout}" PARENT_SCOPE)
+  endfunction(run_make_program)
+
+  function(count_substring STRING SUBSTRING COUNT_VAR)
+      string(LENGTH "${STRING}" STRING_LENGTH)
+      string(LENGTH "${SUBSTRING}" SUBSTRING_LENGTH)
+      if (SUBSTRING_LENGTH EQUAL 0)
+          message(FATAL_ERROR "SUBSTRING_LENGTH is 0")
+      endif()
+
+      if (STRING_LENGTH EQUAL 0)
+          message(FATAL_ERROR "STRING_LENGTH is 0")
+      endif()
+
+      if (STRING_LENGTH LESS SUBSTRING_LENGTH)
+          message(FATAL_ERROR "STRING_LENGTH is less than SUBSTRING_LENGTH")
+      endif()
+
+      set(COUNT 0)
+      string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START)
+      while(SUBSTRING_START GREATER_EQUAL 0)
+          math(EXPR COUNT "${COUNT} + 1")
+          math(EXPR SUBSTRING_START "${SUBSTRING_START} + ${SUBSTRING_LENGTH}")
+          string(SUBSTRING "${STRING}" ${SUBSTRING_START} -1 STRING)
+          string(FIND "${STRING}" "${SUBSTRING}" SUBSTRING_START)
+      endwhile()
+
+      set(${COUNT_VAR} ${COUNT} PARENT_SCOPE)
+  endfunction()
+
+  function(expect_only_once make_program_stdout expected_output test_name)
+    count_substring("${make_program_stdout}" "${expected_output}" count)
+    if(NOT count EQUAL 1)
+      message(STATUS "${test_name}-expect_only_once - FAILED")
+      message(FATAL_ERROR "Expected to find ${expected_output} exactly once in ${make_program_stdout} but found ${count} occurrences of ${expected_output}")
+    else()
+      message(STATUS "${test_name}-expect_only_once - PASSED")
+    endif()
+  endfunction()
+
+  function(expect_n_times string_to_check expected_output expected_count test_name)
+    count_substring("${string_to_check}" "${expected_output}" count)
+    if(NOT count EQUAL ${expected_count})
+      message(STATUS "${test_name}-expect_${expected_count}_times - FAILED")
+      message(FATAL_ERROR "Expected to find ${expected_output} exactly ${expected_count} times in ${string_to_check} but found ${count} occurrences of ${expected_output}")
+    else()
+      message(STATUS "${test_name}-expect_${expected_count}_times - PASSED")
+    endif()
+  endfunction()
+
+  function(not_expect make_program_stdout unexpected_output test_name)
+    count_substring("${make_program_stdout}" "${unexpected_output}" count)
+    if(NOT count EQUAL 0)
+      message(STATUS "${test_name}-not_expect - FAILED")
+      message(FATAL_ERROR "Expected to find ${unexpected_output} exactly 0 times in ${make_program_stdout} but found ${count} occurrences of ${unexpected_output}")
+    else()
+      message(STATUS "${test_name}-not_expect - PASSED")
+    endif()
+  endfunction()
+
+  if (QtCore_VERSION VERSION_GREATER_EQUAL 5.15.0)
+    foreach(exe IN ITEMS Moc Uic Rcc)
+      if(RunCMake_GENERATOR MATCHES "Ninja Multi-Config")
+        block()
+          set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
+          set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-multi-config-build)
+          run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
+          unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+          set(RunCMake_TEST_NO_CLEAN 1)
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+              set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*")
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_running_exe_${config}")
+              run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config})
+            endblock()
+          endforeach()
+          set(RunCMake_TEST_EXPECT_stdout "ninja: no work to do")
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-expect_no_work_to_do")
+              run_cmake_command(Auto${exe}ExecutableConfig-multi-config-build ${CMAKE_COMMAND} --build . --config ${config})
+            endblock()
+          endforeach()
+        endblock()
+        block()
+          set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build)
+          run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+              run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${config}.ninja)
+
+              set(expected_output "running_exe_${config}")
+              expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}")
+
+              foreach(sub_config IN ITEMS Debug Release RelWithDebInfo)
+                if(NOT sub_config STREQUAL config)
+                  set(unexpected_output "running_exe_${sub_config}")
+                  not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig-${config}-${unexpected_output}")
+                endif()
+              endforeach()
+
+              if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+                set(expected_output "cmake_autogen")
+              else()
+                set(expected_output "cmake_autorcc")
+              endif()
+              expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig-${config}-${expected_output}")
+            endblock()
+          endforeach()
+        endblock()
+        block()
+          foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+            foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
+              block()
+                set(TEST_SUFFIX "-CrossConfig-${ninja_config}-${target_config}")
+                set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build)
+                set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX})
+                run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all -DCMAKE_DEFAULT_BUILD_TYPE=${ninja_config})
+                unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+
+                run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja dummy:${target_config})
+
+                set(expected_output "running_exe_${ninja_config}")
+                expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}")
+
+                foreach(sub_config IN ITEMS Debug Release RelWithDebInfo)
+                  if(NOT sub_config STREQUAL ninja_config)
+                    set(unexpected_output "running_exe_${sub_config}")
+                    not_expect("${make_program_stdout}" "${unexpected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${unexpected_output}")
+                  endif()
+                endforeach()
+
+                if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+                  set(expected_output "cmake_autogen")
+                else()
+                  set(expected_output "cmake_autorcc")
+                endif()
+                expect_only_once("${make_program_stdout}" "${expected_output}" "Auto${exe}ExecutableConfig${TEST_SUFFIX}-${expected_output}")
+              endblock()
+            endforeach()
+          endforeach()
+        endblock()
+        block()
+          foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+            set(TEST_SUFFIX "-CrossConfig-${ninja_config}-all-all")
+            set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig${TEST_SUFFIX}-build)
+            set(RunCMake_TEST_VARIANT_DESCRIPTION ${TEST_SUFFIX})
+            run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_CROSS_CONFIGS=all)
+            unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+            run_make_program(${RunCMake_TEST_BINARY_DIR} --verbose -f build-${ninja_config}.ninja all:all)
+          endforeach()
+        endblock()
+      elseif (RunCMake_GENERATOR MATCHES "Ninja|Make")
+        block()
+          set(RunCMake_TEST_BINARY_DIR  ${RunCMake_BINARY_DIR}/Auto${exe}ExecutableConfig-build)
+          foreach(config IN ITEMS Debug Release RelWithDebInfo)
+            block()
+              set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}")
+              run_cmake_with_options(Auto${exe}ExecutableConfig ${RunCMake_TEST_OPTIONS} -DCMAKE_BUILD_TYPE=${config} -DCMAKE_AUTOGEN_VERBOSE=ON)
+              unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+              set(RunCMake_TEST_NO_CLEAN 1)
+              set(RunCMake_TEST_EXPECT_stdout ".*running_exe_${config}*")
+              run_cmake_command(Auto${exe}ExecutableConfig-${config}-build ${CMAKE_COMMAND} --build .)
+            endblock()
+          endforeach()
+        endblock()
+      endif()
+    endforeach()
+  endif()
+
+  # Visual Studio specific dependency tests
+  if (RunCMake_GENERATOR MATCHES "Visual Studio")
+      foreach(exe IN ITEMS Moc Uic Rcc)
+          block()
+            set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build)
+            set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
+            run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
+            unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+            set(RunCMake_TEST_NO_CLEAN 1)
+            foreach(config IN ITEMS Debug Release RelWithDebInfo)
+              block()
+                set(RunCMake_TEST_VARIANT_DESCRIPTION "-${config}-first-build")
+                run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config})
+              endblock()
+            endforeach()
+            foreach(config IN ITEMS Debug Release RelWithDebInfo)
+              block()
+                if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+                  set(RunCMake_TEST_NOT_EXPECT_stdout "Auto${exe}")
+                  set(not_expect_descripton "Auto${exe}")
+                else ()
+                  set(RunCMake_TEST_NOT_EXPECT_stdout "qrc_data.cpp|Auto${exe}")
+                  set(not_expect_descripton "qrc_data.cpp_and_Auto${exe}")
+                endif()
+                set(RunCMake_TEST_VARIANT_DESCRIPTION "-second-build-${config}_expect_no_${not_expect_descripton}")
+                run_cmake_command(${exe}Example-build ${CMAKE_COMMAND} --build . --config ${config})
+              endblock()
+            endforeach()
+          endblock()
+      endforeach()
+  endif()
+
+  if (RunCMake_GENERATOR MATCHES "Xcode")
+    foreach(exe IN ITEMS Moc Uic Rcc)
+      block()
+        set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${exe}Example-build)
+        set(RunCMake_TEST_VARIANT_DESCRIPTION "-CMake-configure")
+        set(RunCMake_TEST_EXPECT_stderr ".*")
+        run_cmake_with_options(${exe}Example ${RunCMake_TEST_OPTIONS} -DCMAKE_AUTOGEN_VERBOSE=ON)
+        set(RunCMake_TEST_NO_CLEAN 1)
+        set(RunCMake_MAKE_PROGRAM ${CMAKE_COMMAND})
+        run_make_program(${RunCMake_TEST_BINARY_DIR}  --build . --config Debug)
+        if (exe STREQUAL "Moc")
+          set(expected_count 16)
+        elseif (exe STREQUAL "Uic")
+          set(expected_count 4)
+        else()
+          set(expected_count 12)
+        endif()
+        expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}")
+        expect_n_times("${make_program_stdout}" "Auto${exe}:" ${expected_count} "${exe}Example-build-Auto${exe}")
+
+        if (exe STREQUAL "Moc" OR exe STREQUAL "Uic")
+          expect_n_times("${make_program_stdout}" "AutoGen:" 20 "${exe}Example-build-AutoGen:")
+        endif()
+
+        foreach(config IN ITEMS Debug Release RelWithDebInfo)
+          block()
+            run_make_program(${RunCMake_TEST_BINARY_DIR} --build . --config ${config})
+            not_expect("${make_program_stdout}" "Auto${exe}" "${exe}Example-${config}_Auto${exe}")
+            not_expect("${make_program_stdout}" "AutoGen:" "${exe}Example-${config}_AutoGen")
+          endblock()
+        endforeach()
+      endblock()
+    endforeach()
+  endif()
 endif ()
diff --git a/Tests/RunCMake/Autogen/UicExample.cmake b/Tests/RunCMake/Autogen/UicExample.cmake
new file mode 100644
index 0000000..4b1f8c1
--- /dev/null
+++ b/Tests/RunCMake/Autogen/UicExample.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+find_package(Qt${with_qt_version} REQUIRED COMPONENTS Core Widgets Gui)
+
+add_library(dummy STATIC example_ui.cpp uiA.ui)
+target_link_libraries(dummy Qt${with_qt_version}::Core
+                            Qt${with_qt_version}::Widgets
+                            Qt${with_qt_version}::Gui)
+
+set_target_properties(dummy PROPERTIES AUTOUIC ON)
diff --git a/Tests/RunCMake/Autogen/app.cpp b/Tests/RunCMake/Autogen/app.cpp
new file mode 100644
index 0000000..57380e4
--- /dev/null
+++ b/Tests/RunCMake/Autogen/app.cpp
@@ -0,0 +1,6 @@
+int main(int argc, char* argv[])
+{
+  (void)argc;
+  (void)argv;
+  return 0;
+}
diff --git a/Tests/RunCMake/Autogen/app_qt.cpp b/Tests/RunCMake/Autogen/app_qt.cpp
new file mode 100644
index 0000000..302c672
--- /dev/null
+++ b/Tests/RunCMake/Autogen/app_qt.cpp
@@ -0,0 +1,11 @@
+#include <QObject>
+
+class Mango : public QObject
+{
+  Q_OBJECT
+public:
+Q_SIGNALS:
+  void eatFruit();
+};
+
+#include "app_qt.moc"
diff --git a/Tests/RunCMake/Autogen/data.qrc b/Tests/RunCMake/Autogen/data.qrc
new file mode 100644
index 0000000..9bd068c
--- /dev/null
+++ b/Tests/RunCMake/Autogen/data.qrc
@@ -0,0 +1,4 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/res/affine">
+</qresource>
+</RCC>
diff --git a/Tests/RunCMake/Autogen/example.cpp b/Tests/RunCMake/Autogen/example.cpp
new file mode 100644
index 0000000..7f1a781
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example.cpp
@@ -0,0 +1,5 @@
+#include "example.h"
+
+Example::Example()
+{
+}
diff --git a/Tests/RunCMake/Autogen/example.h b/Tests/RunCMake/Autogen/example.h
new file mode 100644
index 0000000..e8bfa42
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example.h
@@ -0,0 +1,12 @@
+#ifndef EXAMPLE_H
+#define EXAMPLE_H
+
+#include <QObject>
+
+class Example : public QObject
+{
+  Q_OBJECT
+  Example();
+};
+
+#endif
diff --git a/Tests/RunCMake/Autogen/example_ui.cpp b/Tests/RunCMake/Autogen/example_ui.cpp
new file mode 100644
index 0000000..fb97c32
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example_ui.cpp
@@ -0,0 +1,5 @@
+#include "example_ui.h"
+
+Example::Example()
+{
+}
diff --git a/Tests/RunCMake/Autogen/example_ui.h b/Tests/RunCMake/Autogen/example_ui.h
new file mode 100644
index 0000000..d691133
--- /dev/null
+++ b/Tests/RunCMake/Autogen/example_ui.h
@@ -0,0 +1,14 @@
+#ifndef EXAMPLE_UI_H
+#define EXAMPLE_UI_H
+
+#include <QObject>
+
+#include "ui_uiA.h"
+
+class Example : public QObject
+{
+  Q_OBJECT
+  Example();
+};
+
+#endif
diff --git a/Tests/RunCMake/Autogen/exe.cpp b/Tests/RunCMake/Autogen/exe.cpp
new file mode 100644
index 0000000..f8b643a
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/Autogen/exe_common.h b/Tests/RunCMake/Autogen/exe_common.h
new file mode 100644
index 0000000..15311c6
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_common.h
@@ -0,0 +1,48 @@
+#ifndef EXE_COMMON_H
+#define EXE_COMMON_H
+
+#include <cstdlib>
+#include <fstream>
+#include <string>
+#include <vector>
+
+inline int runRealExe(const int argc, char** argv)
+{
+  std::vector<std::string> args;
+  std::string realMocPath;
+  std::string const pathArg = "EXE_PATH=";
+  std::string cmd;
+  if (argc > 1) {
+    for (int i = 1; i < argc; ++i) {
+      std::string const arg = argv[i];
+      if (arg.find(pathArg) != std::string::npos) {
+        realMocPath = arg.substr(pathArg.length());
+        // if EXE_PATH contains spaces, wrap it in quotes
+        if (realMocPath.find(" ") != std::string::npos) {
+          realMocPath = "\"" + realMocPath + "\"";
+        }
+      } else {
+        args.push_back(arg);
+      }
+    }
+  }
+#ifdef _WIN32
+  cmd += "cmd /C \"";
+#endif
+  cmd += realMocPath + " ";
+  for (auto arg : args) {
+    // if arg contains spaces, wrap it in quotes
+    if (arg.find(' ') != std::string::npos) {
+      cmd += " \"" + arg + "\"";
+    } else {
+      cmd += " " + arg;
+    }
+  }
+#ifdef _WIN32
+  cmd += "\"";
+#endif
+  std::cout << "Running real exe:" << cmd << std::endl;
+  return std::system(cmd.c_str());
+}
+
+#endif
diff --git a/Tests/RunCMake/Autogen/exe_debug.cpp b/Tests/RunCMake/Autogen/exe_debug.cpp
new file mode 100644
index 0000000..ae5185b
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_debug.cpp
@@ -0,0 +1,10 @@
+#include <fstream>
+#include <iostream>
+
+#include "exe_common.h"
+
+int main(int argc, char* argv[])
+{
+  std::cout << "running_exe_Debug\n";
+  return runRealExe(argc, argv);
+}
diff --git a/Tests/RunCMake/Autogen/exe_release.cpp b/Tests/RunCMake/Autogen/exe_release.cpp
new file mode 100644
index 0000000..384c992
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_release.cpp
@@ -0,0 +1,10 @@
+#include <fstream>
+#include <iostream>
+
+#include "exe_common.h"
+
+int main(int argc, char* argv[])
+{
+  std::cout << "running_exe_Release\n";
+  return runRealExe(argc, argv);
+}
diff --git a/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp b/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp
new file mode 100644
index 0000000..aa6c558
--- /dev/null
+++ b/Tests/RunCMake/Autogen/exe_relwithdebinfo.cpp
@@ -0,0 +1,10 @@
+#include <fstream>
+#include <iostream>
+
+#include "exe_common.h"
+
+int main(int argc, char* argv[])
+{
+  std::cout << "running_exe_RelWithDebInfo\n";
+  return runRealExe(argc, argv);
+}
diff --git a/Tests/RunCMake/Autogen/simple_lib.cpp b/Tests/RunCMake/Autogen/simple_lib.cpp
new file mode 100644
index 0000000..cf8d689
--- /dev/null
+++ b/Tests/RunCMake/Autogen/simple_lib.cpp
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  void dummy_symbol()
+{
+}
diff --git a/Tests/RunCMake/Autogen/uiA.ui b/Tests/RunCMake/Autogen/uiA.ui
new file mode 100644
index 0000000..4c5762e
--- /dev/null
+++ b/Tests/RunCMake/Autogen/uiA.ui
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>UiA</class>
+ <widget class="QWidget" name="UiA">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="QTreeView" name="treeView"/>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Tests/RunCMake/BuildDepends/FortranInclude.cmake b/Tests/RunCMake/BuildDepends/FortranInclude.cmake
index fa9f399..ad5fd0a 100644
--- a/Tests/RunCMake/BuildDepends/FortranInclude.cmake
+++ b/Tests/RunCMake/BuildDepends/FortranInclude.cmake
@@ -1,5 +1,10 @@
 enable_language(Fortran)
 
+if("${CMAKE_Fortran_COMPILER_ID};${CMAKE_Fortran_SIMULATE_ID}" MATCHES "^Intel(LLVM)?;MSVC$")
+  string(APPEND CMAKE_Fortran_FLAGS_DEBUG " -Z7")
+  string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO " -Z7")
+endif()
+
 set(check_pairs "")
 
 add_executable(preprocess FortranIncludePreprocess.F)
diff --git a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
index 7230a07..07c17e2 100644
--- a/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
+++ b/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-stderr.txt
@@ -6,7 +6,7 @@
   Some library files are in directories implicitly searched by the linker
   when invoked for C:
 
-   .*/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-build/lib/(lib)?CMP0060.(a|lib)
+   .*/Tests/RunCMake/CMP0060/CMP0060-WARN-ON-build/lib/(lib)?CMP0060.(a|lib|l)
 
   For compatibility with older versions of CMake, the generated link line
   will ask the linker to search for these by library name.
diff --git a/Tests/RunCMake/CMP0111/CMP0111-Common.cmake b/Tests/RunCMake/CMP0111/CMP0111-Common.cmake
index c31e4ba..ab9e405 100644
--- a/Tests/RunCMake/CMP0111/CMP0111-Common.cmake
+++ b/Tests/RunCMake/CMP0111/CMP0111-Common.cmake
@@ -1,6 +1,3 @@
-# Prevent duplicate errors on some platforms.
-set(CMAKE_IMPORT_LIBRARY_SUFFIX "placeholder")
-
 add_library(unknown_lib UNKNOWN IMPORTED)
 add_library(static_lib STATIC IMPORTED)
 add_library(shared_lib SHARED IMPORTED)
diff --git a/Tests/RunCMake/CMP0111/CMP0111-NEW-stderr.txt b/Tests/RunCMake/CMP0111/CMP0111-NEW-stderr.txt
index 91a90e5..c6439e2 100644
--- a/Tests/RunCMake/CMP0111/CMP0111-NEW-stderr.txt
+++ b/Tests/RunCMake/CMP0111/CMP0111-NEW-stderr.txt
@@ -1,17 +1,6 @@
-^CMake Error in CMakeLists.txt:
-  IMPORTED_LOCATION not set for imported target "unknown_lib"( configuration
+^(CMake Error in CMakeLists.txt:
+  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration
   "[^"]+")?.
-+
-CMake Error in CMakeLists.txt:
-  IMPORTED_LOCATION not set for imported target "static_lib"( configuration
-  "[^"]+")?.
-+
-CMake Error in CMakeLists.txt:
-  IMPORTED_IMPLIB not set for imported target "shared_lib"( configuration
-  "[^"]+")?.(
-+
-CMake Error in CMakeLists.txt:
-  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static|shared)_lib"( configuration
-  "[^"]+")?.)*
-+
+)+
+.*(IMPORTED_LOCATION or )?IMPORTED_IMPLIB not set for imported target.*"shared_lib".*
 CMake Generate step failed.  Build files cannot be regenerated correctly.$
diff --git a/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt b/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt
index 27af911..7a46c41 100644
--- a/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt
+++ b/Tests/RunCMake/CMP0111/CMP0111-WARN-stderr.txt
@@ -1,39 +1,19 @@
-^CMake Warning \(dev\) in CMakeLists.txt:
+^(CMake Warning \(dev\) in CMakeLists.txt:
   Policy CMP0111 is not set: An imported target missing its location property
   fails during generation.  Run "cmake --help-policy CMP0111" for policy
   details.  Use the cmake_policy command to set the policy and suppress this
   warning.
 
-  IMPORTED_LOCATION not set for imported target "unknown_lib"( configuration
+  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration
   "[^"]+")?.
 This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) in CMakeLists.txt:
++)+CMake Warning \(dev\) in CMakeLists.txt:
   Policy CMP0111 is not set: An imported target missing its location property
   fails during generation.  Run "cmake --help-policy CMP0111" for policy
   details.  Use the cmake_policy command to set the policy and suppress this
   warning.
 
-  IMPORTED_LOCATION not set for imported target "static_lib"( configuration
+  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static)_lib"( configuration
   "[^"]+")?.
-This warning is for project developers.  Use -Wno-dev to suppress it.
-+
-CMake Warning \(dev\) in CMakeLists.txt:
-  Policy CMP0111 is not set: An imported target missing its location property
-  fails during generation.  Run "cmake --help-policy CMP0111" for policy
-  details.  Use the cmake_policy command to set the policy and suppress this
-  warning.
-
-  IMPORTED_IMPLIB not set for imported target "shared_lib"( configuration
-  "[^"]+")?.
-This warning is for project developers.  Use -Wno-dev to suppress it.(
-+
-CMake Warning \(dev\) in CMakeLists.txt:
-  Policy CMP0111 is not set: An imported target missing its location property
-  fails during generation.  Run "cmake --help-policy CMP0111" for policy
-  details.  Use the cmake_policy command to set the policy and suppress this
-  warning.
-
-  IMPORTED_(LOCATION|IMPLIB) not set for imported target "(unknown|static|shared)_lib"( configuration
-  "[^"]+")?.
-This warning is for project developers.  Use -Wno-dev to suppress it.)*$
+.*(IMPORTED_LOCATION or )?IMPORTED_IMPLIB not set for imported target.*"shared_lib".*
+This warning is for project developers.  Use -Wno-dev to suppress it.$
diff --git a/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt b/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
index 67d00f7..3472f33 100644
--- a/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
+++ b/Tests/RunCMake/CMP0115/CMP0115-OLD-stderr.txt
@@ -1,4 +1,13 @@
-^CMake Error at CMP0115\.cmake:[0-9]+ \(add_executable\):
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+  The OLD behavior for policy CMP0115 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
++
+CMake Error at CMP0115\.cmake:[0-9]+ \(add_executable\):
   Cannot find source file:
 
     noexist
diff --git a/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt
index 10e83a9..930dd3c 100644
--- a/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt
+++ b/Tests/RunCMake/CMP0116/CMP0116-Mixed-stderr.txt
@@ -1,4 +1,15 @@
-^CMake Warning \(dev\) at CMP0116-Mixed\.cmake:1 \(add_custom_command\):
+^CMake Deprecation Warning at CMP0116-Mixed\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0116 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Warning \(dev\) at CMP0116-Mixed\.cmake:1 \(add_custom_command\):
   Policy CMP0116 is not set: Ninja generators transform DEPFILEs from
   add_custom_command\(\)\.  Run "cmake --help-policy CMP0116" for policy
   details\.  Use the cmake_policy command to set the policy and suppress this
diff --git a/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt
new file mode 100644
index 0000000..887601c
--- /dev/null
+++ b/Tests/RunCMake/CMP0116/CMP0116-OLD-NOWARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+  The OLD behavior for policy CMP0116 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.$
diff --git a/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt b/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt
new file mode 100644
index 0000000..887601c
--- /dev/null
+++ b/Tests/RunCMake/CMP0116/CMP0116-OLD-WARN-stderr.txt
@@ -0,0 +1,8 @@
+^CMake Deprecation Warning at CMakeLists\.txt:[0-9]+ \(cmake_minimum_required\):
+  The OLD behavior for policy CMP0116 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
index 2af72a4..9285f9d 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test1-stderr.txt
@@ -1,4 +1,15 @@
-^prop: `0`
+^CMake Deprecation Warning at CMP0118-OLD-Test1\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+prop: `0`
 CMake Error at CMP0118-Common-Test1\.cmake:[0-9]+ \(target_sources\):
   Cannot find source file:
 
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
index 6109f65..9bd3d33 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test10-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source0\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test10\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source0\.txt: # 1a # GENERATED = `1`
 Generated_source0\.txt: # 1b # GENERATED = `1`
 Generated_source0\.txt: # 2a # GENERATED = `1`
 Generated_source0\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
index e5e97de..4730caf 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test11-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source0\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test11\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source0\.txt: # 1a # GENERATED = `1`
 Generated_source0\.txt: # 1b # GENERATED = `1`
 Generated_source0\.txt: # 2a # GENERATED = `1`
 Generated_source0\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt
index e6c429c..69a07e1 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test12-stderr.txt
@@ -1,4 +1,15 @@
-^CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\):
+^CMake Deprecation Warning at CMP0118-OLD-Test12\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\):
   TARGET 'custom[4-6]' was not created in this directory\.
 +
 CMake Error at subdir-Common-Test12/CMakeLists\.txt:[0-9]+ \(add_custom_command\):
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt
index 75dbf23..45c1dcb 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test13-stderr.txt
@@ -1,4 +1,15 @@
-^CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\):
+^CMake Deprecation Warning at CMP0118-OLD-Test13\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\):
   TARGET 'custom[4-6]' was not created in this directory\.
 +
 CMake Error at subdir-Common-Test13/CMakeLists\.txt:[0-9]+ \(add_custom_command\):
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
index f5b3d1a..7ade0cf 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test14-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source0\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test14\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source0\.txt: # 1a # GENERATED = `1`
 Generated_source0\.txt: # 1b # GENERATED = `1`
 Generated_source0\.txt: # 2a # GENERATED = `1`
 Generated_source0\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
index a30bc84..5735539 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test15-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source0\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test15\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source0\.txt: # 1a # GENERATED = `1`
 Generated_source0\.txt: # 1b # GENERATED = `1`
 Generated_source0\.txt: # 2a # GENERATED = `1`
 Generated_source0\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt
index 403ce5a..74eb3e1 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test2-stderr.txt
@@ -1 +1,12 @@
-^prop: `1`$
+^CMake Deprecation Warning at CMP0118-OLD-Test2\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+prop: `1`$
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
index 4f4fea3..cce5b19 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_with_full_path1\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test3\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_with_full_path1\.txt: # 1a # GENERATED = `1`
 Generated_with_full_path1\.txt: # 1b # GENERATED = `1`
 Generated_with_full_path1\.txt: # 2a # GENERATED = `1`
 Generated_with_full_path1\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
index 3c80531..47eee2e 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test3b-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_with_full_path1\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test3b\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_with_full_path1\.txt: # 1a # GENERATED = `1`
 Generated_with_full_path1\.txt: # 1b # GENERATED = `1`
 Generated_with_full_path1\.txt: # 2a # GENERATED = `1`
 Generated_with_full_path1\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
index 9600fee..f17c9be 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_with_full_path1\.txt: # 1a # GENERATED = `0`
+^CMake Deprecation Warning at CMP0118-OLD-Test4\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_with_full_path1\.txt: # 1a # GENERATED = `0`
 Generated_with_full_path1\.txt: # 1b # GENERATED = `0`
 Generated_with_full_path1\.txt: # 2a # GENERATED = `0`
 Generated_with_full_path1\.txt: # 2b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
index e638660..388e90e 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test4b-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_with_full_path1\.txt: # 1a # GENERATED = `0`
+^CMake Deprecation Warning at CMP0118-OLD-Test4b\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_with_full_path1\.txt: # 1a # GENERATED = `0`
 Generated_with_full_path1\.txt: # 1b # GENERATED = `0`
 Generated_with_full_path1\.txt: # 2a # GENERATED = `0`
 Generated_with_full_path1\.txt: # 2b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
index 18e6a8c..4a67fa7 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test5-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_with_full_path1\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test5\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_with_full_path1\.txt: # 1a # GENERATED = `1`
 Generated_with_full_path1\.txt: # 1b # GENERATED = `1`
 Generated_with_full_path1\.txt: # 2a # GENERATED = `1`
 Generated_with_full_path1\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
index a60545f..0cad373 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test6-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source1\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test6\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source1\.txt: # 1a # GENERATED = `1`
 Generated_source1\.txt: # 1b # GENERATED = `1`
 Generated_source1\.txt: # 2a # GENERATED = `1`
 Generated_source1\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
index fd496cb..7f232d5 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test7-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source1\.txt: # 1a # GENERATED = `1`
+^CMake Deprecation Warning at CMP0118-OLD-Test7\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source1\.txt: # 1a # GENERATED = `1`
 Generated_source1\.txt: # 1b # GENERATED = `1`
 Generated_source1\.txt: # 2a # GENERATED = `1`
 Generated_source1\.txt: # 2b # GENERATED = `1`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
index 3505242..dd9d2ef 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test8-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source1\.txt: # 1a # GENERATED = `0`
+^CMake Deprecation Warning at CMP0118-OLD-Test8\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source1\.txt: # 1a # GENERATED = `0`
 Generated_source1\.txt: # 1b # GENERATED = `0`
 Generated_source1\.txt: # 2a # GENERATED = `0`
 Generated_source1\.txt: # 2b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
index 63a9341..e01f782 100644
--- a/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
+++ b/Tests/RunCMake/CMP0118/CMP0118-OLD-Test9-stderr.txt
@@ -1,4 +1,15 @@
-^Generated_source1\.txt: # 1a # GENERATED = `0`
+^CMake Deprecation Warning at CMP0118-OLD-Test9\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+Generated_source1\.txt: # 1a # GENERATED = `0`
 Generated_source1\.txt: # 1b # GENERATED = `0`
 Generated_source1\.txt: # 2a # GENERATED = `0`
 Generated_source1\.txt: # 2b # GENERATED = `0`
diff --git a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
index 5e9cf6c..2fc472b 100644
--- a/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
+++ b/Tests/RunCMake/CMP0118/GenInSubdir-OLD-stderr.txt
@@ -1,4 +1,15 @@
-^CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\):
+^CMake Deprecation Warning at GenInSubdir-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0118 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at GenInSubdir/CMakeLists\.txt:[0-9]+ \(target_sources\):
   Cannot find source file:
 
     [^
diff --git a/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt b/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt
new file mode 100644
index 0000000..86eac41
--- /dev/null
+++ b/Tests/RunCMake/CMP0119/CMP0119-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0119-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0119 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake
index 7395827..2576a28 100644
--- a/Tests/RunCMake/CMP0119/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMP0119/RunCMakeTest.cmake
@@ -8,10 +8,10 @@
 endfunction()
 
 if(NOT RunCMake_GENERATOR MATCHES "Visual Studio|Xcode" AND
-    NOT CMAKE_C_COMPILER_ID MATCHES "(Borland|Embarcadero|Watcom)")
+    NOT CMAKE_C_COMPILER_ID MATCHES "(Borland|Embarcadero|Watcom|OrangeC)")
   run_CMP0119(WARN)
   run_CMP0119(OLD)
 endif()
-if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI)"))
+if((CMAKE_C_COMPILER_ID MATCHES "(GNU|LCC|Clang|MSVC|Borland|Embarcadero|Intel|TI|OrangeC)"))
   run_CMP0119(NEW)
 endif()
diff --git a/Tests/RunCMake/CMP0152/CMP0152-Common.cmake b/Tests/RunCMake/CMP0152/CMP0152-Common.cmake
new file mode 100644
index 0000000..6429cca
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/CMP0152-Common.cmake
@@ -0,0 +1,5 @@
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/")
+file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin/")
+file(CREATE_LINK  "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin" "${CMAKE_CURRENT_BINARY_DIR}/dir/bin" SYMBOLIC)
+file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../" real_path)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/CMP0152/CMP0152-NEW-stdout.txt
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/CMP0152/CMP0152-NEW-stdout.txt
diff --git a/Tests/RunCMake/CMP0152/CMP0152-NEW.cmake b/Tests/RunCMake/CMP0152/CMP0152-NEW.cmake
new file mode 100644
index 0000000..86a3b55
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/CMP0152-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0152 NEW)
+include(CMP0152-Common.cmake)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/CMP0152/CMP0152-OLD-stderr.txt
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/CMP0152/CMP0152-OLD-stderr.txt
diff --git a/Tests/RunCMake/CMP0152/CMP0152-OLD.cmake b/Tests/RunCMake/CMP0152/CMP0152-OLD.cmake
new file mode 100644
index 0000000..62ac300
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/CMP0152-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0152 OLD)
+include(CMP0152-Common.cmake)
diff --git a/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt b/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt
new file mode 100644
index 0000000..8d63168
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/CMP0152-WARN-stderr.txt
@@ -0,0 +1,27 @@
+^CMake Warning \(dev\) at CMP0152-Common\.cmake:[0-9]+ \(file\):
+  Policy CMP0152 is not set: file\(REAL_PATH\) resolves symlinks before
+  collapsing \.\./ components\.  Run "cmake --help-policy CMP0152" for policy
+  details\.  Use the cmake_policy command to set the policy and suppress this
+  warning\.
+
+  From input path:
+
+  [^
+]*/Tests/RunCMake/CMP0152/CMP0152-WARN-build/dir/bin/\.\./
+
+  the policy OLD behavior produces path:
+
+  [^
+]*/Tests/RunCMake/CMP0152/CMP0152-WARN-build/dir
+
+  but the policy NEW behavior produces path:
+
+  [^
+]*/Tests/RunCMake/CMP0152/CMP0152-WARN-build/dir/nested
+
+  Since the policy is not set, CMake is using the OLD behavior for
+  compatibility.
+Call Stack \(most recent call first\):
+  CMP0152-WARN\.cmake:[0-9]+ \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CMP0152/CMP0152-WARN.cmake b/Tests/RunCMake/CMP0152/CMP0152-WARN.cmake
new file mode 100644
index 0000000..e85589e
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/CMP0152-WARN.cmake
@@ -0,0 +1,2 @@
+
+include(CMP0152-Common.cmake)
diff --git a/Tests/RunCMake/CMP0152/CMakeLists.txt b/Tests/RunCMake/CMP0152/CMakeLists.txt
new file mode 100644
index 0000000..5ff8d3e
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.23)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0152/RunCMakeTest.cmake b/Tests/RunCMake/CMP0152/RunCMakeTest.cmake
new file mode 100644
index 0000000..7a9bab3
--- /dev/null
+++ b/Tests/RunCMake/CMP0152/RunCMakeTest.cmake
@@ -0,0 +1,7 @@
+include(RunCMake)
+
+if(NOT CMAKE_GENERATOR_NO_COMPILER_ENV)
+  run_cmake(CMP0152-WARN)
+  run_cmake(CMP0152-OLD)
+  run_cmake(CMP0152-NEW)
+endif()
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CMP0153/CMP0153-NEW-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/CMP0153/CMP0153-NEW-result.txt
diff --git a/Tests/RunCMake/CMP0153/CMP0153-NEW-stderr.txt b/Tests/RunCMake/CMP0153/CMP0153-NEW-stderr.txt
new file mode 100644
index 0000000..e24eee7
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-NEW-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error at [^
+]*/Tests/RunCMake/CMP0153/CMP0153-NEW\.cmake:[0-9]+ \(exec_program\):
+  The exec_program command should not be called; see CMP0153\.$
diff --git a/Tests/RunCMake/CMP0153/CMP0153-NEW-stdout.txt b/Tests/RunCMake/CMP0153/CMP0153-NEW-stdout.txt
new file mode 100644
index 0000000..10f3293
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-NEW-stdout.txt
@@ -0,0 +1 @@
+^$
diff --git a/Tests/RunCMake/CMP0153/CMP0153-NEW.cmake b/Tests/RunCMake/CMP0153/CMP0153-NEW.cmake
new file mode 100644
index 0000000..d252b46
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0153 NEW)
+exec_program("${CMAKE_COMMAND}" ARGS "-E echo \"exec_program() called\"")
diff --git a/Tests/RunCMake/CMP0153/CMP0153-OLD-stdout.txt b/Tests/RunCMake/CMP0153/CMP0153-OLD-stdout.txt
new file mode 100644
index 0000000..1aa5183
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-OLD-stdout.txt
@@ -0,0 +1 @@
+exec_program\(\) called
diff --git a/Tests/RunCMake/CMP0153/CMP0153-OLD.cmake b/Tests/RunCMake/CMP0153/CMP0153-OLD.cmake
new file mode 100644
index 0000000..d3c47a7
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0153 OLD)
+exec_program("${CMAKE_COMMAND}" ARGS "-E echo \"exec_program() called\"")
diff --git a/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt b/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt
new file mode 100644
index 0000000..8f22d4e
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-WARN-stderr.txt
@@ -0,0 +1,6 @@
+^CMake Warning \(dev\) at [^
+]*/Tests/RunCMake/CMP0153/CMP0153-WARN\.cmake:[0-9]+ \(exec_program\):
+  Policy CMP0153 is not set: The exec_program command should not be called\.
+  Run "cmake --help-policy CMP0153" for policy details\.  Use the cmake_policy
+  command to set the policy and suppress this warning\.
+This warning is for project developers\.  Use -Wno-dev to suppress it\.$
diff --git a/Tests/RunCMake/CMP0153/CMP0153-WARN-stdout.txt b/Tests/RunCMake/CMP0153/CMP0153-WARN-stdout.txt
new file mode 100644
index 0000000..1aa5183
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-WARN-stdout.txt
@@ -0,0 +1 @@
+exec_program\(\) called
diff --git a/Tests/RunCMake/CMP0153/CMP0153-WARN.cmake b/Tests/RunCMake/CMP0153/CMP0153-WARN.cmake
new file mode 100644
index 0000000..ba81501
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMP0153-WARN.cmake
@@ -0,0 +1 @@
+exec_program("${CMAKE_COMMAND}" ARGS "-E echo \"exec_program() called\"")
diff --git a/Tests/RunCMake/CMP0153/CMakeLists.txt b/Tests/RunCMake/CMP0153/CMakeLists.txt
new file mode 100644
index 0000000..922aad6
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.27)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CMP0153/RunCMakeTest.cmake b/Tests/RunCMake/CMP0153/RunCMakeTest.cmake
new file mode 100644
index 0000000..3d01dbf
--- /dev/null
+++ b/Tests/RunCMake/CMP0153/RunCMakeTest.cmake
@@ -0,0 +1,9 @@
+include(RunCMake)
+
+function(run_cmp0153 name)
+  run_cmake_command(${name} ${CMAKE_COMMAND} -P "${RunCMake_SOURCE_DIR}/${name}.cmake")
+endfunction()
+
+run_cmp0153(CMP0153-WARN)
+run_cmp0153(CMP0153-OLD)
+run_cmp0153(CMP0153-NEW)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 63b7568..4387c5b 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -165,14 +165,21 @@
   add_RunCMake_test(CMP0150)
 endif()
 
+if(NOT WIN32 OR CYGWIN)
+  add_RunCMake_test(CMP0152)
+endif()
+
+add_RunCMake_test(CMP0153)
+
 # The test for Policy 65 requires the use of the
 # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode
 # generators ignore.  The policy will have no effect on those generators.
 if(NOT CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
   add_RunCMake_test(CMP0065 -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 endif()
+add_executable(detect_jobserver detect_jobserver.c)
 if(CMAKE_GENERATOR MATCHES "Make")
-  add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU})
+  add_RunCMake_test(Make -DMAKE_IS_GNU=${MAKE_IS_GNU} -DDETECT_JOBSERVER=$<TARGET_FILE:detect_jobserver>)
 endif()
 unset(ninja_test_with_qt_version)
 unset(ninja_qt_args)
@@ -194,6 +201,7 @@
 endif()
 if(CMAKE_GENERATOR MATCHES "Ninja")
   set(Ninja_ARGS
+    -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID}
     -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION}
     -DCMAKE_SHARED_LIBRARY_PREFIX=${CMAKE_SHARED_LIBRARY_PREFIX}
     -DCMAKE_SHARED_LIBRARY_SUFFIX=${CMAKE_SHARED_LIBRARY_SUFFIX})
@@ -235,6 +243,9 @@
   add_RunCMake_test(NinjaMultiConfig)
   set_property(TEST RunCMake.NinjaMultiConfig APPEND
     PROPERTY LABELS "CUDA")
+  add_RunCMake_test(NinjaPrivateDeps
+    -DCMAKE_C_OUTPUT_EXTENSION=${CMAKE_C_OUTPUT_EXTENSION}
+    -DRunCMake_GENERATOR_IS_MULTI_CONFIG=${_isMultiConfig})
 endif()
 add_RunCMake_test(CTest)
 
@@ -267,6 +278,7 @@
   cmake_path(GET base_dir PARENT_PATH base_dir)  # <base>
   add_RunCMake_test(AutogenQt6 TEST_DIR Autogen
     -Dwith_qt_version=6
+    -DQtCore_VERSION=${Qt6Core_VERSION}
     "-DQt6_DIR:PATH=${Qt6_DIR}"
     "-DCMAKE_PREFIX_PATH:STRING=${base_dir}"
     -DPSEUDO_TIDY=$<TARGET_FILE:pseudo_tidy>
@@ -279,6 +291,7 @@
 if(CMake_TEST_Qt5 AND Qt5Widgets_FOUND)
   add_RunCMake_test(AutogenQt5 TEST_DIR Autogen
     -Dwith_qt_version=5
+    -DQtCore_VERSION=${Qt5Core_VERSION}
     "-DQt5_DIR:PATH=${Qt5_DIR}"
   )
   set(want_NoQt_test FALSE)
@@ -363,7 +376,8 @@
 endif()
 if(CMake_TEST_UseSWIG)
   add_RunCMake_test(FindSWIG)
-  add_RunCMake_test(UseSWIG -DCMake_TEST_FindPython=${CMake_TEST_FindPython})
+  add_RunCMake_test(UseSWIG -DCMake_TEST_FindPython2=${CMake_TEST_FindPython2}
+                            -DCMake_TEST_FindPython3=${CMake_TEST_FindPython3})
 endif()
 if(NOT CMAKE_C_COMPILER_ID MATCHES "Watcom")
   add_RunCMake_test(GenerateExportHeader)
@@ -385,6 +399,7 @@
 add_RunCMake_test(GenEx-PATH_EQUAL)
 add_RunCMake_test(GenEx-LIST)
 add_RunCMake_test(GeneratorExpression)
+add_RunCMake_test(GeneratorExpressionShortCircuit)
 add_RunCMake_test(GeneratorInstance)
 add_RunCMake_test(GeneratorPlatform)
 if(XCODE_VERSION)
@@ -458,6 +473,7 @@
 add_executable(exit_code exit_code.c)
 set(execute_process_ARGS
   -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code>
+  -DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin>
   -DPython_EXECUTABLE=${Python_EXECUTABLE}
   )
 if(NOT CMake_TEST_EXTERNAL_CMAKE)
@@ -465,6 +481,9 @@
 endif()
 add_RunCMake_test(execute_process)
 add_RunCMake_test(export)
+if(CMake_TEST_MSYSTEM_PREFIX)
+  list(APPEND cmake_host_system_information_ARGS -DCMake_TEST_MSYSTEM_PREFIX=${CMake_TEST_MSYSTEM_PREFIX})
+endif()
 add_RunCMake_test(cmake_host_system_information)
 add_RunCMake_test(cmake_language)
 add_RunCMake_test(cmake_minimum_required)
@@ -506,10 +525,10 @@
 endforeach()
 add_RunCMake_test(file-DOWNLOAD)
 add_RunCMake_test(file-RPATH -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
-add_RunCMake_test(find_file)
-add_RunCMake_test(find_library -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
-add_RunCMake_test(find_package -DMSYS=${MSYS})
-add_RunCMake_test(find_path)
+add_RunCMake_test(find_file -DMINGW=${MINGW})
+add_RunCMake_test(find_library -DMINGW=${MINGW} -DCYGWIN=${CYGWIN} -DMSYS=${MSYS})
+add_RunCMake_test(find_package -DMINGW=${MINGW} -DMSYS=${MSYS})
+add_RunCMake_test(find_path -DMINGW=${MINGW})
 add_RunCMake_test(find_program -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME})
 add_RunCMake_test(foreach)
 add_RunCMake_test(function)
@@ -528,6 +547,7 @@
 add_RunCMake_test(PrintHelpers)
 add_RunCMake_test(project -DCMake_TEST_RESOURCES=${CMake_TEST_RESOURCES})
 add_RunCMake_test(project_injected)
+add_RunCMake_test(property_init)
 add_RunCMake_test(DependencyProviders)
 add_RunCMake_test(return)
 add_RunCMake_test(separate_arguments)
@@ -625,8 +645,10 @@
   message(STATUS "Could not find ctresalloc")
 endif()
 
+get_filename_component(real_binary_dir "${CMake_BINARY_DIR}" REALPATH)
 if(NOT WIN32
     AND NOT MSYS # FIXME: This works on CYGWIN but not on MSYS
+    AND real_binary_dir STREQUAL CMake_BINARY_DIR
     )
   add_RunCMake_test(SymlinkTrees)
 endif ()
@@ -692,9 +714,27 @@
   set_property(TEST RunCMake.XcodeProject PROPERTY TIMEOUT ${CMake_TEST_XcodeProject_TIMEOUT})
 endif()
 
-if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
+if((CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
     AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0)
-  add_RunCMake_test(Framework)
+  OR (APPLE AND CMAKE_C_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0))
+  add_RunCMake_test(Framework -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID})
+  if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT DEFINED CMake_TEST_XcFramework)
+    set(CMake_TEST_XcFramework ON)
+  endif()
+  if(CMake_TEST_XcFramework AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 11.0)
+    set(XcFramework_ARGS -DCMake_TEST_XCODE_VERSION=${CMake_TEST_XCODE_VERSION})
+    add_RunCMake_test(XcFramework)
+
+    # This test can take a very long time due to lots of combinations.
+    # Use a long default timeout and provide an option to customize it.
+    if(NOT DEFINED CMake_TEST_XcFramework_TIMEOUT)
+      set(CMake_TEST_XcFramework_TIMEOUT 3000)
+    endif()
+    set_tests_properties(RunCMake.XcFramework PROPERTIES
+      TIMEOUT "${CMake_TEST_XcFramework_TIMEOUT}"
+      RUN_SERIAL TRUE
+      )
+  endif()
 endif()
 
 add_RunCMake_test(File_Archive)
@@ -825,7 +865,7 @@
 if(CMake_TEST_RunCMake_ExternalProject_DOWNLOAD_SERVER_TIMEOUT)
   list(APPEND ExternalProject_ARGS -DDOWNLOAD_SERVER_TIMEOUT=${CMake_TEST_RunCMake_ExternalProject_DOWNLOAD_SERVER_TIMEOUT})
 endif()
-add_RunCMake_test(ExternalProject)
+add_RunCMake_test(ExternalProject -DDETECT_JOBSERVER=$<TARGET_FILE:detect_jobserver>)
 add_RunCMake_test(FetchContent)
 add_RunCMake_test(FetchContent_find_package)
 set(CTestCommandLine_ARGS -DPython_EXECUTABLE=${Python_EXECUTABLE})
@@ -1038,6 +1078,7 @@
   )
 
 add_RunCMake_test(VerifyHeaderSets)
+add_RunCMake_test(set_tests_properties)
 
 if(${CMAKE_GENERATOR} MATCHES "Make|Ninja")
   add_RunCMake_test(TransformDepfile)
diff --git a/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake b/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake
index d9e399f..df58e72 100644
--- a/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake
+++ b/Tests/RunCMake/CMakePresets/GoodNoSCache.cmake
@@ -1,5 +1,4 @@
 include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake)
 
 get_filename_component(_parent "${CMAKE_SOURCE_DIR}" DIRECTORY)
-file(REAL_PATH "${_parent}" _parent)
 test_variable(CMAKE_BINARY_DIR "" "${_parent}/GoodNoSCachePrep-build")
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index c4a8b3f..88027fb 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -153,6 +153,13 @@
 run_cmake_presets(SubConditionNull)
 run_cmake_presets(TraceNotSupported)
 
+set(CMakePresets_NO_PRESET 1)
+set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
+run_cmake_presets(SchemaSupported --list-presets)
+set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
+run_cmake_presets(SchemaNotSupported --list-presets)
+unset(CMakePresets_NO_PRESET)
+
 # Test cmakeMinimumRequired field
 run_cmake_presets(MinimumRequiredInvalid)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CMakePresets/SchemaNotSupported-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/CMakePresets/SchemaNotSupported-result.txt
diff --git a/Tests/RunCMake/CMakePresets/SchemaNotSupported-stderr.txt b/Tests/RunCMake/CMakePresets/SchemaNotSupported-stderr.txt
new file mode 100644
index 0000000..2df4b3d
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SchemaNotSupported-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/SchemaNotSupported:
+File version must be 8 or higher for [$]schema support$
diff --git a/Tests/RunCMake/CMakePresets/SchemaNotSupported.json.in b/Tests/RunCMake/CMakePresets/SchemaNotSupported.json.in
new file mode 100644
index 0000000..736f307
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SchemaNotSupported.json.in
@@ -0,0 +1,4 @@
+{
+    "version": 7,
+    "$schema": "https://example.com/schema.json"
+}
diff --git a/Tests/RunCMake/CMakePresets/SchemaSupported-result.txt b/Tests/RunCMake/CMakePresets/SchemaSupported-result.txt
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SchemaSupported-result.txt
@@ -0,0 +1 @@
+0
diff --git a/Tests/RunCMake/CMakePresets/SchemaSupported.json.in b/Tests/RunCMake/CMakePresets/SchemaSupported.json.in
new file mode 100644
index 0000000..5426131
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SchemaSupported.json.in
@@ -0,0 +1,4 @@
+{
+    "version": 8,
+    "$schema": "https://example.com/schema.json"
+}
diff --git a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
index c90d543..223a61c 100644
--- a/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestCommandLine/RunCMakeTest.cmake
@@ -472,7 +472,7 @@
 add_test(test2 \"${CMAKE_COMMAND}\" -E echo \"hello world\")
 add_test(test3 \"${CMAKE_COMMAND}\" -E true)
 set_tests_properties(test3 PROPERTIES  DISABLED \"ON\")
-add_test(test4 \"${CMAKE_COMMAND}/doesnt_exist\")
+add_test(test4 \"${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist\")
 add_test(test5 \"${CMAKE_COMMAND}\" -E echo \"please skip\")
 set_tests_properties(test5 PROPERTIES  SKIP_REGULAR_EXPRESSION \"please skip\")
 ")
diff --git a/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt b/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt
index ce30dc8..c57c378 100644
--- a/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt
+++ b/Tests/RunCMake/CTestCommandLine/output-junit-stderr.txt
@@ -1 +1,2 @@
-Unable to find executable: .*doesnt_exist
+Unable to find executable:[^
+]*does_not_exist
diff --git a/Tests/RunCMake/CTestResourceAllocation/CMakeLists.txt.in b/Tests/RunCMake/CTestResourceAllocation/CMakeLists.txt.in
index 9984421..7c08dcc 100644
--- a/Tests/RunCMake/CTestResourceAllocation/CMakeLists.txt.in
+++ b/Tests/RunCMake/CTestResourceAllocation/CMakeLists.txt.in
@@ -4,6 +4,8 @@
   set(projname "${CMAKE_MATCH_1}")
   project(${projname} NONE)
   include(CTest)
-  include("@RunCMake_SOURCE_DIR@/ResourceCommon.cmake")
+  if(NOT CASE_NAME MATCHES "^dynamic-resource-")
+    include("@RunCMake_SOURCE_DIR@/resource-common.cmake")
+  endif()
   include("@RunCMake_SOURCE_DIR@/${projname}.cmake")
 endif()
diff --git a/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake b/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
index f5f0699..42e13fc 100644
--- a/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CTestResourceAllocation/RunCMakeTest.cmake
@@ -179,3 +179,19 @@
 set(ENV{CTEST_RESOURCE_GROUP_COUNT} 2)
 run_ctest_resource(process_count 1 0 0)
 unset(ENV{CTEST_RESOURCE_GROUP_COUNT})
+
+function(run_ctest_resource_dynamic name)
+  run_ctest("${name}-ctest-s" ${ARGN})
+endfunction()
+
+run_ctest_resource_dynamic(dynamic-resource -VV)
+run_ctest_resource_dynamic(dynamic-resource-notenough)
+run_ctest_resource_dynamic(dynamic-resource-nofile)
+run_ctest_resource_dynamic(dynamic-resource-multiple-generators)
+run_ctest_resource_dynamic(dynamic-resource-no-setup-fixture)
+run_ctest_resource_dynamic(dynamic-resource-multiple-setup-fixtures)
+run_ctest_resource_dynamic(dynamic-resource-no-required-fixture)
+run_ctest_resource_dynamic(dynamic-resource-conflicting-spec -DCTEST_RESOURCE_SPEC_SOURCE=CACHE)
+run_ctest_resource_dynamic(dynamic-resource-circular)
+run_ctest_resource_dynamic(dynamic-resource-circular-no-required-fixtures)
+run_ctest_resource_dynamic(dynamic-resource-relative-path)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt
new file mode 100644
index 0000000..397ca38
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-ctest-s-stderr.txt
@@ -0,0 +1,3 @@
+^Error: a cycle exists in the test dependency graph for the test "GenerateSpecFile"\.
+Please fix the cycle and run ctest again.
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt
new file mode 100644
index 0000000..06ea90f
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^All tests that have RESOURCE_GROUPS must include the resource spec generator fixture in their FIXTURES_REQUIRED
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures.cmake
new file mode 100644
index 0000000..9accdf3
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular-no-required-fixtures.cmake
@@ -0,0 +1,11 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular.cmake
new file mode 100644
index 0000000..4917e30
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-circular.cmake
@@ -0,0 +1,12 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt
new file mode 100644
index 0000000..4e4c01c
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^GENERATED_RESOURCE_SPEC_FILE test property cannot be used in conjunction with ResourceSpecFile option
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec.cmake
new file mode 100644
index 0000000..668b049
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-conflicting-spec.cmake
@@ -0,0 +1,10 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-stdout.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-stdout.txt
new file mode 100644
index 0000000..ec97787
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-stdout.txt
@@ -0,0 +1,24 @@
+test 2
+    Start 2: GenerateSpecFile
+
+2: Test command: "?[^
+]*[\\/]bin([\\/][^\\/
+]+)?[\\/]cmake(\.exe)?"? "-E" "copy" "[^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resspec\.json" "[^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-build"
+2: Working Directory: [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-build
+2: Test timeout computed to be: 600
+1/2 Test #2: GenerateSpecFile .................   Passed +[0-9]+\.[0-9]+ sec
+Using generated resource spec file [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-build/dynamic-resspec\.json
+test 1
+    Start 1: RealTest
+
+1: Test command: "?[^
+]*[\\/]bin([\\/][^\\/
+]+)?[\\/]cmake(\.exe)?"? "-E" "true"
+1: Working Directory: [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-ctest-s-build
+1: Test timeout computed to be: 600
+2/2 Test #1: RealTest .........................   Passed +[0-9]+\.[0-9]+ sec
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt
new file mode 100644
index 0000000..273cb80
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^Only one test may define the GENERATED_RESOURCE_SPEC_FILE property
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators.cmake
new file mode 100644
index 0000000..7ee58d4
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-generators.cmake
@@ -0,0 +1,11 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile1 COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile2 COMMAND "${CMAKE_COMMAND}" -E true)
+set_tests_properties(GenerateSpecFile1 GenerateSpecFile2 PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt
new file mode 100644
index 0000000..39ee275
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^Test that defines GENERATED_RESOURCE_SPEC_FILE must have exactly one FIXTURES_SETUP
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures.cmake
new file mode 100644
index 0000000..a9e72ea
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-multiple-setup-fixtures.cmake
@@ -0,0 +1,10 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec;InvalidResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt
new file mode 100644
index 0000000..06ea90f
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^All tests that have RESOURCE_GROUPS must include the resource spec generator fixture in their FIXTURES_REQUIRED
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture.cmake
new file mode 100644
index 0000000..1983678
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-required-fixture.cmake
@@ -0,0 +1,9 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt
new file mode 100644
index 0000000..39ee275
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^Test that defines GENERATED_RESOURCE_SPEC_FILE must have exactly one FIXTURES_SETUP
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture.cmake
new file mode 100644
index 0000000..b6dec5e
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-no-setup-fixture.cmake
@@ -0,0 +1,9 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-stderr.txt
new file mode 100644
index 0000000..343f632
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-stderr.txt
@@ -0,0 +1,7 @@
+^Could not read/parse resource spec file [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-build/dynamic-resspec\.json:[ ]
+File not found: [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-build/dynamic-resspec\.json
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-stdout.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-stdout.txt
new file mode 100644
index 0000000..fcf8ace
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile-ctest-s-stdout.txt
@@ -0,0 +1,5 @@
+    Start 2: GenerateSpecFile
+1/2 Test #2: GenerateSpecFile .................\*\*\*Failed  Invalid resource spec file +[0-9]+\.[0-9]+ sec
+    Start 1: RealTest
+Failed test dependencies: GenerateSpecFile
+2/2 Test #1: RealTest .........................\*\*\*Not Run +[0-9]+\.[0-9]+ sec
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile.cmake
new file mode 100644
index 0000000..e771c60
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-nofile.cmake
@@ -0,0 +1,10 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E true)
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-stderr.txt
new file mode 100644
index 0000000..393ab84
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-stderr.txt
@@ -0,0 +1,14 @@
+^Insufficient resources for test RealTest:
+
+  Test requested resources of type 'widgets' in the following amounts:
+    2 slots
+  but only the following units were available:
+    '0': 1 slot
+
+Resource spec file:
+
+  [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-build/dynamic-resspec\.json
+CMake Error at [^
+]*/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s/test\.cmake:[0-9]+ \(message\):
+  Tests did not pass$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-stdout.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-stdout.txt
new file mode 100644
index 0000000..b411a74
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough-ctest-s-stdout.txt
@@ -0,0 +1,4 @@
+    Start 2: GenerateSpecFile
+1/2 Test #2: GenerateSpecFile .................   Passed +[0-9]+\.[0-9]+ sec
+    Start 1: RealTest
+2/2 Test #1: RealTest .........................\*\*\*Not Run +[0-9]+\.[0-9]+ sec
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough.cmake
new file mode 100644
index 0000000..c8e1313
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-notenough.cmake
@@ -0,0 +1,10 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:2"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-result.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt
new file mode 100644
index 0000000..2c4dff8
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path-ctest-s-stderr.txt
@@ -0,0 +1,2 @@
+^GENERATED_RESOURCE_SPEC_FILE must be an absolute path
+No tests were found!!!$
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path.cmake
new file mode 100644
index 0000000..3ee83d7
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource-relative-path.cmake
@@ -0,0 +1,10 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resource.cmake b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource.cmake
new file mode 100644
index 0000000..668b049
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resource.cmake
@@ -0,0 +1,10 @@
+add_test(NAME RealTest COMMAND "${CMAKE_COMMAND}" -E true)
+add_test(NAME GenerateSpecFile COMMAND "${CMAKE_COMMAND}" -E copy "${CTEST_DYNAMIC_RESOURCE_SPEC_FILE}" "${CMAKE_BINARY_DIR}")
+set_tests_properties(GenerateSpecFile PROPERTIES
+  GENERATED_RESOURCE_SPEC_FILE "${CMAKE_BINARY_DIR}/dynamic-resspec.json"
+  FIXTURES_SETUP "ResourceSpec"
+  )
+set_tests_properties(RealTest PROPERTIES
+  FIXTURES_REQUIRED "ResourceSpec"
+  RESOURCE_GROUPS "widgets:1"
+  )
diff --git a/Tests/RunCMake/CTestResourceAllocation/dynamic-resspec.json b/Tests/RunCMake/CTestResourceAllocation/dynamic-resspec.json
new file mode 100644
index 0000000..f9bedaf
--- /dev/null
+++ b/Tests/RunCMake/CTestResourceAllocation/dynamic-resspec.json
@@ -0,0 +1,16 @@
+{
+  "version": {
+    "major": 1,
+    "minor": 0
+  },
+  "local": [
+    {
+      "widgets": [
+        {
+          "id": "0",
+          "slots": 1
+        }
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CTestResourceAllocation/ResourceCommon.cmake b/Tests/RunCMake/CTestResourceAllocation/resource-common.cmake
similarity index 100%
rename from Tests/RunCMake/CTestResourceAllocation/ResourceCommon.cmake
rename to Tests/RunCMake/CTestResourceAllocation/resource-common.cmake
diff --git a/Tests/RunCMake/CTestResourceAllocation/test.cmake.in b/Tests/RunCMake/CTestResourceAllocation/test.cmake.in
index 9ad9ac8..319ebf1 100644
--- a/Tests/RunCMake/CTestResourceAllocation/test.cmake.in
+++ b/Tests/RunCMake/CTestResourceAllocation/test.cmake.in
@@ -8,9 +8,15 @@
 set(CTEST_BUILD_CONFIGURATION "$ENV{CMAKE_CONFIG_TYPE}")
 set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
 
-set(config_options
-  "-DCTEST_RESOURCE_ALLOC_ENABLED=${CTEST_RESOURCE_ALLOC_ENABLED};-DCTRESALLOC_COMMAND=${CTRESALLOC_COMMAND}"
-  )
+if("@CASE_NAME@" MATCHES "^dynamic-resource-")
+  set(config_options
+    "-DCTEST_DYNAMIC_RESOURCE_SPEC_FILE=@RunCMake_SOURCE_DIR@/dynamic-resspec.json"
+    )
+else()
+  set(config_options
+    "-DCTEST_RESOURCE_ALLOC_ENABLED=${CTEST_RESOURCE_ALLOC_ENABLED};-DCTRESALLOC_COMMAND=${CTRESALLOC_COMMAND}"
+    )
+endif()
 
 if(CTEST_RESOURCE_SPEC_SOURCE STREQUAL "CMDLINE")
   list(APPEND config_options "-DCTEST_RESOURCE_SPEC_FILE=@RunCMake_SOURCE_DIR@/noexist.json")
diff --git a/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt b/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt
index 6c42612..dbadd048 100644
--- a/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt
+++ b/Tests/RunCMake/CUDA_architectures/architectures-empty-stderr.txt
@@ -1,5 +1,7 @@
-^CMake Error at .*/Modules/CMakeDetermineCUDACompiler\.cmake:[0-9]+ \(message\):
+^CMake Error at .*/Internal/CMakeCUDAArchitecturesValidate\.cmake:[0-9]+ \(message\):
   CMAKE_CUDA_ARCHITECTURES must be non-empty if set\.
 Call Stack \(most recent call first\):
+  [^
+]*/Modules/CMakeDetermineCUDACompiler.cmake:[0-9]+ \(cmake_cuda_architectures_validate\)
   architectures-empty\.cmake:2 \(enable_language\)
   CMakeLists\.txt:3 \(include\)
diff --git a/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt b/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt
index 14c76d2..891aa40 100644
--- a/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt
+++ b/Tests/RunCMake/CUDA_architectures/architectures-invalid-stderr.txt
@@ -1,4 +1,4 @@
-^CMake Error at .*/Modules/CMakeDetermineCUDACompiler\.cmake:[0-9]+ \(message\):
+^CMake Error at .*/Internal/CMakeCUDAArchitecturesValidate\.cmake:[0-9]+ \(message\):
   CMAKE_CUDA_ARCHITECTURES:
 
     invalid
@@ -10,5 +10,7 @@
     \* a special value: all, all-major, native
 
 Call Stack \(most recent call first\):
+  [^
+]*/Modules/CMakeDetermineCUDACompiler.cmake:[0-9]+ \(cmake_cuda_architectures_validate\)
   architectures-invalid\.cmake:2 \(enable_language\)
   CMakeLists\.txt:3 \(include\)$
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CXXModules/CMP0155-NEW-result.txt
similarity index 100%
rename from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
rename to Tests/RunCMake/CXXModules/CMP0155-NEW-result.txt
diff --git a/Tests/RunCMake/CXXModules/CMP0155-NEW-stderr.txt b/Tests/RunCMake/CXXModules/CMP0155-NEW-stderr.txt
new file mode 100644
index 0000000..0843d0b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/CMP0155-NEW-stderr.txt
@@ -0,0 +1,10 @@
+(CMake Error in CMakeLists.txt:
+  The target named "cmp0155-new" has C\+\+ sources that may use modules, but
+  the compiler does not provide a way to discover the import graph
+  dependencies\.  See the cmake-cxxmodules\(7\) manual and the
+  CMAKE_CXX_SCAN_FOR_MODULES variable\.
+|CMake Error in CMakeLists.txt:
+  The target named "cmp0155-new" has C\+\+ sources that may use modules, but
+  modules are not supported by this generator\.  See the cmake-cxxmodules\(7\)
+  manual and the CMAKE_CXX_SCAN_FOR_MODULES variable\.
+)
diff --git a/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake b/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake
new file mode 100644
index 0000000..bea858b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/CMP0155-NEW.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+unset(CMAKE_CXX_SCANDEP_SOURCE)
+
+cmake_policy(SET CMP0155 NEW)
+
+add_executable(cmp0155-new
+  sources/module-use.cxx)
+set_target_properties(cmp0155-new
+  PROPERTIES
+    CXX_STANDARD 20
+    CXX_STANDARD_REQUIRED ON)
diff --git a/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake b/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake
new file mode 100644
index 0000000..201598e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/CMP0155-OLD.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+unset(CMAKE_CXX_SCANDEP_SOURCE)
+
+cmake_policy(SET CMP0155 OLD)
+
+add_executable(cmp0155-old
+  sources/module-use.cxx)
+set_target_properties(cmp0155-old
+  PROPERTIES
+    CXX_STANDARD 20
+    CXX_STANDARD_REQUIRED ON)
diff --git a/Tests/RunCMake/CXXModules/CMakeLists.txt b/Tests/RunCMake/CXXModules/CMakeLists.txt
index 88eb282..913371f 100644
--- a/Tests/RunCMake/CXXModules/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/CMakeLists.txt
@@ -1,6 +1,4 @@
 cmake_minimum_required(VERSION 3.23)
 project(${RunCMake_TEST} NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt
deleted file mode 100644
index a82791b..0000000
--- a/Tests/RunCMake/CXXModules/ExportBuildCxxModules-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at ExportBuildCxxModules.cmake:5 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake b/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake
index eb3a49c..8cee83e 100644
--- a/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake
+++ b/Tests/RunCMake/CXXModules/ExportBuildCxxModules.cmake
@@ -1,5 +1,5 @@
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 add_library(export-modules)
 target_sources(export-modules
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt b/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt
deleted file mode 100644
index db02227..0000000
--- a/Tests/RunCMake/CXXModules/ExportInstallCxxModules-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at ExportInstallCxxModules.cmake:5 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake b/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake
index b4cb376..24a32a6 100644
--- a/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake
+++ b/Tests/RunCMake/CXXModules/ExportInstallCxxModules.cmake
@@ -1,5 +1,5 @@
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 add_library(export-modules)
 target_sources(export-modules
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt
index dfcdbec..7269c65 100644
--- a/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt
+++ b/Tests/RunCMake/CXXModules/FileSetModulesInterface-stderr.txt
@@ -1,12 +1,5 @@
-CMake Warning \(dev\) at FileSetModulesInterface.cmake:2 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
-CMake Error at FileSetModulesInterface.cmake:2 \(target_sources\):
+CMake Error at FileSetModulesInterface.cmake:[0-9]+ \(target_sources\):
   target_sources File set TYPE "CXX_MODULES" may not have "INTERFACE"
   visibility
 Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
+  CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt
deleted file mode 100644
index 4420bbc..0000000
--- a/Tests/RunCMake/CXXModules/FileSetModulesInterfaceImported-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at FileSetModulesInterfaceImported.cmake:2 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt
deleted file mode 100644
index 0c82ccc..0000000
--- a/Tests/RunCMake/CXXModules/FileSetModulesPrivate-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at FileSetModulesPrivate.cmake:5 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake
index 198ae15..83bbd4d 100644
--- a/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake
+++ b/Tests/RunCMake/CXXModules/FileSetModulesPrivate.cmake
@@ -1,5 +1,5 @@
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 add_library(module)
 target_sources(module
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt b/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt
deleted file mode 100644
index a27a28e..0000000
--- a/Tests/RunCMake/CXXModules/FileSetModulesPublic-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at FileSetModulesPublic.cmake:5 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake b/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake
index 904e42e..52ba35f 100644
--- a/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake
+++ b/Tests/RunCMake/CXXModules/FileSetModulesPublic.cmake
@@ -1,5 +1,5 @@
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 add_library(module)
 target_sources(module
diff --git a/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake
new file mode 100644
index 0000000..cac1777
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/ImplicitCXX20.cmake
@@ -0,0 +1,13 @@
+# Enable scanning by default for targets that explicitly use C++ 20.
+cmake_policy(SET CMP0155 NEW)
+
+# Force CMAKE_CXX_STANDARD_DEFAULT to be C++ 20.
+set(ENV{CXXFLAGS} "$ENV{CXXFLAGS} ${CMAKE_CXX20_STANDARD_COMPILE_OPTION}")
+enable_language(CXX)
+
+# Hide any real scanning rule that may be available.
+unset(CMAKE_CXX_SCANDEP_SOURCE)
+
+# Create a target that does not explicitly use C++ 20 to verify it works
+# without any scanning rule available.
+add_executable(cmp0155-new sources/module-use.cxx)
diff --git a/Tests/RunCMake/CXXModules/Inspect.cmake b/Tests/RunCMake/CXXModules/Inspect.cmake
new file mode 100644
index 0000000..612b01b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/Inspect.cmake
@@ -0,0 +1,30 @@
+enable_language(CXX)
+
+set(info "")
+
+# See `Modules/Compiler/MSVC-CXX.cmake` for this. If there is explicitly no
+# default, the feature list is populated to be everything.
+if (DEFINED CMAKE_CXX_STANDARD_DEFAULT AND
+    CMAKE_CXX_STANDARD_DEFAULT STREQUAL "")
+  set(CMAKE_CXX_COMPILE_FEATURES "")
+endif ()
+
+# Detect if the environment forces a C++ standard, let the test selection know.
+set(forced_cxx_standard 0)
+if (CMAKE_CXX_FLAGS MATCHES "-std=")
+  set(forced_cxx_standard 1)
+endif ()
+
+# Forward information about the C++ compile features.
+string(APPEND info "\
+set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
+set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
+set(forced_cxx_standard \"${forced_cxx_standard}\")
+set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\")
+set(CMAKE_CXX_OUTPUT_EXTENSION \"${CMAKE_CXX_OUTPUT_EXTENSION}\")
+set(CXXModules_default_build_type \"${CMAKE_BUILD_TYPE}\")
+set(CMAKE_CXX_STANDARD_DEFAULT \"${CMAKE_CXX_STANDARD_DEFAULT}\")
+set(CMAKE_CXX20_STANDARD_COMPILE_OPTION \"${CMAKE_CXX20_STANDARD_COMPILE_OPTION}\")
+")
+
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt
deleted file mode 100644
index fc3c7db..0000000
--- a/Tests/RunCMake/CXXModules/InstallBMI-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at InstallBMI.cmake:8 \(install\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt
deleted file mode 100644
index 44c961f..0000000
--- a/Tests/RunCMake/CXXModules/InstallBMIGenericArgs-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at InstallBMIGenericArgs.cmake:8 \(install\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt b/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt
deleted file mode 100644
index d9d2c2d..0000000
--- a/Tests/RunCMake/CXXModules/InstallBMIIgnore-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at InstallBMIIgnore.cmake:5 \(install\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake
index 0d08c44..6de2e1e 100644
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake
+++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-check.cmake
@@ -2,19 +2,19 @@
 
 if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
   set(have_file 0)
-  foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
-    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json")
+  foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
+    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${CXXModules_config}/CXXDependInfo.json")
       continue ()
     endif ()
     set(have_file 1)
 
-    set(CMAKE_BUILD_TYPE "${config}")
+    set(CMAKE_BUILD_TYPE "${CXXModules_config}")
 
-    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents)
     file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents)
     check_json("${actual_contents}" "${expect_contents}")
 
-    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents)
     file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents)
     check_json("${actual_contents}" "${expect_contents}")
   endforeach ()
@@ -24,6 +24,9 @@
       "No recognized build configurations found.")
   endif ()
 else ()
+  set(CXXModules_config "${CXXModules_default_build_type}")
+  set(CMAKE_BUILD_TYPE "${CXXModules_default_build_type}")
+
   file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-bmi-install-public.dir/CXXDependInfo.json" actual_contents)
   file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-public.json" expect_contents)
   check_json("${actual_contents}" "${expect_contents}")
@@ -32,3 +35,5 @@
   file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoBMIInstall-private.json" expect_contents)
   check_json("${actual_contents}" "${expect_contents}")
 endif ()
+
+string(REPLACE ";" "\n  " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt
deleted file mode 100644
index 9a7c1f9..0000000
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at NinjaDependInfoBMIInstall.cmake:13 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake
index 9f8e51a..fd8fd25 100644
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake
+++ b/Tests/RunCMake/CXXModules/NinjaDependInfoBMIInstall.cmake
@@ -1,6 +1,6 @@
 # Fake out that we have dyndep; we only need to generate, not actually build
 # here.
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 enable_language(CXX)
 
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake
index 7720257..0c933c9 100644
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake
+++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-check.cmake
@@ -2,19 +2,19 @@
 
 if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
   set(have_file 0)
-  foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
-    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json")
+  foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
+    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${CXXModules_config}/CXXDependInfo.json")
       continue ()
     endif ()
     set(have_file 1)
 
-    set(CMAKE_BUILD_TYPE "${config}")
+    set(CMAKE_BUILD_TYPE "${CXXModules_config}")
 
-    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents)
     file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents)
     check_json("${actual_contents}" "${expect_contents}")
 
-    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents)
     file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents)
     check_json("${actual_contents}" "${expect_contents}")
   endforeach ()
@@ -24,6 +24,9 @@
       "No recognized build configurations found.")
   endif ()
 else ()
+  set(CXXModules_config "${CXXModules_default_build_type}")
+  set(CMAKE_BUILD_TYPE "${CXXModules_default_build_type}")
+
   file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-exports-public.dir/CXXDependInfo.json" actual_contents)
   file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-public.json" expect_contents)
   check_json("${actual_contents}" "${expect_contents}")
@@ -32,3 +35,5 @@
   file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoExport-private.json" expect_contents)
   check_json("${actual_contents}" "${expect_contents}")
 endif ()
+
+string(REPLACE ";" "\n  " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt
deleted file mode 100644
index b66005b..0000000
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoExport-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at NinjaDependInfoExport.cmake:13 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake
index 0b09d23..a224eca 100644
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake
+++ b/Tests/RunCMake/CXXModules/NinjaDependInfoExport.cmake
@@ -1,6 +1,6 @@
 # Fake out that we have dyndep; we only need to generate, not actually build
 # here.
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 enable_language(CXX)
 
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake
index b9a1315..4eaa891 100644
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake
+++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-check.cmake
@@ -2,19 +2,19 @@
 
 if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
   set(have_file 0)
-  foreach (config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
-    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json")
+  foreach (CXXModules_config IN ITEMS Release Debug RelWithDebInfo MinSizeRel)
+    if (NOT EXISTS "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${CXXModules_config}/CXXDependInfo.json")
       continue ()
     endif ()
     set(have_file 1)
 
-    set(CMAKE_BUILD_TYPE "${config}")
+    set(CMAKE_BUILD_TYPE "${CXXModules_config}")
 
-    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents)
     file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents)
     check_json("${actual_contents}" "${expect_contents}")
 
-    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/${config}/CXXDependInfo.json" actual_contents)
+    file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-private.dir/${CXXModules_config}/CXXDependInfo.json" actual_contents)
     file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents)
     check_json("${actual_contents}" "${expect_contents}")
   endforeach ()
@@ -24,6 +24,9 @@
       "No recognized build configurations found.")
   endif ()
 else ()
+  set(CXXModules_config "${CXXModules_default_build_type}")
+  set(CMAKE_BUILD_TYPE "${CXXModules_default_build_type}")
+
   file(READ "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/ninja-file-sets-public.dir/CXXDependInfo.json" actual_contents)
   file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-public.json" expect_contents)
   check_json("${actual_contents}" "${expect_contents}")
@@ -32,3 +35,5 @@
   file(READ "${CMAKE_CURRENT_LIST_DIR}/expect/NinjaDependInfoFileSet-private.json" expect_contents)
   check_json("${actual_contents}" "${expect_contents}")
 endif ()
+
+string(REPLACE ";" "\n  " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt
deleted file mode 100644
index 949b7af..0000000
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Warning \(dev\) at NinjaDependInfoFileSet.cmake:13 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake
index b50be99..77351bf 100644
--- a/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake
+++ b/Tests/RunCMake/CXXModules/NinjaDependInfoFileSet.cmake
@@ -1,6 +1,6 @@
 # Fake out that we have dyndep; we only need to generate, not actually build
 # here.
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 enable_language(CXX)
 
diff --git a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt
index 7e8f384..ba2715b 100644
--- a/Tests/RunCMake/CXXModules/NoCXX-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NoCXX-stderr.txt
@@ -1,25 +1,15 @@
-CMake Warning \(dev\) at NoCXX.cmake:4 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
+(CMake Error in CMakeLists.txt:
+  The target named "nocxx" has C\+\+ sources that use modules, but the "CXX"
+  language has not been enabled\.
+|CMake Error in CMakeLists.txt:
+  The target named "nocxx" has C\+\+ sources that may use modules, but modules
+  are not supported by this generator\.  See the cmake-cxxmodules\(7\) manual
+  and the CMAKE_CXX_SCAN_FOR_MODULES variable\.
+|CMake Error in CMakeLists.txt:
+  Target "nocxx" has source file
 
-CMake Error in CMakeLists.txt:
-  The "nocxx" target has C\+\+ module sources but the "CXX" language has not
-  been enabled
-
-(
-CMake Error in CMakeLists.txt:
-(  The "nocxx" target has C\+\+ module sources but the "CXX" language has not
-  been enabled
-|  The "nocxx" target contains C\+\+ module sources which are not supported by
-  the generator
-|  Target "nocxx" has source file
-
-    .*/Tests/RunCMake/CXXModules/sources/module.cxx
+    [^
+]*/Tests/RunCMake/CXXModules/sources/module.cxx
 
   in a "FILE_SET TYPE CXX_MODULES" but it is not scheduled for compilation.
 )
-)*
-CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt
index 95d73b1..3f3a547 100644
--- a/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NoCXX20-stderr.txt
@@ -1,20 +1,9 @@
-CMake Warning \(dev\) at NoCXX20.cmake:4 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
-CMake Error in CMakeLists.txt:
-  The "nocxx20" target has C\+\+ module sources but is not using at least
-  "cxx_std_20"
-
-(
-CMake Error in CMakeLists.txt:
-(  The "nocxx20" target has C\+\+ module sources but is not using at least
-  "cxx_std_20"
-|  The "nocxx20" target contains C\+\+ module sources which are not supported by
-  the generator
+(CMake Error in CMakeLists.txt:
+  The target named "nocxx20" has C\+\+ sources that use modules, but does not
+  include "cxx_std_20" \(or newer\) among its `target_compile_features`; found
+  "cxx_std_17"\.
+|CMake Error in CMakeLists.txt:
+  The target named "nocxx20" has C\+\+ sources that may use modules, but
+  modules are not supported by this generator\.  See the cmake-cxxmodules\(7\)
+  manual and the CMAKE_CXX_SCAN_FOR_MODULES variable\.
 )
-)*
-CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt
deleted file mode 100644
index aa99af0..0000000
--- a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-stderr.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-CMake Warning \(dev\) at NoCXX20ModuleFlag.cmake:6 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
-CMake Error in CMakeLists.txt:
-  The "noexperimentalflag" target has C\+\+ module sources but its experimental
-  support has not been requested
-
-(
-CMake Error in CMakeLists.txt:
-(  The "noexperimentalflag" target has C\+\+ module sources but its experimental
-  support has not been requested
-|  The "noexperimentalflag" target contains C\+\+ module sources which are not
-  supported by the generator
-)
-)*
-CMake Generate step failed.  Build files cannot be regenerated correctly.
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake b/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake
deleted file mode 100644
index f6ccb99..0000000
--- a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag.cmake
+++ /dev/null
@@ -1,14 +0,0 @@
-enable_language(CXX)
-
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "echo")
-
-add_library(noexperimentalflag)
-target_sources(noexperimentalflag
-  PUBLIC
-    FILE_SET fs TYPE CXX_MODULES FILES
-      sources/module.cxx)
-target_compile_features(noexperimentalflag
-  PRIVATE
-    cxx_std_20)
-
-unset(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API)
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt
index b63d291..c7e2979 100644
--- a/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NoDyndepSupport-stderr.txt
@@ -1,25 +1,12 @@
-CMake Warning \(dev\) at NoDyndepSupport.cmake:9 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
-(CMake Error:
+^(CMake Error:
   The Ninja generator does not support C\+\+20 modules using Ninja version
 
-    .*
+    [^
+]+
 
-  due to lack of required features.  Ninja 1.11 or higher is required.
-
+  due to lack of required features\.  Ninja 1\.11 or higher is required\.
 |CMake Error in CMakeLists.txt:
-  The "nodyndep" target contains C\+\+ module sources which are not supported
-  by the generator
-
-(
-CMake Error in CMakeLists.txt:
-  The "nodyndep" target contains C\+\+ module sources which are not supported
-  by the generator
-
-)*)
-CMake Generate step failed.  Build files cannot be regenerated correctly.
+  The target named "nodyndep" has C\+\+ sources that may use modules, but
+  modules are not supported by this generator\.  See the cmake-cxxmodules\(7\)
+  manual and the CMAKE_CXX_SCAN_FOR_MODULES variable\.
+)
diff --git a/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake b/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake
index 71d0f26..6e2d94b 100644
--- a/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake
+++ b/Tests/RunCMake/CXXModules/NoDyndepSupport.cmake
@@ -1,5 +1,5 @@
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 if (NOT CMAKE_CXX_STANDARD_DEFAULT)
   set(CMAKE_CXX_STANDARD_DEFAULT "11")
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/CXXModules/NoScanningSourceFileProperty-result.txt
diff --git a/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty-stderr.txt b/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty-stderr.txt
new file mode 100644
index 0000000..ed78672
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty-stderr.txt
@@ -0,0 +1,10 @@
+(CMake Error in CMakeLists.txt:
+  The target named "noscanning-sf-property" has C\+\+ sources that may use
+  modules, but the compiler does not provide a way to discover the import
+  graph dependencies\.  See the cmake-cxxmodules\(7\) manual and the
+  CMAKE_CXX_SCAN_FOR_MODULES variable\.
+|CMake Error in CMakeLists.txt:
+  The target named "noscanning-sf-property" has C\+\+ sources that may use
+  modules, but modules are not supported by this generator\.  See the
+  cmake-cxxmodules\(7\) manual and the CMAKE_CXX_SCAN_FOR_MODULES variable\.
+)
diff --git a/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty.cmake b/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty.cmake
new file mode 100644
index 0000000..f356a11
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoScanningSourceFileProperty.cmake
@@ -0,0 +1,13 @@
+enable_language(CXX)
+unset(CMAKE_CXX_SCANDEP_SOURCE)
+
+add_executable(noscanning-sf-property
+  sources/module-use.cxx)
+set_target_properties(noscanning-sf-property
+  PROPERTIES
+    CXX_STANDARD 20
+    CXX_STANDARD_REQUIRED ON
+    CXX_SCAN_FOR_MODULES 0)
+set_source_files_properties(sources/module-use.cxx
+  PROPERTIES
+    CXX_SCAN_FOR_MODULES 1)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/CXXModules/NoScanningTargetProperty-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/CXXModules/NoScanningTargetProperty-result.txt
diff --git a/Tests/RunCMake/CXXModules/NoScanningTargetProperty-stderr.txt b/Tests/RunCMake/CXXModules/NoScanningTargetProperty-stderr.txt
new file mode 100644
index 0000000..9dfb3e9
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoScanningTargetProperty-stderr.txt
@@ -0,0 +1,10 @@
+(CMake Error in CMakeLists.txt:
+  The target named "noscanning-target-property" has C\+\+ sources that may use
+  modules, but the compiler does not provide a way to discover the import
+  graph dependencies\.  See the cmake-cxxmodules\(7\) manual and the
+  CMAKE_CXX_SCAN_FOR_MODULES variable\.
+|CMake Error in CMakeLists.txt:
+  The target named "noscanning-target-property" has C\+\+ sources that may use
+  modules, but modules are not supported by this generator\.  See the
+  cmake-cxxmodules\(7\) manual and the CMAKE_CXX_SCAN_FOR_MODULES variable\.
+)
diff --git a/Tests/RunCMake/CXXModules/NoScanningTargetProperty.cmake b/Tests/RunCMake/CXXModules/NoScanningTargetProperty.cmake
new file mode 100644
index 0000000..97a3d44
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoScanningTargetProperty.cmake
@@ -0,0 +1,10 @@
+enable_language(CXX)
+unset(CMAKE_CXX_SCANDEP_SOURCE)
+
+add_executable(noscanning-target-property
+  sources/module-use.cxx)
+set_target_properties(noscanning-target-property
+  PROPERTIES
+    CXX_STANDARD 20
+    CXX_STANDARD_REQUIRED ON
+    CXX_SCAN_FOR_MODULES 1)
diff --git a/Tests/RunCMake/CXXModules/NoScanningVariable.cmake b/Tests/RunCMake/CXXModules/NoScanningVariable.cmake
new file mode 100644
index 0000000..4bb6a70
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/NoScanningVariable.cmake
@@ -0,0 +1,21 @@
+# Enable scanning by default for targets that explicitly use C++ 20.
+cmake_policy(SET CMP0155 NEW)
+
+enable_language(CXX)
+
+# Hide any real scanning rule that may be available.
+unset(CMAKE_CXX_SCANDEP_SOURCE)
+
+# Explicitly enable C++20 for all targets.
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# Explicitly suppress scanning so that support is not required.
+set(CMAKE_CXX_SCAN_FOR_MODULES OFF)
+
+add_executable(noscanning-variable main-no-use.cxx)
+
+# Verify that CMAKE_CXX_SCAN_FOR_MODULES is propagated into the check.
+# FIXME: Unset CMAKE_CXX_SCANDEP_SOURCE inside try_compile so this
+# test can verify behavior on newer compilers too.
+try_compile(result SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/main-no-use.cxx)
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt
index 78d3dc6..e5f0f0b 100644
--- a/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules-stderr.txt
@@ -1,10 +1,3 @@
-CMake Warning \(dev\) at NotCXXSourceModules.cmake:6 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
 CMake Error in CMakeLists.txt:
   Target "not-cxx-source" contains the source
 
diff --git a/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake b/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake
index c4e039f..f802cb7 100644
--- a/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake
+++ b/Tests/RunCMake/CXXModules/NotCXXSourceModules.cmake
@@ -1,6 +1,6 @@
 enable_language(C)
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 add_library(not-cxx-source)
 target_sources(not-cxx-source
diff --git a/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt b/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt
index 52f4072..0af7b93 100644
--- a/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt
+++ b/Tests/RunCMake/CXXModules/NotCompiledSourceModules-stderr.txt
@@ -1,10 +1,3 @@
-CMake Warning \(dev\) at NotCompiledSourceModules.cmake:5 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:6 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
 (CMake Error in CMakeLists.txt:
   Target "not-cxx-source" has source file
 
diff --git a/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake b/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake
index 0bab635..11d789d 100644
--- a/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake
+++ b/Tests/RunCMake/CXXModules/NotCompiledSourceModules.cmake
@@ -1,5 +1,5 @@
 enable_language(CXX)
-set(CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE "")
+set(CMAKE_CXX_SCANDEP_SOURCE "")
 
 add_library(not-cxx-source)
 target_sources(not-cxx-source
diff --git a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
index 89c198f..e13b2d4 100644
--- a/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CXXModules/RunCMakeTest.cmake
@@ -3,8 +3,8 @@
 # For `if (IN_LIST)`
 cmake_policy(SET CMP0057 NEW)
 
-run_cmake(compiler_introspection)
-include("${RunCMake_BINARY_DIR}/compiler_introspection-build/info.cmake")
+run_cmake(Inspect)
+include("${RunCMake_BINARY_DIR}/Inspect-build/info.cmake")
 
 # Test negative cases where C++20 modules do not work.
 run_cmake(NoCXX)
@@ -13,11 +13,16 @@
   # standard. If the compiler forces a standard to be used, skip it.
   if (NOT forced_cxx_standard)
     run_cmake(NoCXX20)
+    if(CMAKE_CXX_STANDARD_DEFAULT AND CMAKE_CXX20_STANDARD_COMPILE_OPTION)
+      run_cmake_with_options(ImplicitCXX20 -DCMAKE_CXX20_STANDARD_COMPILE_OPTION=${CMAKE_CXX20_STANDARD_COMPILE_OPTION})
+    endif()
   endif ()
 
-  # This test uses C++20, but another prerequisite is missing, so forced
-  # standards don't matter.
-  run_cmake(NoCXX20ModuleFlag)
+  run_cmake(NoScanningSourceFileProperty)
+  run_cmake(NoScanningTargetProperty)
+  run_cmake(NoScanningVariable)
+  run_cmake(CMP0155-OLD)
+  run_cmake(CMP0155-NEW)
 endif ()
 
 if (RunCMake_GENERATOR MATCHES "Ninja")
@@ -149,6 +154,8 @@
   run_cxx_module_test(duplicate)
   set(RunCMake_CXXModules_NO_TEST 1)
   run_cxx_module_test(circular)
+  run_cxx_module_test(try-compile)
+  run_cxx_module_test(try-run)
   unset(RunCMake_CXXModules_NO_TEST)
   run_cxx_module_test(same-src-name)
   run_cxx_module_test(scan_properties)
@@ -186,7 +193,24 @@
 if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
   run_cxx_module_test(export-interface-no-properties-build)
   run_cxx_module_test(export-interface-build)
+  run_cxx_module_test(export-include-directories-build)
+  run_cxx_module_test(export-usage-build)
   run_cxx_module_test(export-bmi-and-interface-build)
+
+  if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND
+      "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION)
+    set(test_suffix export-interface-build)
+    run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build")
+
+    set(test_suffix export-interface-no-properties-build)
+    run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DNO_PROPERTIES=1)
+
+    set(test_suffix export-include-directories-build)
+    run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DINCLUDE_PROPERTIES=1)
+
+    set(test_suffix export-bmi-and-interface-build)
+    run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-build" -DWITH_BMIS=1)
+  endif ()
 endif ()
 
 # All of the following tests perform installation.
@@ -200,6 +224,25 @@
   if ("export_bmi" IN_LIST CMake_TEST_MODULE_COMPILATION)
     run_cxx_module_test(export-interface-no-properties-install)
     run_cxx_module_test(export-interface-install)
+    run_cxx_module_test(export-include-directories-install)
+    run_cxx_module_test(export-usage-install)
     run_cxx_module_test(export-bmi-and-interface-install)
+
+    if ("collation" IN_LIST CMake_TEST_MODULE_COMPILATION AND
+        "bmionly" IN_LIST CMake_TEST_MODULE_COMPILATION)
+      set(RunCMake_CXXModules_INSTALL 0)
+      set(test_suffix export-interface-install)
+      run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install")
+
+      set(test_suffix export-interface-no-properties-install)
+      run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DNO_PROPERTIES=1)
+
+      set(test_suffix export-include-directories-install)
+      run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DINCLUDE_PROPERTIES=1)
+
+      set(test_suffix export-bmi-and-interface-install)
+      run_cxx_module_test(import-modules "import-modules-${test_suffix}" "-DCMAKE_PREFIX_PATH=${RunCMake_BINARY_DIR}/examples/${test_suffix}-install" -DWITH_BMIS=1)
+      set(RunCMake_CXXModules_INSTALL 1)
+    endif ()
   endif ()
 endif ()
diff --git a/Tests/RunCMake/CXXModules/check-json.cmake b/Tests/RunCMake/CXXModules/check-json.cmake
index 19d0c8a..bb04b36 100644
--- a/Tests/RunCMake/CXXModules/check-json.cmake
+++ b/Tests/RunCMake/CXXModules/check-json.cmake
@@ -2,19 +2,20 @@
 cmake_policy(SET CMP0057 NEW)
 
 function (json_placeholders in out)
-  string(REPLACE "<CONFIG>" "${CMAKE_BUILD_TYPE}" in "${in}")
+  string(REPLACE "<CONFIG>" "${CXXModules_config}" in "${in}")
   if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
-    string(REPLACE "<CONFIG_DIR>" "${CMAKE_BUILD_TYPE}/" in "${in}")
+    string(REPLACE "<CONFIG_DIR>" "/${CXXModules_config}" in "${in}")
   else ()
     string(REPLACE "<CONFIG_DIR>" "" in "${in}")
   endif ()
   if (CMAKE_BUILD_TYPE)
-    string(REPLACE "<CONFIG_FORCE>" "${CMAKE_BUILD_TYPE}" in "${in}")
+    string(REPLACE "<CONFIG_FORCE>" "${CXXModules_config}" in "${in}")
   else ()
     string(REPLACE "<CONFIG_FORCE>" "noconfig" in "${in}")
   endif ()
   string(REPLACE "<SOURCE_DIR>" "${RunCMake_SOURCE_DIR}" in "${in}")
   string(REPLACE "<BINARY_DIR>" "${RunCMake_TEST_BINARY_DIR}" in "${in}")
+  string(REPLACE "<OBJEXT>" "${CMAKE_CXX_OUTPUT_EXTENSION}" in "${in}")
   set("${out}" "${in}" PARENT_SCOPE)
 endfunction ()
 
@@ -22,6 +23,7 @@
   if (NOT actual_type STREQUAL expect_type)
     list(APPEND RunCMake_TEST_FAILED
       "Type mismatch at ${path}: ${actual_type} vs. ${expect_type}")
+    set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
     return ()
   endif ()
 
@@ -53,6 +55,8 @@
   elseif (actual_type STREQUAL OBJECT)
     check_json_object("${path}" "${actual_value}" "${expect_value}")
   endif ()
+
+  set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
 endfunction ()
 
 # Check that two arrays are the same.
@@ -82,6 +86,8 @@
     string(JSON expect_value GET "${expect}" "${idx}")
     check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}")
   endforeach ()
+
+  set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
 endfunction ()
 
 # Check that two inner objects are the same.
@@ -131,12 +137,12 @@
   if (actual_keys_missed)
     string(REPLACE ";" ", " actual_keys_missed_text "${actual_keys_missed}")
     list(APPEND RunCMake_TEST_FAILED
-      "Missing expected members at ${path}: ${actual_keys_missed_text}")
+      "Extra unexpected members at ${path}: ${actual_keys_missed_text}")
   endif ()
   if (expect_keys_missed)
     string(REPLACE ";" ", " expect_keys_missed_text "${expect_keys_missed}")
     list(APPEND RunCMake_TEST_FAILED
-      "Extra unexpected members at ${path}: ${expect_keys_missed_text}")
+      "Missing expected members at ${path}: ${expect_keys_missed_text}")
   endif ()
 
   foreach (key IN LISTS common_keys)
@@ -148,13 +154,15 @@
     string(JSON expect_value GET "${expect}" "${key}")
     check_json_value("${new_path}" "${actual_type}" "${expect_type}" "${actual_value}" "${expect_value}")
   endforeach ()
+
+  set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
 endfunction ()
 
 # Check that two JSON objects are the same.
 function (check_json actual expect)
   check_json_object("" "${actual}" "${expect}")
-endfunction ()
 
-string(REPLACE ";" "; " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
+  set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE)
+endfunction ()
 
 cmake_policy(POP)
diff --git a/Tests/RunCMake/CXXModules/compiler_introspection.cmake b/Tests/RunCMake/CXXModules/compiler_introspection.cmake
deleted file mode 100644
index 0e61383..0000000
--- a/Tests/RunCMake/CXXModules/compiler_introspection.cmake
+++ /dev/null
@@ -1,26 +0,0 @@
-enable_language(CXX)
-
-set(info "")
-
-# See `Modules/Compiler/MSVC-CXX.cmake` for this. If there is explicitly no
-# default, the feature list is populated to be everything.
-if (DEFINED CMAKE_CXX_STANDARD_DEFAULT AND
-    CMAKE_CXX_STANDARD_DEFAULT STREQUAL "")
-  set(CMAKE_CXX_COMPILE_FEATURES "")
-endif ()
-
-# Detect if the environment forces a C++ standard, let the test selection know.
-set(forced_cxx_standard 0)
-if (CMAKE_CXX_FLAGS MATCHES "-std=")
-  set(forced_cxx_standard 1)
-endif ()
-
-# Forward information about the C++ compile features.
-string(APPEND info "\
-set(CMAKE_CXX_COMPILE_FEATURES \"${CMAKE_CXX_COMPILE_FEATURES}\")
-set(CMAKE_MAKE_PROGRAM \"${CMAKE_MAKE_PROGRAM}\")
-set(forced_cxx_standard \"${forced_cxx_standard}\")
-set(CMAKE_CXX_COMPILER_VERSION \"${CMAKE_CXX_COMPILER_VERSION}\")
-")
-
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/info.cmake" "${info}")
diff --git a/Tests/RunCMake/CXXModules/examples/circular-stderr.txt b/Tests/RunCMake/CXXModules/examples/circular-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/circular-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt
index 4d1997c..c1b000e 100644
--- a/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/circular/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_circular CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake
index ff7219a..1016afd 100644
--- a/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake
+++ b/Tests/RunCMake/CXXModules/examples/cxx-modules-rules.cmake
@@ -1,17 +1,5 @@
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
-if (NOT EXISTS "${CMake_TEST_MODULE_COMPILATION_RULES}")
-  message(FATAL_ERROR
-    "The `CMake_TEST_MODULE_COMPILATION_RULES` file must be specified "
-    "for these tests to operate.")
-endif ()
-
-include("${CMake_TEST_MODULE_COMPILATION_RULES}")
-
-if (NOT CMake_TEST_CXXModules_UUID STREQUAL "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
-  message(FATAL_ERROR
-    "The compilation rule file needs updated for changes in the test "
-    "suite. Please see the history for what needs to be updated.")
+if (CMake_TEST_MODULE_COMPILATION_RULES)
+  include("${CMake_TEST_MODULE_COMPILATION_RULES}")
 endif ()
 
 include(CTest)
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt b/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/deep-chain-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt
index 515b240..153a2df 100644
--- a/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/deep-chain/CMakeLists.txt
@@ -1,8 +1,16 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_deep_chain CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
 
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+  include(CheckCompilerFlag)
+  check_compiler_flag(CXX "-Wread-modules-implicitly" have_implicit_module_warning)
+  if (have_implicit_module_warning)
+    add_compile_options(-Werror=read-modules-implicitly)
+  endif ()
+endif ()
+
 add_library(a STATIC)
 target_sources(a
   PUBLIC
diff --git a/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt b/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/duplicate-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt
index 27be7a8..51ad167 100644
--- a/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/duplicate/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_duplicate CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt
deleted file mode 100644
index be89b8c..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt
index 71e7b62..cbe8fb7 100644
--- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_bmi_and_interfaces CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt
index d227e55..0666ff1 100644
--- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-build/test/CMakeLists.txt
@@ -1,8 +1,6 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 find_package(export_bmi_and_interfaces REQUIRED)
 
 if (NOT TARGET CXXModules::export_bmi_and_interfaces)
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt
deleted file mode 100644
index be89b8c..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt
index e675507..c8679d6 100644
--- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_bmi_and_interfaces CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt
index d46d28b..36b0e8b 100644
--- a/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-bmi-and-interface-install/test/CMakeLists.txt
@@ -1,8 +1,6 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 find_package(export_bmi_and_interfaces REQUIRED)
 
 if (NOT TARGET CXXModules::export_bmi_and_interfaces)
diff --git a/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt
deleted file mode 100644
index e868787..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-compile-commands-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:9 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-compile-commands/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-compile-commands/CMakeLists.txt
index 2eb4d5f..b9c7bb3 100644
--- a/Tests/RunCMake/CXXModules/examples/export-compile-commands/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-compile-commands/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_compile_commands CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/CMakeLists.txt
new file mode 100644
index 0000000..560994e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/CMakeLists.txt
@@ -0,0 +1,63 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_export_include_directories CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_include_directories STATIC
+  include/include.h)
+target_sources(export_include_directories
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx
+        subdir/importable.cxx
+  )
+target_compile_features(export_include_directories PUBLIC cxx_std_20)
+target_include_directories(export_include_directories
+  PRIVATE
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>")
+
+add_library(no_modules STATIC no_modules.cxx)
+
+install(TARGETS export_include_directories no_modules
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu")
+export(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-targets.cmake"
+  CXX_MODULES_DIRECTORY "export_include_directories-cxx-modules")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_include_directories-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_include_directories_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_source_dir=${CMAKE_CURRENT_SOURCE_DIR}"
+    "-Dexpected_binary_dir=${CMAKE_CURRENT_BINARY_DIR}"
+    "-Dexport_include_directories_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/forward.cxx
new file mode 100644
index 0000000..7f53271
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/forward.cxx
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/importable.cxx
new file mode 100644
index 0000000..6a1d83e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/importable.cxx
@@ -0,0 +1,18 @@
+module;
+
+#include "include/include.h"
+
+#ifndef include_h_included
+#  error "include define not found"
+#endif
+
+export module importable;
+
+extern "C++" {
+int forwarding();
+}
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/include/include.h b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/include/include.h
new file mode 100644
index 0000000..e3eee34
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/include/include.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define include_h_included
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/no_modules.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/no_modules.cxx
new file mode 100644
index 0000000..eea854f
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/no_modules.cxx
@@ -0,0 +1,3 @@
+void no_modules()
+{
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/private.cxx
new file mode 100644
index 0000000..c5b719a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/private.cxx
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/subdir/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/subdir/importable.cxx
new file mode 100644
index 0000000..07d6af6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/subdir/importable.cxx
@@ -0,0 +1,6 @@
+export module subdir_importable;
+
+export int from_subdir()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/test/CMakeLists.txt
new file mode 100644
index 0000000..6931176
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-build/test/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.24)
+project(cxx_modules_library NONE)
+
+find_package(export_include_directories REQUIRED)
+
+if (NOT TARGET CXXModules::export_include_directories)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+get_property(include_directories TARGET CXXModules::export_include_directories
+  PROPERTY IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES)
+foreach (include_directory IN LISTS include_directories)
+  if (NOT EXISTS "${include_directory}")
+    message(FATAL_ERROR
+      "Missing include directory in C++ module interface CXXModules::export_include_directories:\n  ${include_directory}")
+  endif ()
+endforeach ()
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/CMakeLists.txt
new file mode 100644
index 0000000..3d4e687
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/CMakeLists.txt
@@ -0,0 +1,69 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_export_include_directories CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_include_directories STATIC
+  include/include.h)
+target_sources(export_include_directories
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx
+        subdir/importable.cxx
+  )
+target_compile_features(export_include_directories PUBLIC cxx_std_20)
+target_include_directories(export_include_directories
+  PRIVATE
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
+    "$<INSTALL_INTERFACE:include>")
+
+add_library(no_modules STATIC no_modules.cxx)
+
+install(TARGETS export_include_directories no_modules
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu")
+install(DIRECTORY include
+  DESTINATION "include")
+install(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  DESTINATION "lib/cmake/export_include_directories"
+  FILE "export_include_directories-targets.cmake"
+  CXX_MODULES_DIRECTORY "export_include_directories-cxx-modules")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_include_directories-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_include_directories-config.cmake"
+  DESTINATION "lib/cmake/export_include_directories")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_include_directories_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_source_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu"
+    "-Dexpected_binary_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/bmi"
+    "-Dexport_include_directories_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_include_directories"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/forward.cxx
new file mode 100644
index 0000000..7f53271
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/forward.cxx
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/importable.cxx
new file mode 100644
index 0000000..6a1d83e
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/importable.cxx
@@ -0,0 +1,18 @@
+module;
+
+#include "include/include.h"
+
+#ifndef include_h_included
+#  error "include define not found"
+#endif
+
+export module importable;
+
+extern "C++" {
+int forwarding();
+}
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/include/include.h b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/include/include.h
new file mode 100644
index 0000000..e3eee34
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/include/include.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#define include_h_included
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/no_modules.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/no_modules.cxx
new file mode 100644
index 0000000..eea854f
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/no_modules.cxx
@@ -0,0 +1,3 @@
+void no_modules()
+{
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/private.cxx
new file mode 100644
index 0000000..c5b719a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/private.cxx
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/subdir/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/subdir/importable.cxx
new file mode 100644
index 0000000..07d6af6
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/subdir/importable.cxx
@@ -0,0 +1,6 @@
+export module subdir_importable;
+
+export int from_subdir()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-include-directories-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/test/CMakeLists.txt
new file mode 100644
index 0000000..4bc2b4b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-include-directories-install/test/CMakeLists.txt
@@ -0,0 +1,16 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_library NONE)
+
+find_package(export_include_directories REQUIRED)
+
+if (NOT TARGET CXXModules::export_include_directories)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+get_property(file_sets TARGET CXXModules::export_include_directories
+  PROPERTY INTERFACE_CXX_MODULE_SETS)
+if (NOT file_sets STREQUAL "modules")
+  message(FATAL_ERROR
+    "Incorrect exported file sets in CXXModules::export_include_directories:\n  ${file_sets}")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt
deleted file mode 100644
index 28a7b1f..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-interface-build-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9] \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt
index 136e885..df471af 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_interfaces CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt
index 3cd156a..95fb6e8 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-build/test/CMakeLists.txt
@@ -1,8 +1,6 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 find_package(export_interfaces REQUIRED)
 
 if (NOT TARGET CXXModules::export_interfaces)
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt
deleted file mode 100644
index be89b8c..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-interface-install-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt
index df87980..019ae4a 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_interfaces CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt
index 71bf86c..725090e 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-install/test/CMakeLists.txt
@@ -1,8 +1,6 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 find_package(export_interfaces REQUIRED)
 
 if (NOT TARGET CXXModules::export_interfaces)
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-stderr.txt
deleted file mode 100644
index be89b8c..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/CMakeLists.txt
index a93e3a4..decadf7 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_interfaces_no_properties CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt
index 0c094ac..cd8d1df 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-build/test/CMakeLists.txt
@@ -1,8 +1,6 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 find_package(export_interfaces_no_properties REQUIRED)
 
 if (NOT TARGET CXXModules::export_interfaces_no_properties)
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-stderr.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-stderr.txt
deleted file mode 100644
index be89b8c..0000000
--- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9]+ \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/CMakeLists.txt
index 99e67e7..be32ecd 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_export_interfaces CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt
index 0c094ac..cd8d1df 100644
--- a/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/export-interface-no-properties-install/test/CMakeLists.txt
@@ -1,8 +1,6 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library NONE)
 
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
 find_package(export_interfaces_no_properties REQUIRED)
 
 if (NOT TARGET CXXModules::export_interfaces_no_properties)
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt
new file mode 100644
index 0000000..a58e287
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/CMakeLists.txt
@@ -0,0 +1,110 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_export_usage CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_usage STATIC)
+target_sources(export_usage
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(export_usage PUBLIC cxx_std_20)
+
+list(APPEND CMAKE_CXX_KNOWN_FEATURES
+  exported
+  buildiface
+  installiface
+  buildlocaliface)
+
+target_include_directories(export_usage
+  PRIVATE
+    "/usr/exported"
+    "$<BUILD_INTERFACE:/usr/buildiface>"
+    "$<INSTALL_INTERFACE:/usr/installiface>"
+    "$<BUILD_LOCAL_INTERFACE:/usr/buildlocaliface>")
+target_compile_definitions(export_usage
+  PRIVATE
+    "exported"
+    "$<BUILD_INTERFACE:buildiface>"
+    "$<INSTALL_INTERFACE:installiface>"
+    "$<BUILD_LOCAL_INTERFACE:buildlocaliface>")
+target_compile_features(export_usage
+  PRIVATE
+    "cxx_std_11"
+    "$<BUILD_INTERFACE:cxx_std_14>"
+    "$<INSTALL_INTERFACE:cxx_std_17>"
+    "$<BUILD_LOCAL_INTERFACE:cxx_std_20>")
+
+if (MSVC)
+  set(variable_flag "-constexpr:depth")
+else ()
+  set(variable_flag "-fconstexpr-depth=")
+endif ()
+
+target_compile_options(export_usage
+  PRIVATE
+    "${variable_flag}100"
+    "$<BUILD_INTERFACE:${variable_flag}200>"
+    "$<INSTALL_INTERFACE:${variable_flag}300>"
+    "$<BUILD_LOCAL_INTERFACE:${variable_flag}400>")
+
+add_library(export_used INTERFACE)
+add_library(export_build INTERFACE)
+add_library(export_install INTERFACE)
+add_library(export_never INTERFACE)
+
+target_link_libraries(export_usage
+  PRIVATE
+    "export_used"
+    "$<BUILD_INTERFACE:export_build>"
+    "$<INSTALL_INTERFACE:export_install>"
+    "$<BUILD_LOCAL_INTERFACE:export_never>")
+
+install(TARGETS export_usage
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu")
+export(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-targets.cmake")
+install(TARGETS export_used export_build export_install
+  EXPORT CXXModulesDeps)
+export(EXPORT CXXModulesDeps
+  NAMESPACE CXXModules::
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-dep-targets.cmake")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-dep-targets.cmake\")
+include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_usage_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_dir=${CMAKE_CURRENT_SOURCE_DIR}"
+    "-Dexport_interfaces_flag=${variable_flag}"
+    "-Dexport_usage_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx
new file mode 100644
index 0000000..7f53271
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/forward.cxx
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx
new file mode 100644
index 0000000..8dfc41b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/importable.cxx
@@ -0,0 +1,10 @@
+export module importable;
+
+extern "C++" {
+int forwarding();
+}
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx
new file mode 100644
index 0000000..c5b719a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/private.cxx
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt
new file mode 100644
index 0000000..c84153d
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-build/test/CMakeLists.txt
@@ -0,0 +1,67 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_library NONE)
+
+find_package(export_usage REQUIRED)
+
+if (NOT TARGET CXXModules::export_usage)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (NOT TARGET CXXModules::export_used)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (NOT TARGET CXXModules::export_build)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (NOT TARGET CXXModules::export_install)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (TARGET CXXModules::export_never)
+  message(FATAL_ERROR
+    "Extra imported target")
+endif ()
+
+function (check_property expected property)
+  get_property(actual TARGET CXXModules::export_usage
+    PROPERTY "${property}")
+  if (NOT actual STREQUAL expected)
+    message(SEND_ERROR
+      "Mismatch for ${property}:\n  expected: ${expected}\n  actual: ${actual}")
+  endif ()
+endfunction ()
+
+check_property("/usr/exported;/usr/buildiface" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES")
+check_property("exported;buildiface" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS")
+check_property("cxx_std_20;cxx_std_11;cxx_std_14" "IMPORTED_CXX_MODULES_COMPILE_FEATURES")
+check_property("${export_interfaces_flag}100;${export_interfaces_flag}200" "IMPORTED_CXX_MODULES_COMPILE_OPTIONS")
+check_property("$<COMPILE_ONLY:CXXModules::export_used>;$<COMPILE_ONLY:CXXModules::export_build>" "IMPORTED_CXX_MODULES_LINK_LIBRARIES")
+
+# Extract the export-dependent targets from the export file.
+file(STRINGS "${export_usage_DIR}/export_usage-targets.cmake" usage_dependent_targets
+  REGEX "foreach._target ")
+# Rudimentary argument splitting.
+string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}")
+# Keep only "target" names.
+list(FILTER usage_dependent_targets INCLUDE REGEX "CXXModules::")
+# Strip quotes.
+string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}")
+
+if (NOT "CXXModules::export_used" IN_LIST usage_dependent_targets)
+  message(SEND_ERROR
+    "The main export does not require the 'CXXModules::export_used' target")
+endif ()
+if (NOT "CXXModules::export_build" IN_LIST usage_dependent_targets)
+  message(SEND_ERROR
+    "The main export does not require the 'CXXModules::export_build' target")
+endif ()
+if ("CXXModules::export_install" IN_LIST usage_dependent_targets)
+  message(SEND_ERROR
+    "The main export requires the 'CXXModules::export_install' target")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt
new file mode 100644
index 0000000..3d7d67b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/CMakeLists.txt
@@ -0,0 +1,114 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_export_usage CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+add_library(export_usage STATIC)
+target_sources(export_usage
+  PRIVATE
+    forward.cxx
+  PRIVATE
+    FILE_SET modules_private TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        private.cxx
+  PUBLIC
+    FILE_SET modules TYPE CXX_MODULES
+      BASE_DIRS
+        "${CMAKE_CURRENT_SOURCE_DIR}"
+      FILES
+        importable.cxx)
+target_compile_features(export_usage PUBLIC cxx_std_20)
+
+list(APPEND CMAKE_CXX_KNOWN_FEATURES
+  exported
+  buildiface
+  installiface
+  buildlocaliface)
+
+target_include_directories(export_usage
+  PRIVATE
+    "/usr/exported"
+    "$<BUILD_INTERFACE:/usr/buildiface>"
+    "$<INSTALL_INTERFACE:/usr/installiface>"
+    "$<BUILD_LOCAL_INTERFACE:/usr/buildlocaliface>")
+target_compile_definitions(export_usage
+  PRIVATE
+    "exported"
+    "$<BUILD_INTERFACE:buildiface>"
+    "$<INSTALL_INTERFACE:installiface>"
+    "$<BUILD_LOCAL_INTERFACE:buildlocaliface>")
+target_compile_features(export_usage
+  PRIVATE
+    "cxx_std_11"
+    "$<BUILD_INTERFACE:cxx_std_14>"
+    "$<INSTALL_INTERFACE:cxx_std_17>"
+    "$<BUILD_LOCAL_INTERFACE:cxx_std_20>")
+
+if (MSVC)
+  set(variable_flag "-constexpr:depth")
+else ()
+  set(variable_flag "-fconstexpr-depth=")
+endif ()
+
+target_compile_options(export_usage
+  PRIVATE
+    "${variable_flag}100"
+    "$<BUILD_INTERFACE:${variable_flag}200>"
+    "$<INSTALL_INTERFACE:${variable_flag}300>"
+    "$<BUILD_LOCAL_INTERFACE:${variable_flag}400>")
+
+add_library(export_used INTERFACE)
+add_library(export_build INTERFACE)
+add_library(export_install INTERFACE)
+add_library(export_never INTERFACE)
+
+target_link_libraries(export_usage
+  PRIVATE
+    "export_used"
+    "$<BUILD_INTERFACE:export_build>"
+    "$<INSTALL_INTERFACE:export_install>"
+    "$<BUILD_LOCAL_INTERFACE:export_never>")
+
+install(TARGETS export_usage
+  EXPORT CXXModules
+  FILE_SET modules DESTINATION "lib/cxx/miu")
+install(EXPORT CXXModules
+  NAMESPACE CXXModules::
+  DESTINATION "lib/cmake/export_usage"
+  FILE "export_usage-targets.cmake")
+install(TARGETS export_used export_build export_install
+  EXPORT CXXModulesDeps)
+install(EXPORT CXXModulesDeps
+  NAMESPACE CXXModules::
+  DESTINATION "lib/cmake/export_usage"
+  FILE "export_usage-dep-targets.cmake")
+file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/export_usage-config.cmake"
+  "include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-dep-targets.cmake\")
+include(\"\${CMAKE_CURRENT_LIST_DIR}/export_usage-targets.cmake\")
+set(\${CMAKE_FIND_PACKAGE_NAME}_FOUND 1)
+")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/export_usage-config.cmake"
+  DESTINATION "lib/cmake/export_usage")
+
+set(generator
+  -G "${CMAKE_GENERATOR}")
+if (CMAKE_GENERATOR_TOOLSET)
+  list(APPEND generator
+    -T "${CMAKE_GENERATOR_TOOLSET}")
+endif ()
+if (CMAKE_GENERATOR_PLATFORM)
+  list(APPEND generator
+    -A "${CMAKE_GENERATOR_PLATFORM}")
+endif ()
+
+add_test(NAME export_usage_build
+  COMMAND
+    "${CMAKE_COMMAND}"
+    "-Dexpected_dir=${CMAKE_INSTALL_PREFIX}/lib/cxx/miu"
+    "-Dexport_interfaces_flag=${variable_flag}"
+    "-Dexport_usage_DIR=${CMAKE_INSTALL_PREFIX}/lib/cmake/export_usage"
+    ${generator}
+    -S "${CMAKE_CURRENT_SOURCE_DIR}/test"
+    -B "${CMAKE_CURRENT_BINARY_DIR}/test")
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx
new file mode 100644
index 0000000..7f53271
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/forward.cxx
@@ -0,0 +1,6 @@
+import priv;
+
+int forwarding()
+{
+  return from_private();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx
new file mode 100644
index 0000000..8dfc41b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/importable.cxx
@@ -0,0 +1,10 @@
+export module importable;
+
+extern "C++" {
+int forwarding();
+}
+
+export int from_import()
+{
+  return forwarding();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx b/Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx
new file mode 100644
index 0000000..c5b719a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/private.cxx
@@ -0,0 +1,6 @@
+export module priv;
+
+export int from_private()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt
new file mode 100644
index 0000000..ceb428b
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/export-usage-install/test/CMakeLists.txt
@@ -0,0 +1,67 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_library NONE)
+
+find_package(export_usage REQUIRED)
+
+if (NOT TARGET CXXModules::export_usage)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (NOT TARGET CXXModules::export_used)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (NOT TARGET CXXModules::export_build)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (NOT TARGET CXXModules::export_install)
+  message(FATAL_ERROR
+    "Missing imported target")
+endif ()
+
+if (TARGET CXXModules::export_never)
+  message(FATAL_ERROR
+    "Extra imported target")
+endif ()
+
+function (check_property expected property)
+  get_property(actual TARGET CXXModules::export_usage
+    PROPERTY "${property}")
+  if (NOT actual STREQUAL expected)
+    message(SEND_ERROR
+      "Mismatch for ${property}:\n  expected: ${expected}\n  actual  : ${actual}")
+  endif ()
+endfunction ()
+
+check_property("/usr/exported;/usr/installiface" "IMPORTED_CXX_MODULES_INCLUDE_DIRECTORIES")
+check_property("exported;installiface" "IMPORTED_CXX_MODULES_COMPILE_DEFINITIONS")
+check_property("cxx_std_20;cxx_std_11;cxx_std_17" "IMPORTED_CXX_MODULES_COMPILE_FEATURES")
+check_property("${export_interfaces_flag}100;${export_interfaces_flag}300" "IMPORTED_CXX_MODULES_COMPILE_OPTIONS")
+check_property("$<COMPILE_ONLY:CXXModules::export_used>;$<COMPILE_ONLY:CXXModules::export_install>" "IMPORTED_CXX_MODULES_LINK_LIBRARIES")
+
+# Extract the export-dependent targets from the export file.
+file(STRINGS "${export_usage_DIR}/export_usage-targets.cmake" usage_dependent_targets
+  REGEX "foreach._target ")
+# Rudimentary argument splitting.
+string(REPLACE " " ";" usage_dependent_targets "${usage_dependent_targets}")
+# Keep only "target" names.
+list(FILTER usage_dependent_targets INCLUDE REGEX "CXXModules::")
+# Strip quotes.
+string(REPLACE "\"" "" usage_dependent_targets "${usage_dependent_targets}")
+
+if (NOT "CXXModules::export_used" IN_LIST usage_dependent_targets)
+  message(SEND_ERROR
+    "The main export does not require the 'CXXModules::export_used' target")
+endif ()
+if ("CXXModules::export_build" IN_LIST usage_dependent_targets)
+  message(SEND_ERROR
+    "The main export requires the 'CXXModules::export_build' target")
+endif ()
+if (NOT "CXXModules::export_install" IN_LIST usage_dependent_targets)
+  message(SEND_ERROR
+    "The main export does not require the 'CXXModules::export_install' target")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt b/Tests/RunCMake/CXXModules/examples/generated-stderr.txt
deleted file mode 100644
index 1dd9876..0000000
--- a/Tests/RunCMake/CXXModules/examples/generated-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:12 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt
index 73f7ff7..efa825a 100644
--- a/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/generated/CMakeLists.txt
@@ -1,12 +1,16 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_generated CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
 
-configure_file(
-  "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx.in"
-  "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx"
-  COPYONLY)
+add_custom_command(
+  OUTPUT  "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx"
+  DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx.in"
+  COMMAND "${CMAKE_COMMAND}"
+          -E copy_if_different
+          "${CMAKE_CURRENT_SOURCE_DIR}/importable.cxx.in"
+          "${CMAKE_CURRENT_BINARY_DIR}/importable.cxx"
+  COMMENT "Copying 'importable.cxx'")
 
 add_executable(generated)
 target_sources(generated
diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt
new file mode 100644
index 0000000..8e1db5d
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/import-modules/CMakeLists.txt
@@ -0,0 +1,26 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_import_interfaces CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+if (NO_PROPERTIES)
+  set(package_name "export_interfaces_no_properties")
+elseif (WITH_BMIS)
+  set(package_name "export_bmi_and_interfaces")
+elseif (INCLUDE_PROPERTIES)
+  set(package_name "export_include_directories")
+else ()
+  set(package_name "export_interfaces")
+endif ()
+set(target_name "CXXModules::${package_name}")
+
+find_package("${package_name}" REQUIRED)
+
+add_executable(use_import_interfaces)
+target_sources(use_import_interfaces
+  PRIVATE
+    use.cxx)
+target_compile_features(use_import_interfaces PRIVATE cxx_std_20)
+target_link_libraries(use_import_interfaces PRIVATE "${target_name}")
+
+add_test(NAME use_import_interfaces COMMAND use_import_interfaces)
diff --git a/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx b/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx
new file mode 100644
index 0000000..2da1913
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/import-modules/use.cxx
@@ -0,0 +1,12 @@
+#if defined(__has_include)
+#  if __has_include(<include/include.h>)
+#    error "include directories leaked from private module requirements"
+#  endif
+#endif
+
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt
index efaca0e..0beb672 100644
--- a/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/install-bmi-and-interfaces/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_install_bmi_and_interfaces CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt b/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/install-bmi-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt
index 4e039f9..4047612 100644
--- a/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/install-bmi/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_install_bmi CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt
deleted file mode 100644
index 79c5637..0000000
--- a/Tests/RunCMake/CXXModules/examples/internal-partitions-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt
index f5e9d94..bf99f7c 100644
--- a/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/internal-partitions/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_internal_partitions CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt
deleted file mode 100644
index 79c5637..0000000
--- a/Tests/RunCMake/CXXModules/examples/library-shared-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt b/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt
deleted file mode 100644
index 79c5637..0000000
--- a/Tests/RunCMake/CXXModules/examples/library-static-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt
index 27fd94f..97d2325 100644
--- a/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/library/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_library CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt b/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt
deleted file mode 100644
index 4709399..0000000
--- a/Tests/RunCMake/CXXModules/examples/object-library-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:[0-9]* \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/object-library/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/object-library/CMakeLists.txt
index 238e30a..c858b3e 100644
--- a/Tests/RunCMake/CXXModules/examples/object-library/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/object-library/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_objlib CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt b/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt
deleted file mode 100644
index 79c5637..0000000
--- a/Tests/RunCMake/CXXModules/examples/partitions-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:10 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt
index 3a7b0d4..07d32ae 100644
--- a/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/partitions/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_partitions CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt b/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/public-req-private-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt
index 600fec4..f59f237 100644
--- a/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/public-req-private/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_public_req_private CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt b/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/req-private-other-target-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/req-private-other-target/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/req-private-other-target/CMakeLists.txt
index 910c515..68da617 100644
--- a/Tests/RunCMake/CXXModules/examples/req-private-other-target/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/req-private-other-target/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.26)
+cmake_minimum_required(VERSION 3.26...3.28)
 project(req_private_other_target CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt b/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/same-src-name-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/same-src-name/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/same-src-name/CMakeLists.txt
index 997bbb1..8a47b0c 100644
--- a/Tests/RunCMake/CXXModules/examples/same-src-name/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/same-src-name/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.27)
+cmake_minimum_required(VERSION 3.27...3.28)
 project(cxx_modules_same_src_name CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt b/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt
deleted file mode 100644
index 34f3f85..0000000
--- a/Tests/RunCMake/CXXModules/examples/scan_properties-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:25 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt
index f2f1c38..f5e5da6 100644
--- a/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/scan_properties/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(scan_properties CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
@@ -11,9 +11,9 @@
 # To detect that not-to-be scanned sources are not scanned, add a `-D` to the
 # scan flags so that the files can detect whether scanning happened and error
 # if not.
-string(APPEND CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG
+string(APPEND CMAKE_CXX_MODULE_MAP_FLAG
   " -DCMAKE_SCANNED_THIS_SOURCE")
-string(APPEND CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
+string(APPEND CMAKE_CXX_SCANDEP_SOURCE
   " -DCMAKE_SCANNED_THIS_SOURCE")
 
 set_property(SOURCE always_scan.cxx
diff --git a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt b/Tests/RunCMake/CXXModules/examples/simple-stderr.txt
deleted file mode 100644
index 78bdf2b..0000000
--- a/Tests/RunCMake/CXXModules/examples/simple-stderr.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-CMake Warning \(dev\) at CMakeLists.txt:7 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-This warning is for project developers.  Use -Wno-dev to suppress it.
diff --git a/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt
index 442e425..43f425a 100644
--- a/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt
+++ b/Tests/RunCMake/CXXModules/examples/simple/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.24...3.28)
 project(cxx_modules_simple CXX)
 
 include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
diff --git a/Tests/RunCMake/CXXModules/examples/try-compile/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/try-compile/CMakeLists.txt
new file mode 100644
index 0000000..66c32ba
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/try-compile/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_try_compile CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
+try_compile(can_use_modules
+  SOURCES_TYPE CXX_MODULE
+  SOURCES
+    "${CMAKE_CURRENT_LIST_DIR}/importable.cxx"
+  SOURCES_TYPE NORMAL
+  SOURCE_FROM_FILE
+    use_importable.cxx "${CMAKE_CURRENT_LIST_DIR}/use_importable.cxx"
+  CXX_STANDARD 20)
+
+if (NOT can_use_modules)
+  message(FATAL_ERROR
+    "`try_compile` could not compile sources using modules.")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/try-compile/importable.cxx b/Tests/RunCMake/CXXModules/examples/try-compile/importable.cxx
new file mode 100644
index 0000000..607680a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/try-compile/importable.cxx
@@ -0,0 +1,6 @@
+export module importable;
+
+export int from_import()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/try-compile/use_importable.cxx b/Tests/RunCMake/CXXModules/examples/try-compile/use_importable.cxx
new file mode 100644
index 0000000..8d6bab2
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/try-compile/use_importable.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int foo()
+{
+  return from_import();
+}
diff --git a/Tests/RunCMake/CXXModules/examples/try-run/CMakeLists.txt b/Tests/RunCMake/CXXModules/examples/try-run/CMakeLists.txt
new file mode 100644
index 0000000..9480d74
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/try-run/CMakeLists.txt
@@ -0,0 +1,23 @@
+cmake_minimum_required(VERSION 3.24...3.28)
+project(cxx_modules_try_run CXX)
+
+include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
+
+try_run(can_run_modules_result can_compile_modules
+  SOURCES_TYPE CXX_MODULE
+  SOURCES
+    "${CMAKE_CURRENT_LIST_DIR}/importable.cxx"
+  SOURCES_TYPE NORMAL
+  SOURCE_FROM_FILE
+    main.cxx "${CMAKE_CURRENT_LIST_DIR}/main.cxx"
+  CXX_STANDARD 20)
+
+if (NOT can_compile_modules)
+  message(FATAL_ERROR
+    "`try_run` could not compile sources using modules.")
+endif ()
+
+if (can_run_modules_result)
+  message(FATAL_ERROR
+    "`try_run` could not run sources using modules.")
+endif ()
diff --git a/Tests/RunCMake/CXXModules/examples/try-run/importable.cxx b/Tests/RunCMake/CXXModules/examples/try-run/importable.cxx
new file mode 100644
index 0000000..607680a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/try-run/importable.cxx
@@ -0,0 +1,6 @@
+export module importable;
+
+export int from_import()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CXXModules/examples/try-run/main.cxx b/Tests/RunCMake/CXXModules/examples/try-run/main.cxx
new file mode 100644
index 0000000..5c1bb42
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/examples/try-run/main.cxx
@@ -0,0 +1,6 @@
+import importable;
+
+int main(int argc, char* argv[])
+{
+  return from_import() == 1;
+}
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json
index 65f0759..45b0396 100644
--- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json
+++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-private.json
@@ -6,9 +6,12 @@
     "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir/install-cxx-module-bmi-<CONFIG_FORCE>.cmake"
   },
   "compiler-id": "<IGNORE>",
+  "compiler-frontend-variant": "<IGNORE>",
+  "compiler-simulate-id": "<IGNORE>",
   "config": "<CONFIG>",
   "cxx-modules": {
-    "CMakeFiles/ninja-bmi-install-private.dir/sources/module-internal-part.cxx.o": {
+    "CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "internal_partitions",
       "relative-directory": "sources",
@@ -16,7 +19,8 @@
       "type": "CXX_MODULES",
       "visibility": "PRIVATE"
     },
-    "CMakeFiles/ninja-bmi-install-private.dir/sources/module-part.cxx.o": {
+    "CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "modules",
       "relative-directory": "",
@@ -24,7 +28,8 @@
       "type": "CXX_MODULES",
       "visibility": "PRIVATE"
     },
-    "CMakeFiles/ninja-bmi-install-private.dir/sources/module.cxx.o": {
+    "CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "modules",
       "relative-directory": "",
@@ -41,5 +46,5 @@
   "include-dirs": [],
   "language": "CXX",
   "linked-target-dirs": [],
-  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir"
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-private.dir<CONFIG_DIR>"
 }
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json
index 9c8a895..30b55e3 100644
--- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json
+++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoBMIInstall-public.json
@@ -3,12 +3,15 @@
     "destination": "lib/cxx/modules/<CONFIG>",
     "message-level": "",
     "permissions": "",
-    "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir/install-cxx-module-bmi-noconfig.cmake"
+    "script-location": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir/install-cxx-module-bmi-<CONFIG_FORCE>.cmake"
   },
   "compiler-id": "<IGNORE>",
+  "compiler-frontend-variant": "<IGNORE>",
+  "compiler-simulate-id": "<IGNORE>",
   "config": "<CONFIG>",
   "cxx-modules": {
-    "CMakeFiles/ninja-bmi-install-public.dir/sources/module-internal-part.cxx.o": {
+    "CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx/internals",
       "name": "internal_partitions",
       "relative-directory": "sources",
@@ -16,7 +19,8 @@
       "type": "CXX_MODULES",
       "visibility": "PUBLIC"
     },
-    "CMakeFiles/ninja-bmi-install-public.dir/sources/module-part.cxx.o": {
+    "CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx",
       "name": "modules",
       "relative-directory": "",
@@ -24,7 +28,8 @@
       "type": "CXX_MODULES",
       "visibility": "PUBLIC"
     },
-    "CMakeFiles/ninja-bmi-install-public.dir/sources/module.cxx.o": {
+    "CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx",
       "name": "modules",
       "relative-directory": "",
@@ -41,5 +46,5 @@
   "include-dirs": [],
   "language": "CXX",
   "linked-target-dirs": [],
-  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir"
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-bmi-install-public.dir<CONFIG_DIR>"
 }
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json
index 0545981..f06a846 100644
--- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json
+++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-private.json
@@ -1,9 +1,12 @@
 {
   "bmi-installation": null,
   "compiler-id": "<IGNORE>",
+  "compiler-frontend-variant": "<IGNORE>",
+  "compiler-simulate-id": "<IGNORE>",
   "config": "<CONFIG>",
   "cxx-modules": {
-    "CMakeFiles/ninja-exports-private.dir/sources/module-internal-part.cxx.o": {
+    "CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "internal_partitions",
       "relative-directory": "sources",
@@ -11,7 +14,8 @@
       "type": "CXX_MODULES",
       "visibility": "PRIVATE"
     },
-    "CMakeFiles/ninja-exports-private.dir/sources/module-part.cxx.o": {
+    "CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "modules",
       "relative-directory": "",
@@ -19,7 +23,8 @@
       "type": "CXX_MODULES",
       "visibility": "PRIVATE"
     },
-    "CMakeFiles/ninja-exports-private.dir/sources/module.cxx.o": {
+    "CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "modules",
       "relative-directory": "",
@@ -69,5 +74,5 @@
   "include-dirs": [],
   "language": "CXX",
   "linked-target-dirs": [],
-  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-private.dir"
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-private.dir<CONFIG_DIR>"
 }
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json
index adc3ae3..938481c 100644
--- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json
+++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoExport-public.json
@@ -1,9 +1,12 @@
 {
   "bmi-installation": null,
   "compiler-id": "<IGNORE>",
+  "compiler-frontend-variant": "<IGNORE>",
+  "compiler-simulate-id": "<IGNORE>",
   "config": "<CONFIG>",
   "cxx-modules": {
-    "CMakeFiles/ninja-exports-public.dir/sources/module-internal-part.cxx.o": {
+    "CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx/internals",
       "name": "internal_partitions",
       "relative-directory": "sources",
@@ -11,7 +14,8 @@
       "type": "CXX_MODULES",
       "visibility": "PUBLIC"
     },
-    "CMakeFiles/ninja-exports-public.dir/sources/module-part.cxx.o": {
+    "CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx",
       "name": "modules",
       "relative-directory": "",
@@ -19,7 +23,8 @@
       "type": "CXX_MODULES",
       "visibility": "PUBLIC"
     },
-    "CMakeFiles/ninja-exports-public.dir/sources/module.cxx.o": {
+    "CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx",
       "name": "modules",
       "relative-directory": "",
@@ -69,5 +74,5 @@
   "include-dirs": [],
   "language": "CXX",
   "linked-target-dirs": [],
-  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-public.dir"
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-exports-public.dir<CONFIG_DIR>"
 }
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json
index 9ba6568..3a66a94 100644
--- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json
+++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-private.json
@@ -1,9 +1,12 @@
 {
   "bmi-installation": null,
   "compiler-id": "<IGNORE>",
+  "compiler-frontend-variant": "<IGNORE>",
+  "compiler-simulate-id": "<IGNORE>",
   "config": "<CONFIG>",
   "cxx-modules": {
-    "CMakeFiles/ninja-file-sets-private.dir/sources/module-internal-part.cxx.o": {
+    "CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "internal_partitions",
       "relative-directory": "sources",
@@ -11,7 +14,8 @@
       "type": "CXX_MODULES",
       "visibility": "PRIVATE"
     },
-    "CMakeFiles/ninja-file-sets-private.dir/sources/module-part.cxx.o": {
+    "CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "modules",
       "relative-directory": "",
@@ -19,7 +23,8 @@
       "type": "CXX_MODULES",
       "visibility": "PRIVATE"
     },
-    "CMakeFiles/ninja-file-sets-private.dir/sources/module.cxx.o": {
+    "CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": null,
       "name": "modules",
       "relative-directory": "",
@@ -36,5 +41,5 @@
   "include-dirs": [],
   "language": "CXX",
   "linked-target-dirs": [],
-  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-private.dir"
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-private.dir<CONFIG_DIR>"
 }
diff --git a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json
index 46e2cbf..ac06c0f 100644
--- a/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json
+++ b/Tests/RunCMake/CXXModules/expect/NinjaDependInfoFileSet-public.json
@@ -1,9 +1,12 @@
 {
   "bmi-installation": null,
   "compiler-id": "<IGNORE>",
+  "compiler-frontend-variant": "<IGNORE>",
+  "compiler-simulate-id": "<IGNORE>",
   "config": "<CONFIG>",
   "cxx-modules": {
-    "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-internal-part.cxx.o": {
+    "CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>/sources/module-internal-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx/internals",
       "name": "internal_partitions",
       "relative-directory": "sources",
@@ -11,7 +14,8 @@
       "type": "CXX_MODULES",
       "visibility": "PUBLIC"
     },
-    "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module-part.cxx.o": {
+    "CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>/sources/module-part.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx",
       "name": "modules",
       "relative-directory": "",
@@ -19,7 +23,8 @@
       "type": "CXX_MODULES",
       "visibility": "PUBLIC"
     },
-    "CMakeFiles/ninja-file-sets-public.dir/<CONFIG_DIR>sources/module.cxx.o": {
+    "CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>/sources/module.cxx<OBJEXT>": {
+      "bmi-only": false,
       "destination": "lib/cxx",
       "name": "modules",
       "relative-directory": "",
@@ -36,5 +41,5 @@
   "include-dirs": [],
   "language": "CXX",
   "linked-target-dirs": [],
-  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-public.dir"
+  "module-dir": "<BINARY_DIR>/CMakeFiles/ninja-file-sets-public.dir<CONFIG_DIR>"
 }
diff --git a/Tests/RunCMake/CXXModules/main-no-use.cxx b/Tests/RunCMake/CXXModules/main-no-use.cxx
new file mode 100644
index 0000000..f8b643a
--- /dev/null
+++ b/Tests/RunCMake/CXXModules/main-no-use.cxx
@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/CommandLine/C-no-file-stderr.txt b/Tests/RunCMake/CommandLine/C-no-file-stderr.txt
index b65a349..5d3d4b3 100644
--- a/Tests/RunCMake/CommandLine/C-no-file-stderr.txt
+++ b/Tests/RunCMake/CommandLine/C-no-file-stderr.txt
@@ -1,3 +1,3 @@
-^CMake Error: Error processing file: .*/Tests/RunCMake/CommandLine/C-no-file-build/nosuchcachefile.txt
+^CMake Error: Not a file: .*/Tests/RunCMake/CommandLine/C-no-file-build/nosuchcachefile.txt
 CMake Error: The source directory ".*/Tests/RunCMake/CommandLine/C-no-file-build" does not appear to contain CMakeLists.txt.
 Specify --help for usage, or press the help button on the CMake GUI.$
diff --git a/Tests/RunCMake/CommandLine/Cno-file-stderr.txt b/Tests/RunCMake/CommandLine/Cno-file-stderr.txt
index 416686c..4024499 100644
--- a/Tests/RunCMake/CommandLine/Cno-file-stderr.txt
+++ b/Tests/RunCMake/CommandLine/Cno-file-stderr.txt
@@ -1,3 +1,3 @@
-^CMake Error: Error processing file: .*/Tests/RunCMake/CommandLine/Cno-file-build/nosuchcachefile.txt
+^CMake Error: Not a file: .*/Tests/RunCMake/CommandLine/Cno-file-build/nosuchcachefile.txt
 CMake Error: The source directory ".*/Tests/RunCMake/CommandLine/Cno-file-build" does not appear to contain CMakeLists.txt.
 Specify --help for usage, or press the help button on the CMake GUI.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON-stderr.txt
deleted file mode 100644
index 9080942..0000000
--- a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON-stderr.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-^CMake Warning:
-  The "Visual Studio 11 2012" generator is deprecated and will be removed in
-  a future version of CMake.
-
-  Add CMAKE_WARN_VS11=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-OFF.cmake
similarity index 100%
rename from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
rename to Tests/RunCMake/CommandLine/DeprecateVS12-WARN-OFF.cmake
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt b/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt
new file mode 100644
index 0000000..b69408e
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON-stderr.txt
@@ -0,0 +1,5 @@
+^CMake Warning:
+  The "Visual Studio 12 2013" generator is deprecated and will be removed in
+  a future version of CMake.
+
+  Add CMAKE_WARN_VS12=OFF to the cache to disable this warning.$
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake b/Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON.cmake
similarity index 100%
rename from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake
rename to Tests/RunCMake/CommandLine/DeprecateVS12-WARN-ON.cmake
diff --git a/Tests/RunCMake/CommandLine/P_directory-stderr.txt b/Tests/RunCMake/CommandLine/P_directory-stderr.txt
index b8319a1..ec33aed 100644
--- a/Tests/RunCMake/CommandLine/P_directory-stderr.txt
+++ b/Tests/RunCMake/CommandLine/P_directory-stderr.txt
@@ -1 +1 @@
-^CMake Error: Error processing file: .*/Tests/RunCMake/CommandLine$
+^CMake Error: Not a file: .*/Tests/RunCMake/CommandLine$
diff --git a/Tests/RunCMake/CommandLine/P_no-file-stderr.txt b/Tests/RunCMake/CommandLine/P_no-file-stderr.txt
index 2e12399..a3b4e8e 100644
--- a/Tests/RunCMake/CommandLine/P_no-file-stderr.txt
+++ b/Tests/RunCMake/CommandLine/P_no-file-stderr.txt
@@ -1 +1,2 @@
-^CMake Error: Error processing file: nosuchscriptfile.cmake$
+^CMake Error: Not a file: nosuchscriptfile.cmake
+CMake Error: Error processing file: nosuchscriptfile.cmake$
diff --git a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
index 45b4c0e..52be1bb 100644
--- a/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CommandLine/RunCMakeTest.cmake
@@ -1119,9 +1119,11 @@
   run_cmake_with_options(DeprecateVS9-WARN-OFF -DCMAKE_WARN_VS9=OFF)
 endif()
 
-if(RunCMake_GENERATOR MATCHES "^Visual Studio 11 2012")
-  run_cmake_with_options(DeprecateVS11-WARN-ON -DCMAKE_WARN_VS11=ON)
-  unset(ENV{CMAKE_WARN_VS11})
-  run_cmake(DeprecateVS11-WARN-ON)
-  run_cmake_with_options(DeprecateVS11-WARN-OFF -DCMAKE_WARN_VS11=OFF)
+if(RunCMake_GENERATOR MATCHES "^Visual Studio 12 2013")
+  run_cmake_with_options(DeprecateVS12-WARN-ON -DCMAKE_WARN_VS12=ON)
+  unset(ENV{CMAKE_WARN_VS12})
+  run_cmake(DeprecateVS12-WARN-ON)
+  run_cmake_with_options(DeprecateVS12-WARN-OFF -DCMAKE_WARN_VS12=OFF)
 endif()
+
+run_cmake_with_options(help-arbitrary "--help" "CMAKE_CXX_IGNORE_EXTENSIONS")
diff --git a/Tests/RunCMake/CommandLine/help-arbitrary-stdout.txt b/Tests/RunCMake/CommandLine/help-arbitrary-stdout.txt
new file mode 100644
index 0000000..3d07e91
--- /dev/null
+++ b/Tests/RunCMake/CommandLine/help-arbitrary-stdout.txt
@@ -0,0 +1,7 @@
+CMAKE_<LANG>_IGNORE_EXTENSIONS
+------------------------------
+
+File extensions that should be ignored by the build.
+
+This is a list of file extensions that may be part of a project for a
+given language but are not compiled.
diff --git a/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt b/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt
index 320aecc..87999d5 100644
--- a/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt
+++ b/Tests/RunCMake/CommandLine/install-bad-dir-stderr.txt
@@ -1 +1 @@
-^CMake Error: Error processing file:
+^CMake Error: Not a file:
diff --git a/Tests/RunCMake/CrosscompilingEmulator/EnvCrossCompilingEmulator-stdout.txt b/Tests/RunCMake/CrosscompilingEmulator/EnvCrossCompilingEmulator-stdout.txt
new file mode 100644
index 0000000..9a7d746
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/EnvCrossCompilingEmulator-stdout.txt
@@ -0,0 +1,2 @@
+-- env_emulator='pseudo_emulator(\.exe)?'
+-- emulator='pseudo_emulator(\.exe)?'
diff --git a/Tests/RunCMake/CrosscompilingEmulator/EnvCrossCompilingEmulator.cmake b/Tests/RunCMake/CrosscompilingEmulator/EnvCrossCompilingEmulator.cmake
new file mode 100644
index 0000000..55fc483
--- /dev/null
+++ b/Tests/RunCMake/CrosscompilingEmulator/EnvCrossCompilingEmulator.cmake
@@ -0,0 +1,6 @@
+message(STATUS "ENV{CMAKE_CROSS_COMPILING_EMULATOR}='$ENV{CMAKE_CROSSCOMPILING_EMULATOR}'")
+message(STATUS "CMAKE_CROSSCOMPLING_EMULATOR='${CMAKE_CROSSCOMPILING_EMULATOR}'")
+get_filename_component(env_emulator "$ENV{CMAKE_CROSSCOMPILING_EMULATOR}" NAME)
+message(STATUS "env_emulator='${env_emulator}'")
+get_filename_component(emulator "${CMAKE_CROSSCOMPILING_EMULATOR}" NAME)
+message(STATUS "emulator='${emulator}'")
diff --git a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake
index 97b7b5a..1ffd91c 100644
--- a/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CrosscompilingEmulator/RunCMakeTest.cmake
@@ -26,3 +26,11 @@
 "-DCMAKE_CROSSCOMPILING_EMULATOR=${PSEUDO_EMULATOR_CUSTOM_COMMAND_ARG}\;custom_argument")
 CustomCommandGenerator_run_and_build(AddCustomCommandWithArg)
 CustomCommandGenerator_run_and_build(AddCustomTargetWithArg)
+unset(RunCMake_TEST_OPTIONS)
+
+function(run_EnvCrossCompilingEmulator)
+  set(ENV{CMAKE_CROSSCOMPILING_EMULATOR} "${PSEUDO_EMULATOR}")
+  run_cmake(EnvCrossCompilingEmulator)
+  unset(ENV{CMAKE_CROSSCOMPILING_EMULATOR})
+endfunction()
+run_EnvCrossCompilingEmulator()
diff --git a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake
index 891e138..f7959dc 100644
--- a/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake
+++ b/Tests/RunCMake/DependencyGraph/RunCMakeTest.cmake
@@ -1,7 +1,11 @@
 include(RunCMake)
 
 function(check_files dir)
-  set(expected ${ARGN})
+  set(expected)
+  foreach(i IN LISTS ARGN)
+    get_filename_component(real_path ${i} REALPATH)
+    list(APPEND expected ${real_path})
+  endforeach()
   list(FILTER expected EXCLUDE REGEX "^$")
   list(REMOVE_DUPLICATES expected)
   list(SORT expected)
@@ -59,3 +63,5 @@
 if(CMAKE_Fortran_COMPILER)
   run_optimize_test(OptimizeFortran FortranTop)
 endif()
+
+run_cmake_build(RuntimeTargets mylib SharedTop)
diff --git a/Tests/RunCMake/DependencyGraph/RuntimeTargets.cmake b/Tests/RunCMake/DependencyGraph/RuntimeTargets.cmake
new file mode 100644
index 0000000..21531cd
--- /dev/null
+++ b/Tests/RunCMake/DependencyGraph/RuntimeTargets.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+set(CMAKE_OPTIMIZE_DEPENDENCIES TRUE)
+add_library(mylib STATIC mylib.c)
+add_library(neverbuild SHARED neverbuild.c)
+
+# Building mylib should not require building neverbuild
+target_link_libraries(mylib PRIVATE neverbuild)
+set_target_properties(neverbuild PROPERTIES EXCLUDE_FROM_ALL YES)
+
+# Building SharedTop should require SharedBottom to be built
+add_library(SharedTop SHARED top.c)
+add_library(StaticMiddle STATIC middle.c)
+add_library(SharedBottom SHARED bottom.c)
+target_link_libraries(SharedTop PRIVATE StaticMiddle)
+target_link_libraries(StaticMiddle PRIVATE SharedBottom)
+set_target_properties(StaticMiddle SharedBottom PROPERTIES EXCLUDE_FROM_ALL YES)
+set_target_properties(StaticMiddle PROPERTIES POSITION_INDEPENDENT_CODE YES)
diff --git a/Tests/RunCMake/DependencyGraph/bottom.c b/Tests/RunCMake/DependencyGraph/bottom.c
new file mode 100644
index 0000000..c8ea481
--- /dev/null
+++ b/Tests/RunCMake/DependencyGraph/bottom.c
@@ -0,0 +1,7 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  int bottom(void)
+{
+  return 23;
+}
diff --git a/Tests/RunCMake/DependencyGraph/middle.c b/Tests/RunCMake/DependencyGraph/middle.c
new file mode 100644
index 0000000..3b1b84c
--- /dev/null
+++ b/Tests/RunCMake/DependencyGraph/middle.c
@@ -0,0 +1,9 @@
+#ifdef _WIN32
+__declspec(dllimport)
+#endif
+  int bottom(void);
+
+int middle(void)
+{
+  return bottom() + 19;
+}
diff --git a/Tests/RunCMake/DependencyGraph/neverbuild.c b/Tests/RunCMake/DependencyGraph/neverbuild.c
new file mode 100644
index 0000000..e490510
--- /dev/null
+++ b/Tests/RunCMake/DependencyGraph/neverbuild.c
@@ -0,0 +1 @@
+#error I should not be built
diff --git a/Tests/RunCMake/DependencyGraph/top.c b/Tests/RunCMake/DependencyGraph/top.c
new file mode 100644
index 0000000..eceb0a5
--- /dev/null
+++ b/Tests/RunCMake/DependencyGraph/top.c
@@ -0,0 +1,9 @@
+int middle(void);
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  int top(void)
+{
+  return middle() + 2;
+}
diff --git a/Tests/RunCMake/ExternalProject/DetectJobServer.cmake b/Tests/RunCMake/ExternalProject/DetectJobServer.cmake
new file mode 100644
index 0000000..c6e1412
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/DetectJobServer.cmake
@@ -0,0 +1,16 @@
+include(ExternalProject)
+ExternalProject_Add(Foo
+  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Foo
+  BUILD_COMMAND ${DETECT_JOBSERVER} "ep.txt"
+  BUILD_JOB_SERVER_AWARE 1
+  INSTALL_COMMAND ""
+)
+
+# Add a second step to test JOB_SERVER_AWARE
+ExternalProject_Add_Step(Foo
+  second_step
+  COMMAND ${DETECT_JOBSERVER} "ep_second_step.txt"
+  DEPENDEES build
+  ALWAYS 1
+  JOB_SERVER_AWARE 1
+)
diff --git a/Tests/RunCMake/ExternalProject/Foo/CMakeLists.txt b/Tests/RunCMake/ExternalProject/Foo/CMakeLists.txt
new file mode 100644
index 0000000..b38b173
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/Foo/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.27)
+project(Foo NONE)
+
+add_custom_target(drive ALL COMMAND ${CMAKE_COMMAND} -E true)
diff --git a/Tests/RunCMake/ExternalProject/GNUMakeJobServerAware-check.cmake b/Tests/RunCMake/ExternalProject/GNUMakeJobServerAware-check.cmake
new file mode 100644
index 0000000..55a9f0d
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/GNUMakeJobServerAware-check.cmake
@@ -0,0 +1,16 @@
+set(BUILD_DIR "${RunCMake_BINARY_DIR}/GNUMakeJobServerAware-build")
+
+function(check target regex)
+  file(STRINGS ${BUILD_DIR}/${target} lines
+    REGEX ${regex}
+  )
+
+  list(LENGTH lines len)
+  if(len EQUAL 0)
+    message(FATAL_ERROR "Could not find matching lines '${regex}' in ${BUILD_DIR}/${target}")
+  endif()
+endfunction()
+
+check("/CMakeFiles/Foo.dir/build.make" [[\+cd (/d )?"?.*"? && "?.*"? --build "?.*"?]])
+check("/CMakeFiles/Foo.dir/build.make" [[\+cd (/d )?"?.*"? && "?.*"? -E touch "?.*"?]])
+check("/CMakeFiles/Foo.dir/build.make" [[\+"?.*"? -E true]])
diff --git a/Tests/RunCMake/ExternalProject/GNUMakeJobServerAware.cmake b/Tests/RunCMake/ExternalProject/GNUMakeJobServerAware.cmake
new file mode 100644
index 0000000..3f688ca
--- /dev/null
+++ b/Tests/RunCMake/ExternalProject/GNUMakeJobServerAware.cmake
@@ -0,0 +1,16 @@
+include(ExternalProject)
+ExternalProject_Add(Foo
+  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Foo
+  BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR>
+  BUILD_JOB_SERVER_AWARE 1
+  INSTALL_COMMAND ""
+)
+
+# Add a second step to test JOB_SERVER_AWARE
+ExternalProject_Add_Step(Foo
+  second_step
+  COMMAND ${CMAKE_COMMAND} -E true
+  DEPENDEES build
+  ALWAYS 1
+  JOB_SERVER_AWARE 1
+)
diff --git a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
index 4afdef8..ffaa46c 100644
--- a/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ExternalProject/RunCMakeTest.cmake
@@ -144,6 +144,24 @@
   run_cmake_command(${testName}-build ${CMAKE_COMMAND} --build .)
 endfunction()
 
+if(RunCMake_GENERATOR MATCHES "(MSYS|MinGW|Unix) Makefiles")
+  __ep_test_with_build(GNUMakeJobServerAware)
+endif()
+
+function(__ep_test_jobserver)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DetectJobServer-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  run_cmake_with_options(DetectJobServer -DDETECT_JOBSERVER=${DETECT_JOBSERVER})
+  run_cmake_command(DetectJobServer-clean ${CMAKE_COMMAND} --build . --target clean)
+  run_cmake_command(DetectJobServer-build ${CMAKE_COMMAND} --build . -j4)
+endfunction()
+
+if(RunCMake_GENERATOR MATCHES "(MinGW|Unix) Makefiles")
+  __ep_test_jobserver()
+endif()
+
 __ep_test_with_build(MultiCommand)
 
 set(RunCMake_TEST_OUTPUT_MERGE 1)
diff --git a/Tests/RunCMake/FetchContent/ExcludeFromAll.cmake b/Tests/RunCMake/FetchContent/ExcludeFromAll.cmake
new file mode 100644
index 0000000..376f2eb
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/ExcludeFromAll.cmake
@@ -0,0 +1,11 @@
+enable_language(C)
+
+include(FetchContent)
+
+FetchContent_Declare(
+  ExcludeFromAll
+  SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/ExcludeFromAll
+  EXCLUDE_FROM_ALL
+)
+
+FetchContent_MakeAvailable(ExcludeFromAll)
diff --git a/Tests/RunCMake/FetchContent/ExcludeFromAll/CMakeLists.txt b/Tests/RunCMake/FetchContent/ExcludeFromAll/CMakeLists.txt
new file mode 100644
index 0000000..08ffb33
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/ExcludeFromAll/CMakeLists.txt
@@ -0,0 +1 @@
+add_library(broken STATIC error.c)
diff --git a/Tests/RunCMake/FetchContent/ExcludeFromAll/error.c b/Tests/RunCMake/FetchContent/ExcludeFromAll/error.c
new file mode 100644
index 0000000..f10e687
--- /dev/null
+++ b/Tests/RunCMake/FetchContent/ExcludeFromAll/error.c
@@ -0,0 +1 @@
+#error "This should not be compiled"
diff --git a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
index 3781089..d0790eb 100644
--- a/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FetchContent/RunCMakeTest.cmake
@@ -64,3 +64,15 @@
     -DCMAKE_MAKE_PROGRAM=${RunCMake_MAKE_PROGRAM}
     -P ${CMAKE_CURRENT_LIST_DIR}/ScriptMode.cmake
 )
+
+function(run_FetchContent_ExcludeFromAll)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/ExcludeFromAll-build)
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(ExcludeFromAll)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(ExcludeFromAll-build ${CMAKE_COMMAND} --build .)
+endfunction()
+run_FetchContent_ExcludeFromAll()
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
index 8911e18..807d92b 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py
@@ -846,7 +846,7 @@
                 e["compileGroups"] = apple_exe_framework["compileGroups"]
                 e["link"] = apple_exe_framework["link"]
 
-    if cxx_compiler_id in ['Clang', 'AppleClang', 'LCC', 'GNU', 'Intel', 'IntelLLVM', 'MSVC', 'Embarcadero', 'IBMClang'] and g["name"] != "Xcode":
+    if cxx_compiler_id in ['Clang', 'AppleClang', 'LCC', 'GNU', 'Intel', 'IntelLLVM', 'MSVC', 'Embarcadero', 'CrayClang', 'IBMClang'] and g["name"] != "Xcode":
         for e in expected:
             if e["name"] == "cxx_exe":
                 if matches(g["name"], "^(Visual Studio |Ninja Multi-Config)"):
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
index 28f2b99..22dfabd 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json
@@ -53,6 +53,40 @@
         },
         {
             "component": "Unspecified",
+            "type": "cxxModuleBmi",
+            "destination": "lib",
+            "paths": null,
+            "isExcludeFromAll": null,
+            "isForAllComponents": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "cxxModuleBmiTarget": {
+                "id": "^cxx_exe::@a56b12a3f5c0529fb296",
+                "index": "cxx_exe"
+            },
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": 38,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^cxx/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
             "type": "runtimeDependencySet",
             "destination": "lib",
             "paths": null,
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
index c4df2ec..f260037 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/fileset.json
@@ -16,7 +16,7 @@
       "type": "target",
       "destination": "lib",
       "paths": [
-        "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_1\\.(a|lib)?$"
+        "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_1\\.(a|lib|l)?$"
       ],
       "isExcludeFromAll": null,
       "isForAllComponents": null,
@@ -172,7 +172,7 @@
       "type": "target",
       "destination": "lib",
       "paths": [
-        "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_2\\.(a|lib)?$"
+        "^fileset/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_headers_2\\.(a|lib|l)?$"
       ],
       "isExcludeFromAll": null,
       "isForAllComponents": null,
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/object.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/object.json
index 44bc725..48c3a9d 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/object.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/object.json
@@ -49,6 +49,40 @@
         },
         {
             "component": "Unspecified",
+            "type": "cxxModuleBmi",
+            "destination": "bin",
+            "paths": null,
+            "isExcludeFromAll": null,
+            "isForAllComponents": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "cxxModuleBmiTarget": {
+                "id": "^c_object_exe::@5ed5358f70faf8d8af7a$",
+                "index": "c_object_exe"
+            },
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 13,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
+        },
+        {
+            "component": "Unspecified",
             "type": "target",
             "destination": "bin",
             "paths": [
@@ -78,6 +112,40 @@
                     "hasParent": false
                 }
             ]
+        },
+        {
+            "component": "Unspecified",
+            "type": "cxxModuleBmi",
+            "destination": "bin",
+            "paths": null,
+            "isExcludeFromAll": null,
+            "isForAllComponents": null,
+            "isOptional": null,
+            "targetId": null,
+            "targetIndex": null,
+            "cxxModuleBmiTarget": {
+                "id": "^cxx_object_exe::@5ed5358f70faf8d8af7a$",
+                "index": "cxx_object_exe"
+            },
+            "targetIsImportLibrary": null,
+            "targetInstallNamelink": null,
+            "exportName": null,
+            "exportTargets": null,
+            "scriptFile": null,
+            "backtrace": [
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": 13,
+                    "command": "install",
+                    "hasParent": true
+                },
+                {
+                    "file": "^object/CMakeLists\\.txt$",
+                    "line": null,
+                    "command": null,
+                    "hasParent": false
+                }
+            ]
         }
     ]
 }
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json
index a35d5e2..b57ab45 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/top.json
@@ -80,7 +80,7 @@
             "type": "target",
             "destination": "lib",
             "paths": [
-                "^((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(lib|dll\\.a)$"
+                "^((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?c_shared_lib\\.(dll\\.a|lib|l)$"
             ],
             "isExcludeFromAll": null,
             "isForAllComponents": null,
@@ -173,7 +173,7 @@
             "type": "target",
             "destination": "lib",
             "paths": [
-                "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?cxx_shared_lib\\.(lib|dll\\.a)$"
+                "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib|l)$"
             ],
             "isExcludeFromAll": null,
             "isForAllComponents": null,
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json
index 6d320f4..a4c13a8 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/apple_exe_framework.json
@@ -34,7 +34,7 @@
         "lto": null,
         "commandFragments": [
             {
-                "fragment": "-iframework .+/framework(/(Debug|Release|RelWithDebInfo|MinSizeRel))?\"? -iframework /usr/Frameworks$",
+                "fragment": "-F.+/framework(/(Debug|Release|RelWithDebInfo|MinSizeRel))?\"? -F/usr/Frameworks$",
                 "role": "frameworkPath",
                 "backtrace": null
             },
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json
index f6cfa9c..faf0eaa 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_1.json
@@ -219,10 +219,10 @@
     }
   ],
   "folder": null,
-  "nameOnDisk": "^(lib)?c_headers_1\\.(a|lib)$",
+  "nameOnDisk": "^(lib)?c_headers_1\\.(a|lib|l)$",
   "artifacts": [
     {
-      "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_1\\.(a|lib)$",
+      "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_1\\.(a|lib|l)$",
       "_dllExtra": false
     }
   ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json
index 591ba4f..b4f2f9d 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_headers_2.json
@@ -71,10 +71,10 @@
     }
   ],
   "folder": null,
-  "nameOnDisk": "^(lib)?c_headers_2\\.(a|lib)$",
+  "nameOnDisk": "^(lib)?c_headers_2\\.(a|lib|l)$",
   "artifacts": [
     {
-      "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_2\\.(a|lib)$",
+      "path": "^fileset/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_headers_2\\.(a|lib|l)$",
       "_dllExtra": false
     }
   ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json
index dc74fdf..5b58dd1 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_lib.json
@@ -88,10 +88,10 @@
         }
     ],
     "folder": null,
-    "nameOnDisk": "^(lib)?c_lib\\.(a|lib)$",
+    "nameOnDisk": "^(lib)?c_lib\\.(a|lib|l)$",
     "artifacts": [
         {
-            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_lib\\.(a|lib)$",
+            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_lib\\.(a|lib|l)$",
             "_dllExtra": false
         }
     ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json
index 85b5108..8d5faf8 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_shared_lib.json
@@ -100,7 +100,7 @@
             "_dllExtra": false
         },
         {
-            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_shared_lib\\.(dll\\.a|lib)$",
+            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_shared_lib\\.(dll\\.a|lib|l)$",
             "_dllExtra": true
         },
         {
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json
index 6a51295..2220581 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_static_lib.json
@@ -88,10 +88,10 @@
         }
     ],
     "folder": null,
-    "nameOnDisk": "^(lib)?c_static_lib\\.(a|lib)$",
+    "nameOnDisk": "^(lib)?c_static_lib\\.(a|lib|l)$",
     "artifacts": [
         {
-            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_static_lib\\.(a|lib)$",
+            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_static_lib\\.(a|lib|l)$",
             "_dllExtra": false
         }
     ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json
index 362caf9..a5bebcd 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/c_subdir.json
@@ -113,10 +113,10 @@
         }
     ],
     "folder": null,
-    "nameOnDisk": "^(lib)?c_subdir\\.(a|lib)$",
+    "nameOnDisk": "^(lib)?c_subdir\\.(a|lib|l)$",
     "artifacts": [
         {
-            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_subdir\\.(a|lib)$",
+            "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?c_subdir\\.(a|lib|l)$",
             "_dllExtra": false
         }
     ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json
index 725cad9..2f8d6ed 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_lib.json
@@ -64,10 +64,10 @@
         }
     ],
     "folder": null,
-    "nameOnDisk": "^(lib)?cxx_lib\\.(a|lib)$",
+    "nameOnDisk": "^(lib)?cxx_lib\\.(a|lib|l)$",
     "artifacts": [
         {
-            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_lib\\.(a|lib)$",
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_lib\\.(a|lib|l)$",
             "_dllExtra": false
         }
     ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json
index c92e573..2274e45 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_shared_lib.json
@@ -76,7 +76,7 @@
             "_dllExtra": false
         },
         {
-            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib)$",
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_shared_lib\\.(dll\\.a|lib|l)$",
             "_dllExtra": true
         },
         {
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json
index 38790dd..2f322b0 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_static_lib.json
@@ -64,10 +64,10 @@
         }
     ],
     "folder": null,
-    "nameOnDisk": "^(lib)?cxx_static_lib\\.(a|lib)$",
+    "nameOnDisk": "^(lib)?cxx_static_lib\\.(a|lib|l)$",
     "artifacts": [
         {
-            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_static_lib\\.(a|lib)$",
+            "path": "^cxx/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?cxx_static_lib\\.(a|lib|l)$",
             "_dllExtra": false
         }
     ],
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json
index 521e464..5b47814 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/interface_exe.json
@@ -73,6 +73,47 @@
                 {
                     "define": "interface_exe_EXPORTS",
                     "backtrace": null
+                },
+                {
+                    "define": "COMPILED_WITH_INTERFACE_LIB",
+                    "backtrace": [
+                        {
+                            "file": "^include_test\\.cmake$",
+                            "line": 4,
+                            "command": "target_link_libraries",
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^include_test\\.cmake$",
+                            "line": null,
+                            "command": null,
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^codemodel-v2\\.cmake$",
+                            "line": null,
+                            "command": null,
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": 3,
+                            "command": "include",
+                            "hasParent": true
+                        },
+                        {
+                            "file": "^CMakeLists\\.txt$",
+                            "line": null,
+                            "command": null,
+                            "hasParent": false
+                        }
+                    ]
                 }
             ],
             "compileCommandFragments": null
@@ -129,7 +170,7 @@
             "_dllExtra": false
         },
         {
-            "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib)$",
+            "path": "^lib/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?my_interface_exe\\.(dll\\.a|lib|l)$",
             "_dllExtra": true
         },
         {
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json
index 41b5605..fab6f66 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/shared_framework.json
@@ -76,7 +76,7 @@
             "_dllExtra": false
         },
         {
-            "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?shared_framework\\.(dll\\.a|lib)$",
+            "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?shared_framework\\.(dll\\.a|lib|l)$",
             "_dllExtra": true
         },
         {
diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json
index 00dd11e..d6cbded 100644
--- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json
+++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/static_framework.json
@@ -64,10 +64,10 @@
         }
     ],
     "folder": null,
-    "nameOnDisk": "^(lib)?static_framework\\.(a|lib)$",
+    "nameOnDisk": "^(lib)?static_framework\\.(a|lib|l)$",
     "artifacts": [
         {
-            "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?static_framework\\.(a|lib)$",
+            "path": "^framework/((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib)?static_framework\\.(a|lib|l)$",
             "_dllExtra": false
         }
     ],
diff --git a/Tests/RunCMake/FileAPI/include_test.cmake b/Tests/RunCMake/FileAPI/include_test.cmake
index c74d264..c188cb3 100644
--- a/Tests/RunCMake/FileAPI/include_test.cmake
+++ b/Tests/RunCMake/FileAPI/include_test.cmake
@@ -1,7 +1,7 @@
 add_library(interface_lib INTERFACE)
 target_compile_definitions(interface_lib INTERFACE COMPILED_WITH_INTERFACE_LIB)
 add_executable(interface_exe empty.c)
-target_link_libraries(interface_exe PRIVATE inteface_lib)
+target_link_libraries(interface_exe PRIVATE interface_lib)
 set_property(TARGET interface_exe PROPERTY ENABLE_EXPORTS ON)
 set_property(TARGET interface_exe PROPERTY RUNTIME_OUTPUT_DIRECTORY bin)
 set_property(TARGET interface_exe PROPERTY ARCHIVE_OUTPUT_DIRECTORY lib)
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES.cmake
new file mode 100644
index 0000000..98f3cd6
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES.cmake
@@ -0,0 +1,19 @@
+# Prepare environment and variables
+set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH TRUE)
+if(WIN32)
+    set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}\\pc-bletch")
+else()
+    set(ENV{CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/pc-bletch")
+endif()
+
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(BLETCH QUIET bletch)
+
+if (NOT BLETCH_FOUND)
+  message(FATAL_ERROR "Failed to find embedded package bletch via CMAKE_PREFIX_PATH")
+endif ()
+
+pkg_get_variable(bletchvar bletch exec_prefix DEFINE_VARIABLES prefix=customprefix)
+if (NOT bletchvar STREQUAL "customprefix")
+  message(FATAL_ERROR "Failed to fetch variable exec_prefix from embedded package bletch with prefix overridden to customprefix")
+endif ()
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt
deleted file mode 100644
index 539e5ef..0000000
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-stdout.txt
+++ /dev/null
@@ -1,3 +0,0 @@
--- ZOT_LIBRARIES='zot'
--- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib/prefix-zot-suffix'
--- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH-build/root/lib;-lzot'
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake
deleted file mode 100644
index 1278c49..0000000
--- a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_LIBRARY_PATH.cmake
+++ /dev/null
@@ -1,34 +0,0 @@
-find_package(PkgConfig REQUIRED)
-
-set(ROOT "${CMAKE_CURRENT_BINARY_DIR}/root")
-string(REPLACE " " "\\ " ESCAPED_ROOT "${ROOT}")
-set(LIB_DIR "${ROOT}/lib")
-set(PKGCONFIG_DIR "${LIB_DIR}/pkgconfig")
-
-file(WRITE "${PKGCONFIG_DIR}/zot.pc" "
-prefix=${ESCAPED_ROOT}
-libdir=\${prefix}/lib
-
-Name: Zot
-Description: Dummy package to test LIBRARY_DIR support
-Version: 1.0
-Libs: -L\${libdir} -lzot
-")
-
-# Create a "library" file to find in libdir.
-set(CMAKE_FIND_LIBRARY_PREFIXES "prefix-")
-set(CMAKE_FIND_LIBRARY_SUFFIXES "-suffix")
-file(WRITE "${LIB_DIR}/prefix-zot-suffix")
-
-# 'pkg-config --libs' drops -L flags in PKG_CONFIG_SYSTEM_LIBRARY_PATH by default.
-set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "${LIB_DIR}")
-
-# 'pkgconf --libs' also drops -L flags in LIBRARY_PATH by default.
-set(ENV{LIBRARY_PATH}                   "${LIB_DIR}")
-
-set(ENV{PKG_CONFIG_PATH}                "${PKGCONFIG_DIR}")
-pkg_check_modules(ZOT REQUIRED zot)
-
-message(STATUS "ZOT_LIBRARIES='${ZOT_LIBRARIES}'")
-message(STATUS "ZOT_LINK_LIBRARIES='${ZOT_LINK_LIBRARIES}'")
-message(STATUS "ZOT_LDFLAGS='${ZOT_LDFLAGS}'")
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt
new file mode 100644
index 0000000..012458d
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-stdout.txt
@@ -0,0 +1,4 @@
+-- ZOT_LIBRARIES='zot'
+-- ZOT_LINK_LIBRARIES='[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/lib/prefix-zot-suffix'
+-- ZOT_LDFLAGS='-L[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/lib;-lzot'
+-- ZOT_CFLAGS='-I[^']*/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH-build/root/include'
diff --git a/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake
new file mode 100644
index 0000000..e58cefb
--- /dev/null
+++ b/Tests/RunCMake/FindPkgConfig/FindPkgConfig_SYSTEM_PATH.cmake
@@ -0,0 +1,42 @@
+find_package(PkgConfig REQUIRED)
+
+set(ROOT "${CMAKE_CURRENT_BINARY_DIR}/root")
+string(REPLACE " " "\\ " ESCAPED_ROOT "${ROOT}")
+set(LIB_DIR "${ROOT}/lib")
+set(INCLUDE_DIR "${ROOT}/include")
+set(PKGCONFIG_DIR "${LIB_DIR}/pkgconfig")
+
+file(WRITE "${PKGCONFIG_DIR}/zot.pc" "
+prefix=${ESCAPED_ROOT}
+libdir=\${prefix}/lib
+includedir=\${prefix}/include
+
+Name: Zot
+Description: Dummy package to test LIBRARY_DIR support
+Version: 1.0
+Libs: -L\${libdir} -lzot
+Cflags: -I\${includedir}
+")
+
+# Create a "library" file to find in libdir.
+set(CMAKE_FIND_LIBRARY_PREFIXES "prefix-")
+set(CMAKE_FIND_LIBRARY_SUFFIXES "-suffix")
+file(WRITE "${LIB_DIR}/prefix-zot-suffix")
+
+# 'pkg-config --libs' drops -L flags in PKG_CONFIG_SYSTEM_LIBRARY_PATH by default.
+set(ENV{PKG_CONFIG_SYSTEM_LIBRARY_PATH} "${LIB_DIR}")
+# 'pkg-config --cflags' drops -I flags in PKG_CONFIG_SYSTEM_INCLUDE_PATH by default.
+set(ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH} "${INCLUDE_DIR}")
+
+# 'pkgconf --libs' also drops -L flags in LIBRARY_PATH by default.
+set(ENV{LIBRARY_PATH}                   "${LIB_DIR}")
+# 'pkgconf --cflags' also drops -I flags in CPATH by default.
+set(ENV{CPATH}                          "${INCLUDE_DIR}")
+
+set(ENV{PKG_CONFIG_PATH}                "${PKGCONFIG_DIR}")
+pkg_check_modules(ZOT REQUIRED zot)
+
+message(STATUS "ZOT_LIBRARIES='${ZOT_LIBRARIES}'")
+message(STATUS "ZOT_LINK_LIBRARIES='${ZOT_LINK_LIBRARIES}'")
+message(STATUS "ZOT_LDFLAGS='${ZOT_LDFLAGS}'")
+message(STATUS "ZOT_CFLAGS='${ZOT_CFLAGS}'")
diff --git a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
index 6b8e884..7af425a 100644
--- a/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/FindPkgConfig/RunCMakeTest.cmake
@@ -51,6 +51,7 @@
     endif()
   endif()
   run_cmake(FindPkgConfig_GET_VARIABLE)
+  run_cmake(FindPkgConfig_GET_VARIABLE_DEFINE_VARIABLES)
   run_cmake(FindPkgConfig_GET_VARIABLE_PREFIX_PATH)
   run_cmake(FindPkgConfig_GET_VARIABLE_PKGCONFIG_PATH)
   run_cmake(FindPkgConfig_cache_variables)
@@ -59,6 +60,6 @@
   run_cmake(FindPkgConfig_GET_MATCHING_MODULE_NAME)
   run_cmake(FindPkgConfig_empty_target)
   if(NOT PKG_CONFIG_DONT_SUPPORT_SPACES_IN_PATH)
-    run_cmake(FindPkgConfig_LIBRARY_PATH)
+    run_cmake(FindPkgConfig_SYSTEM_PATH)
   endif()
 endif ()
diff --git a/Tests/RunCMake/Framework/FrameworkConsumption.cmake b/Tests/RunCMake/Framework/FrameworkConsumption.cmake
index 2180cf9..f831a94 100644
--- a/Tests/RunCMake/Framework/FrameworkConsumption.cmake
+++ b/Tests/RunCMake/Framework/FrameworkConsumption.cmake
@@ -1,5 +1,7 @@
 enable_language(C)
 
+set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}/install")
+
 # Create framework and ensure header is placed in Headers
 set(input_header "${CMAKE_SOURCE_DIR}/Gui.h")
 add_library(Gui SHARED Gui.c "${input_header}")
@@ -8,6 +10,8 @@
     FRAMEWORK TRUE
 )
 
+install(TARGETS Gui DESTINATION .)
+
 add_executable(app main.c)
 
 target_link_libraries(app PRIVATE Gui)
diff --git a/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake b/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake
index bcf6c29..b50a8ad 100644
--- a/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake
+++ b/Tests/RunCMake/Framework/FrameworkSystemIncludeTest.cmake
@@ -8,7 +8,7 @@
 )
 
 add_library(testcase FrameworkSystemIncludeTest.c)
-target_compile_options(testcase PRIVATE "-Werror=#pragma-messages")
+target_compile_options(testcase PRIVATE "$<IF:$<C_COMPILER_ID:GNU>,-Werror,-Werror=#pragma-messages>")
 target_link_libraries(testcase PRIVATE Example::Example)
 
 
@@ -20,5 +20,17 @@
 )
 
 add_library(testcase2 FrameworkSystemIncludeTest.c)
-target_compile_options(testcase2 PRIVATE "-Werror=#pragma-messages")
+target_compile_options(testcase2 PRIVATE "$<IF:$<C_COMPILER_ID:GNU>,-Werror,-Werror=#pragma-messages>")
 target_link_libraries(testcase2 PRIVATE Example::Example2)
+
+
+
+add_library(Example::Example3 SHARED IMPORTED)
+set_target_properties(Example::Example3 PROPERTIES
+  FRAMEWORK 1
+  IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/subdir/Example.framework"
+)
+
+add_library(testcase3 FrameworkSystemIncludeTest.c)
+target_compile_options(testcase3 PRIVATE "$<IF:$<C_COMPILER_ID:GNU>,-Werror,-Werror=#pragma-messages>")
+target_link_libraries(testcase3 PRIVATE Example::Example3)
diff --git a/Tests/RunCMake/Framework/ImportedFrameworkConsumption.cmake b/Tests/RunCMake/Framework/ImportedFrameworkConsumption.cmake
new file mode 100644
index 0000000..c44a1bb
--- /dev/null
+++ b/Tests/RunCMake/Framework/ImportedFrameworkConsumption.cmake
@@ -0,0 +1,7 @@
+enable_language(C)
+
+add_library(Gui IMPORTED UNKNOWN)
+set_property(TARGET Gui PROPERTY IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/../FrameworkConsumption-build/install/Gui.framework")
+
+add_executable(app main.c)
+target_link_libraries(app PRIVATE Gui)
diff --git a/Tests/RunCMake/Framework/RunCMakeTest.cmake b/Tests/RunCMake/Framework/RunCMakeTest.cmake
index a767130..2c5b46f 100644
--- a/Tests/RunCMake/Framework/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Framework/RunCMakeTest.cmake
@@ -15,8 +15,10 @@
   run_cmake_command(${Name} ${CMAKE_COMMAND} --build .)
 endfunction()
 
-framework_layout_test(iOSFrameworkLayout-build ios SHARED)
-framework_layout_test(iOSFrameworkLayout-build ios STATIC)
+if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU")
+  framework_layout_test(iOSFrameworkLayout-build ios SHARED)
+  framework_layout_test(iOSFrameworkLayout-build ios STATIC)
+endif()
 framework_layout_test(OSXFrameworkLayout-build osx SHARED)
 framework_layout_test(OSXFrameworkLayout-build osx STATIC)
 
@@ -36,13 +38,17 @@
   run_cmake_command(FrameworkType${Type}-build ${CMAKE_COMMAND} --build .)
 endfunction()
 
-framework_type_test(ios SHARED NO)
-framework_type_test(ios STATIC NO)
+if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU")
+  framework_type_test(ios SHARED NO)
+  framework_type_test(ios STATIC NO)
+endif()
 framework_type_test(osx SHARED NO)
 framework_type_test(osx STATIC NO)
 
-framework_type_test(ios SHARED YES)
-framework_type_test(ios STATIC YES)
+if (NOT CMAKE_C_COMPILER_ID STREQUAL "GNU")
+  framework_type_test(ios SHARED YES)
+  framework_type_test(ios STATIC YES)
+endif()
 framework_type_test(osx SHARED YES)
 framework_type_test(osx STATIC YES)
 
@@ -113,7 +119,16 @@
   file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
   file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
   run_cmake(FrameworkConsumption)
-  run_cmake_command(FrameworkConsumption-build ${CMAKE_COMMAND} --build .)
+  run_cmake_command(FrameworkConsumption-build ${CMAKE_COMMAND} --build . --config Release)
+  run_cmake_command(FrameworkConsumption-install ${CMAKE_COMMAND} --install . --config Release)
+
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/ImportedFrameworkConsumption-build")
+  set(RunCMake_TEST_NO_CLEAN 1)
+
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+  run_cmake(ImportedFrameworkConsumption)
+  run_cmake_command(ImportedFrameworkConsumption-build ${CMAKE_COMMAND} --build . --config Release)
 endfunction()
 
 framework_consumption()
diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake
index 3e35aa3..a34708e 100644
--- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake
+++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake
@@ -98,7 +98,7 @@
   if((CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "IntelLLVM") AND
     CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")
     set(_platform Win32-Clang)
-  elseif(MSVC AND COMPILER_HAS_DEPRECATED)
+  elseif((MSVC OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC") AND COMPILER_HAS_DEPRECATED)
     set(_platform Win32)
   elseif(CYGWIN AND COMPILER_HAS_DEPRECATED)
     set(_platform Cygwin)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-result.txt
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-stderr.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-stderr.txt
new file mode 100644
index 0000000..1267cb3
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at BadAND.cmake:2 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<0>
+
+  \$<0> expression requires a parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND.cmake
new file mode 100644
index 0000000..91efaf4
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadAND.cmake
@@ -0,0 +1,4 @@
+set(error $<0>)
+add_custom_target(check ALL COMMAND check
+  $<AND:1,${error}>
+)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-result.txt
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-stderr.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-stderr.txt
new file mode 100644
index 0000000..4e296a5
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at BadIF.cmake:2 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<0>
+
+  \$<0> expression requires a parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF.cmake
new file mode 100644
index 0000000..797cc69
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadIF.cmake
@@ -0,0 +1,4 @@
+set(error $<0>)
+add_custom_target(check ALL COMMAND check
+  $<IF:0,1,${error}>
+)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-result.txt
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-stderr.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-stderr.txt
new file mode 100644
index 0000000..7876d7d
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at BadOR.cmake:2 \(add_custom_target\):
+  Error evaluating generator expression:
+
+    \$<0>
+
+  \$<0> expression requires a parameter.
+Call Stack \(most recent call first\):
+  CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR.cmake
new file mode 100644
index 0000000..7477b8f
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/BadOR.cmake
@@ -0,0 +1,4 @@
+set(error $<0>)
+add_custom_target(check ALL COMMAND check
+  $<OR:0,${error}>
+)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/CMakeLists.txt b/Tests/RunCMake/GeneratorExpressionShortCircuit/CMakeLists.txt
new file mode 100644
index 0000000..54a4d62
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.26)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodAND.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodAND.cmake
new file mode 100644
index 0000000..26bcaba
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodAND.cmake
@@ -0,0 +1,4 @@
+set(error $<0>)
+add_custom_target(check ALL COMMAND check
+  $<AND:0,${error}>
+)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodIF.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodIF.cmake
new file mode 100644
index 0000000..1f9fbe6
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodIF.cmake
@@ -0,0 +1,5 @@
+set(error $<0>)
+add_custom_target(check ALL
+  COMMAND check $<IF:1,1,${error}>
+  COMMAND Check $<IF:0,${error},1>
+)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodOR.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodOR.cmake
new file mode 100644
index 0000000..b574937
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/GoodOR.cmake
@@ -0,0 +1,4 @@
+set(error $<0>)
+add_custom_target(check ALL COMMAND check
+  $<OR:1,${error}>
+)
diff --git a/Tests/RunCMake/GeneratorExpressionShortCircuit/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpressionShortCircuit/RunCMakeTest.cmake
new file mode 100644
index 0000000..b0ad679
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpressionShortCircuit/RunCMakeTest.cmake
@@ -0,0 +1,9 @@
+include(RunCMake)
+
+run_cmake(GoodIF)
+run_cmake(GoodAND)
+run_cmake(GoodOR)
+
+run_cmake(BadIF)
+run_cmake(BadAND)
+run_cmake(BadOR)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-result.txt
diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt
new file mode 100644
index 0000000..61188b6
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1-stderr.txt
@@ -0,0 +1,10 @@
+CMake Error at CMakeLists.txt:[0-9]+ \(project\):
+  Generator
+
+    Xcode
+
+  toolset specification field
+
+    buildsystem=1
+
+  is not allowed with Xcode [0-9.]+\.$
diff --git a/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake
new file mode 100644
index 0000000..2fc38e5
--- /dev/null
+++ b/Tests/RunCMake/GeneratorToolset/BadToolsetXcodeBuildSystem1.cmake
@@ -0,0 +1 @@
+message(FATAL_ERROR "This should not be reached!")
diff --git a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
index a742391..71cc2d4 100644
--- a/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorToolset/RunCMakeTest.cmake
@@ -89,7 +89,11 @@
     set(RunCMake_GENERATOR_TOOLSET "Test Toolset")
     run_cmake(TestToolsetXcodeBuildSystemDefault12)
     set(RunCMake_GENERATOR_TOOLSET "Test Toolset,buildsystem=1")
-    run_cmake(TestToolsetXcodeBuildSystem1)
+    if(XCODE_VERSION VERSION_GREATER_EQUAL 14)
+      run_cmake(BadToolsetXcodeBuildSystem1)
+    else()
+      run_cmake(TestToolsetXcodeBuildSystem1)
+    endif()
     set(RunCMake_GENERATOR_TOOLSET "Test Toolset,buildsystem=12")
     run_cmake(TestToolsetXcodeBuildSystem12)
   else()
diff --git a/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake b/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake
index d2dc530..571e4aa 100644
--- a/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake
+++ b/Tests/RunCMake/GoogleTest/xcode_sign_adhoc.cmake
@@ -1,8 +1,14 @@
 function(xcode_sign_adhoc target)
   if(CMAKE_GENERATOR STREQUAL "Xcode" AND
-     "${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64")
-    # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing.
-    # See CMake Issue 21845.
-    target_link_options(${target} PRIVATE LINKER:-adhoc_codesign)
+      "${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64")
+    if(XCODE_VERSION VERSION_GREATER_EQUAL 15)
+      # Xcode 15+ enforces '-Xlinker -no_adhoc_codesign' after user flags,
+      # so we cannot convince the linker to add an adhoc signature.
+      add_custom_command(TARGET ${target} POST_BUILD COMMAND codesign --sign - --force "$<TARGET_FILE:${target}>")
+    else()
+      # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing.
+      # See CMake Issue 21845.
+      target_link_options(${target} PRIVATE LINKER:-adhoc_codesign)
+    endif()
   endif()
 endfunction()
diff --git a/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt b/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt
new file mode 100644
index 0000000..4499d97
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeTypeInfo/CMP0117-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0117-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0117 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/Make/DetectJobServer-present.cmake b/Tests/RunCMake/Make/DetectJobServer-present.cmake
new file mode 100644
index 0000000..cfaf7be
--- /dev/null
+++ b/Tests/RunCMake/Make/DetectJobServer-present.cmake
@@ -0,0 +1,13 @@
+# Verifies that the jobserver is present
+add_custom_command(OUTPUT custom_command.txt
+  JOB_SERVER_AWARE ON
+  COMMENT "Should detect jobserver support"
+  COMMAND ${DETECT_JOBSERVER} "custom_command.txt"
+)
+
+# trigger the custom command to run
+add_custom_target(dummy ALL
+  JOB_SERVER_AWARE ON
+  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/custom_command.txt
+  COMMAND ${DETECT_JOBSERVER} "custom_target.txt"
+)
diff --git a/Tests/RunCMake/Make/Foo/CMakeLists.txt b/Tests/RunCMake/Make/Foo/CMakeLists.txt
new file mode 100644
index 0000000..baa6634
--- /dev/null
+++ b/Tests/RunCMake/Make/Foo/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 3.26)
+project(Foo NONE)
+
+add_custom_target(drive ALL COMMAND ${CMAKE_COMMAND} -E true)
diff --git a/Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake b/Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake
new file mode 100644
index 0000000..dbc1555
--- /dev/null
+++ b/Tests/RunCMake/Make/GNUMakeJobServerAware-check.cmake
@@ -0,0 +1,18 @@
+set(BUILD_DIR "${RunCMake_BINARY_DIR}/GNUMakeJobServerAware-build")
+
+function(check target regex)
+  file(STRINGS ${BUILD_DIR}/${target} lines
+    REGEX ${regex}
+  )
+
+  list(LENGTH lines len)
+  if(len EQUAL 0)
+    message(FATAL_ERROR "Could not find matching lines '${regex}' in ${BUILD_DIR}/${target}")
+  endif()
+endfunction()
+
+check("CMakeFiles/dummy.dir/build.make" [[\+\$\(CMAKE_COMMAND\) -E true]])
+check("CMakeFiles/dummy2.dir/build.make" [[\+\$\(CMAKE_COMMAND\) -E true]])
+
+check("CMakeFiles/dummy3.dir/build.make" [[\+cd (/d )?"?.*"? && \$\(CMAKE_COMMAND\) -E true]])
+check("CMakeFiles/dummy4.dir/build.make" [[\+cd (/d )?"?.*"? && \$\(CMAKE_COMMAND\) -E true]])
diff --git a/Tests/RunCMake/Make/GNUMakeJobServerAware.cmake b/Tests/RunCMake/Make/GNUMakeJobServerAware.cmake
new file mode 100644
index 0000000..d92e842
--- /dev/null
+++ b/Tests/RunCMake/Make/GNUMakeJobServerAware.cmake
@@ -0,0 +1,31 @@
+# Test JOB_SERVER_AWARE with custom commands
+add_custom_command(
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/missing"
+  JOB_SERVER_AWARE ON
+  COMMAND $(CMAKE_COMMAND) -E true
+)
+add_custom_target(dummy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/missing")
+
+# Test JOB_SERVER_AWARE with custom targets
+add_custom_target(
+  dummy2 ALL
+  JOB_SERVER_AWARE ON
+  COMMAND $(CMAKE_COMMAND) -E true
+)
+
+# Test JOB_SERVER_AWARE with custom commands with WORKING_DIRECTORY
+add_custom_command(
+  OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/missing2"
+  JOB_SERVER_AWARE ON
+  COMMAND $(CMAKE_COMMAND) -E true
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Foo"
+)
+add_custom_target(dummy3 ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/missing2")
+
+# Test JOB_SERVER_AWARE with custom targets with WORKING_DIRECTORY
+add_custom_target(
+  dummy4 ALL
+  JOB_SERVER_AWARE ON
+  COMMAND $(CMAKE_COMMAND) -E true
+  WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Foo"
+)
diff --git a/Tests/RunCMake/Make/RunCMakeTest.cmake b/Tests/RunCMake/Make/RunCMakeTest.cmake
index c7717ec..5d1ba48 100644
--- a/Tests/RunCMake/Make/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Make/RunCMakeTest.cmake
@@ -70,3 +70,22 @@
   run_CMP0113(OLD)
   run_CMP0113(NEW)
 endif()
+
+function(detect_jobserver_present)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DetectJobServer-present-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OPTIONS "-DDETECT_JOBSERVER=${DETECT_JOBSERVER}")
+  run_cmake(DetectJobServer-present)
+  run_cmake_command(DetectJobServer-present-parallel-build ${CMAKE_COMMAND} --build . -j4)
+endfunction()
+
+# Jobservers are currently only supported by GNU makes, except MSYS2 make
+if(MAKE_IS_GNU AND NOT RunCMake_GENERATOR MATCHES "MSYS Makefiles")
+  detect_jobserver_present()
+endif()
+
+if(MAKE_IS_GNU)
+  # In GNU makes, `JOB_SERVER_AWARE` support is implemented by prefixing
+  # commands with the '+' operator.
+  run_cmake(GNUMakeJobServerAware)
+endif()
diff --git a/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake b/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake
deleted file mode 100644
index c441169..0000000
--- a/Tests/RunCMake/Ninja/QtAutoMocDeps.cmake
+++ /dev/null
@@ -1,27 +0,0 @@
-enable_language(CXX)
-
-set(QtX Qt${with_qt_version})
-
-find_package(${QtX} REQUIRED COMPONENTS Core)
-
-set(CMAKE_AUTOMOC ON)
-
-add_library(simple_lib SHARED simple_lib.cpp)
-add_executable(app_with_qt app.cpp app_qt.cpp)
-target_link_libraries(app_with_qt PRIVATE simple_lib ${QtX}::Core)
-
-if(${QtX}Widgets_DIR)
-  find_package(${QtX} REQUIRED COMPONENTS Widgets)
-  if(with_qt_version STREQUAL 5)
-    qt5_wrap_ui(_headers MyWindow.ui)
-  else()
-    qt_wrap_ui(_headers MyWindow.ui)
-  endif()
-  add_executable(app_with_widget app.cpp MyWindow.cpp ${_headers})
-  target_link_libraries(app_with_widget PRIVATE ${QtX}::Widgets)
-  target_include_directories(app_with_widget PRIVATE "${CMAKE_BINARY_DIR}")
-endif()
-
-add_subdirectory(QtSubDir1)
-add_subdirectory(QtSubDir2)
-add_subdirectory(QtSubDir3)
diff --git a/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt
deleted file mode 100644
index 3a12dcd..0000000
--- a/Tests/RunCMake/Ninja/QtSubDir1/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_policy(SET CMP0116 OLD)
-
-add_executable(sub_exe_1 ../app.cpp)
-target_link_libraries(sub_exe_1 PRIVATE ${QtX}::Core)
diff --git a/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt
deleted file mode 100644
index a2f77e4..0000000
--- a/Tests/RunCMake/Ninja/QtSubDir2/CMakeLists.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-cmake_policy(SET CMP0116 NEW)
-
-add_executable(sub_exe_2 ../app.cpp)
-target_link_libraries(sub_exe_2 PRIVATE ${QtX}::Core)
diff --git a/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt b/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt
deleted file mode 100644
index 70644fa..0000000
--- a/Tests/RunCMake/Ninja/QtSubDir3/CMakeLists.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-add_executable(sub_exe_3 ../app.cpp)
-target_link_libraries(sub_exe_3 PRIVATE ${QtX}::Core)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 6eb0b1d..69f2587 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -348,7 +348,11 @@
   return()
 endif()
 
-foreach(ninja_output_path_prefix "sub space" "sub")
+set(ninja_output_path_prefixes "sub")
+if(NOT CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
+  list(APPEND ninja_output_path_prefixes "sub space")
+endif()
+foreach(ninja_output_path_prefix IN LISTS ninja_output_path_prefixes)
   run_sub_cmake(Executable "${ninja_output_path_prefix}")
   run_sub_cmake(StaticLib  "${ninja_output_path_prefix}")
   run_sub_cmake(SharedLib "${ninja_output_path_prefix}")
@@ -380,45 +384,6 @@
 endfunction()
 run_ChangeBuildType()
 
-function(run_QtAutoMocDeps)
-  set(QtX Qt${CMake_TEST_Qt_version})
-  if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
-    set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/QtAutoMocDeps-build)
-    run_cmake_with_options(QtAutoMocDeps
-      "-Dwith_qt_version=${CMake_TEST_Qt_version}"
-      "-D${QtX}_DIR=${${QtX}_DIR}"
-      "-D${QtX}Core_DIR=${${QtX}Core_DIR}"
-      "-D${QtX}Widgets_DIR=${${QtX}Widgets_DIR}"
-      "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
-    )
-    # Build the project.
-    run_ninja("${RunCMake_TEST_BINARY_DIR}")
-    # Touch just the library source file, which shouldn't cause a rerun of AUTOMOC
-    # for app_with_qt target.
-    file(TOUCH "${RunCMake_SOURCE_DIR}/simple_lib.cpp")
-    # Build and assert that AUTOMOC was not run for app_with_qt.
-    run_ninja("${RunCMake_TEST_BINARY_DIR}")
-    if(ninja_stdout MATCHES "Automatic MOC for target app_with_qt")
-      message(FATAL_ERROR
-        "AUTOMOC should not have executed for 'app_with_qt' target:\nstdout:\n${ninja_stdout}")
-    endif()
-    # Assert that the subdir executables were not rebuilt.
-    if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_1")
-      message(FATAL_ERROR
-        "AUTOMOC should not have executed for 'sub_exe_1' target:\nstdout:\n${ninja_stdout}")
-    endif()
-    if(ninja_stdout MATCHES "Automatic MOC for target sub_exe_2")
-      message(FATAL_ERROR
-        "AUTOMOC should not have executed for 'sub_exe_2' target:\nstdout:\n${ninja_stdout}")
-    endif()
-    # Touch a header file to make sure an automoc dependency cycle is not introduced.
-    file(TOUCH "${RunCMake_SOURCE_DIR}/MyWindow.h")
-    run_ninja("${RunCMake_TEST_BINARY_DIR}")
-    # Need to run a second time to hit the dependency cycle.
-    run_ninja("${RunCMake_TEST_BINARY_DIR}")
-  endif()
-endfunction()
-
 function(run_QtAutoMocSkipPch)
   set(QtX Qt${CMake_TEST_Qt_version})
   if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
@@ -433,7 +398,7 @@
     run_ninja("${RunCMake_TEST_BINARY_DIR}")
   endif()
 endfunction()
+
 if(CMake_TEST_Qt_version)
-  run_QtAutoMocDeps()
   run_QtAutoMocSkipPch()
 endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake
index 30b24bf..e65be3b 100644
--- a/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/CompileCommands-check.cmake
@@ -8,7 +8,7 @@
 ]*",
   "file": "[^
 ]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)",
-  "output": "(CMakeFiles/exe\.dir/Debug/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Debug\\\\main\.c\.obj)"
+  "output": "(CMakeFiles/exe\.dir/Debug/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Debug\\\\main\.c\.(obj|o))"
 },
 {
   "directory": "[^
@@ -18,7 +18,7 @@
 ]*",
   "file": "[^
 ]*(/Tests/RunCMake/NinjaMultiConfig/main\.c|\\\\Tests\\\\RunCMake\\\\NinjaMultiConfig\\\\main\.c)",
-  "output": "(CMakeFiles/exe\.dir/Release/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Release\\\\main\.c\.obj)"
+  "output": "(CMakeFiles/exe\.dir/Release/main\.c\.o|CMakeFiles\\\\exe\.dir\\\\Release\\\\main\.c\.(obj|o))"
 }
 ]$]==])
 
diff --git a/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake
deleted file mode 100644
index 2d8df13..0000000
--- a/Tests/RunCMake/NinjaMultiConfig/QtX-debug-in-release-graph-build-check.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-check_files("${RunCMake_TEST_BINARY_DIR}"
-  INCLUDE
-    ${AUTOGEN_FILES}
-
-    ${TARGET_FILE_exe_Debug}
-    ${TARGET_OBJECT_FILES_exe_Debug}
-  )
diff --git a/Tests/RunCMake/NinjaMultiConfig/QtX.cmake b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
index 130f883..dded383 100644
--- a/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/QtX.cmake
@@ -19,16 +19,17 @@
   set(moc_writes_depfiles 1)
 endif()
 
-set(autogen_files)
-if(moc_writes_depfiles)
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps")
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp")
-endif()
-foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES)
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${c}.cpp")
-  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp")
+foreach(CONFIG IN LISTS CMAKE_CONFIGURATION_TYPES)
+  set(autogen_files)
   if(moc_writes_depfiles)
-    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp.d")
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps_${CONFIG}")
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp_${CONFIG}")
   endif()
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${CONFIG}.cpp")
+    list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${CONFIG}/moc_qt5.cpp")
+    if(moc_writes_depfiles)
+      list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${CONFIG}/moc_qt5.cpp.d")
+    endif()
+  file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES_${CONFIG} [==[${autogen_files}]==])\n")
+  unset(autogen_files)
 endforeach()
-file(APPEND "${CMAKE_BINARY_DIR}/target_files.cmake" "set(AUTOGEN_FILES [==[${autogen_files}]==])\n")
diff --git a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
index 47f5eee..a39606d 100644
--- a/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
+++ b/Tests/RunCMake/NinjaMultiConfig/RunCMakeTest.cmake
@@ -17,7 +17,7 @@
   list(SORT expected)
 
   file(GLOB_RECURSE actual "${dir}/*")
-  list(FILTER actual EXCLUDE REGEX "/CMakeFiles/|\\.ninja$|/CMakeCache\\.txt$|/target_files[^/]*\\.cmake$|/\\.ninja_[^/]*$|/cmake_install\\.cmake$|\\.ilk$|\\.manifest$|\\.pdb$|\\.exp$|/install_manifest\\.txt$|/\\.qt/QtDeploySupport[^/]*\\.cmake$")
+  list(FILTER actual EXCLUDE REGEX "/CMakeFiles/|\\.ninja$|/CMakeCache\\.txt$|/target_files[^/]*\\.cmake$|/\\.ninja_[^/]*$|/cmake_install\\.cmake$|\\.ilk$|\\.manifest$|\\.odx$|\\.pdb$|\\.exp$|/install_manifest\\.txt$|/\\.qt/QtDeploySupport[^/]*\\.cmake$")
   foreach(f IN LISTS _check_files_INCLUDE _check_files_EXCLUDE)
     if(EXISTS ${f})
       list(APPEND actual ${f})
@@ -486,11 +486,35 @@
     "-D${QtX}Core_DIR=${${QtX}Core_DIR}"
     "-DCMAKE_PREFIX_PATH:STRING=${CMAKE_PREFIX_PATH}"
   )
-  run_cmake_configure(QtX)
-  unset(RunCMake_TEST_OPTIONS)
-  include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake)
-  run_cmake_build(QtX debug-in-release-graph Release exe:Debug)
+
+  foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
+    foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+      block()
+        run_cmake_configure(QtX)
+        include(${RunCMake_TEST_BINARY_DIR}/target_files.cmake)
+        run_cmake_build(QtX ${target_config}-in-${ninja_config}-graph ${ninja_config} exe:${target_config})
+        check_files("${RunCMake_TEST_BINARY_DIR}"
+        INCLUDE
+          "${AUTOGEN_FILES_${target_config}}"
+          "${TARGET_FILE_exe_${target_config}}"
+          "${TARGET_OBJECT_FILES_exe_${target_config}}"
+        )
+        if (DEFINED RunCMake_TEST_FAILED AND NOT RunCMake_TEST_FAILED STREQUAL "")
+          message(FATAL_ERROR "RunCMake_TEST_FAILED:${RunCMake_TEST_FAILED}")
+        endif()
+
+        check_file_contents("${RunCMake_TEST_BINARY_DIR}/exe_autogen/deps_${target_config}" "exe_autogen/timestamp_${target_config}")
+        if (DEFINED RunCMake_TEST_FAILED AND NOT RunCMake_TEST_FAILED STREQUAL "")
+          message(FATAL_ERROR "RunCMake_TEST_FAILED:${RunCMake_TEST_FAILED}")
+        endif()
+      endblock()
+    endforeach()
+  endforeach()
   if(CMake_TEST_${QtX}Core_Version VERSION_GREATER_EQUAL 5.15.0)
-    run_ninja(QtX automoc-check build-Debug.ninja -t query exe_autogen/timestamp)
+  foreach(target_config IN ITEMS Debug Release RelWithDebInfo)
+    foreach(ninja_config IN ITEMS Debug Release RelWithDebInfo)
+    run_ninja(QtX automoc-check-${target_config} build-${ninja_config}.ninja -t query exe_autogen/timestamp_${target_config})
+    endforeach()
+  endforeach()
   endif()
 endif()
diff --git a/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt b/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt
index 6db4bcc..ace6512 100644
--- a/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt
+++ b/Tests/RunCMake/NinjaMultiConfig/SimpleNoCross-all-target-ninja-stderr.txt
@@ -1 +1 @@
-^ninja: error: unknown target 'simplestatic:all'$
+^ninja: error: unknown target 'simplestatic:all'(, did you mean 'simplestatic.l'\?)?$
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-Source-check.cmake
new file mode 100644
index 0000000..7658930
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-Source-check.cmake
@@ -0,0 +1,3 @@
+if (EXISTS ${RunCMake_TEST_BINARY_DIR}/empty.cpp)
+  set(RunCMake_TEST_FAILED "Compiled source generated before compilation of consumers.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-check.cmake
new file mode 100644
index 0000000..3287a65
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-EmptyFileSet-check.cmake
@@ -0,0 +1,3 @@
+if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/empty.cpp)
+  set(RunCMake_TEST_FAILED "Compiled source did not generate.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-NoFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-NoFileSet-Source-check.cmake
new file mode 100644
index 0000000..9e99ed8
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-NoFileSet-Source-check.cmake
@@ -0,0 +1,3 @@
+if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/none.cpp)
+  set(RunCMake_TEST_FAILED "Private source dependency used for target without filesets.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-Source-check.cmake
new file mode 100644
index 0000000..dd814c9
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-Source-check.cmake
@@ -0,0 +1,3 @@
+if (EXISTS ${RunCMake_TEST_BINARY_DIR}/private.h)
+  set(RunCMake_TEST_FAILED "Private header generated before compilation.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-check.cmake
new file mode 100644
index 0000000..357b8c0
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PrivateFileSet-check.cmake
@@ -0,0 +1,3 @@
+if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/private.h)
+  set(RunCMake_TEST_FAILED "Private header generated before compilation.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PublicFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PublicFileSet-Source-check.cmake
new file mode 100644
index 0000000..cfceb7a
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW-Build-PublicFileSet-Source-check.cmake
@@ -0,0 +1,3 @@
+if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/public.h)
+  set(RunCMake_TEST_FAILED "Public header did not generate before compilation.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW.cmake
new file mode 100644
index 0000000..3330269
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0154 NEW)
+include(CMP0154-common.cmake)
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-EmptyFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-EmptyFileSet-Source-check.cmake
new file mode 100644
index 0000000..8ee1ad4
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-EmptyFileSet-Source-check.cmake
@@ -0,0 +1,3 @@
+if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/empty.cpp)
+  set(RunCMake_TEST_FAILED "Policy CMP0154 set to OLD but using new behavior compiled sources.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-PrivateFileSet-Source-check.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-PrivateFileSet-Source-check.cmake
new file mode 100644
index 0000000..25c23d7
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD-Build-PrivateFileSet-Source-check.cmake
@@ -0,0 +1,3 @@
+if (NOT EXISTS ${RunCMake_TEST_BINARY_DIR}/private.h)
+  set(RunCMake_TEST_FAILED "Policy CMP0154 set to OLD but using new behavior private headers.")
+endif()
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD.cmake
new file mode 100644
index 0000000..691530c
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0154 OLD)
+include(CMP0154-common.cmake)
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMP0154-common.cmake b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-common.cmake
new file mode 100644
index 0000000..a9e6d20
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMP0154-common.cmake
@@ -0,0 +1,45 @@
+enable_language(CXX)
+
+function(copy_file file dest)
+  add_custom_command(
+    OUTPUT ${CMAKE_BINARY_DIR}/${dest}
+    COMMAND ${CMAKE_COMMAND} -E copy
+      ${CMAKE_SOURCE_DIR}/${file} ${CMAKE_BINARY_DIR}/${dest}
+  )
+endfunction()
+
+copy_file(header.h.in private.h)
+copy_file(header.h.in public.h)
+copy_file(source.cpp.in empty.cpp)
+copy_file(source.cpp.in none.cpp)
+
+add_library(HelloLib_PrivateFileSet STATIC hello_lib.cpp)
+target_sources(HelloLib_PrivateFileSet
+  PRIVATE FILE_SET HEADERS
+    BASE_DIRS ${CMAKE_BINARY_DIR}
+    FILES ${CMAKE_BINARY_DIR}/private.h
+)
+
+add_library(HelloLib_PublicFileSet STATIC hello_lib.cpp)
+target_sources(HelloLib_PublicFileSet
+  PUBLIC FILE_SET HEADERS
+    BASE_DIRS ${CMAKE_BINARY_DIR}
+    FILES ${CMAKE_BINARY_DIR}/public.h
+)
+
+add_library(HelloLib_EmptyFileSet STATIC hello_lib.cpp empty.cpp)
+target_sources(HelloLib_EmptyFileSet
+  PUBLIC FILE_SET HEADERS
+)
+
+add_library(HelloLib_NoFileSet STATIC hello_lib.cpp none.cpp)
+
+function(hello_executable name)
+  add_executable(Hello_${name} hello.cpp)
+  target_link_libraries(Hello_${name} PRIVATE HelloLib_${name})
+endfunction()
+
+hello_executable(PrivateFileSet)
+hello_executable(PublicFileSet)
+hello_executable(EmptyFileSet)
+hello_executable(NoFileSet)
diff --git a/Tests/RunCMake/NinjaPrivateDeps/CMakeLists.txt b/Tests/RunCMake/NinjaPrivateDeps/CMakeLists.txt
new file mode 100644
index 0000000..54a4d62
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.26)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/NinjaPrivateDeps/RunCMakeTest.cmake b/Tests/RunCMake/NinjaPrivateDeps/RunCMakeTest.cmake
new file mode 100644
index 0000000..e821185
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/RunCMakeTest.cmake
@@ -0,0 +1,30 @@
+include(RunCMake)
+
+function(compile_source test target)
+  if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    set(config "Debug/")
+  endif()
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-Build-${target}-Source ${CMAKE_COMMAND} --build .
+    --target CMakeFiles/Hello_${target}.dir/${config}hello.cpp${CMAKE_C_OUTPUT_EXTENSION})
+endfunction()
+
+function(compile_target test target)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(${test}-Build-${target} ${CMAKE_COMMAND} --build .
+    --target Hello_${target})
+endfunction()
+
+run_cmake(CMP0154-OLD)
+compile_source(CMP0154-OLD PrivateFileSet)
+compile_source(CMP0154-OLD EmptyFileSet)
+
+run_cmake(CMP0154-NEW)
+compile_source(CMP0154-NEW PrivateFileSet)
+compile_target(CMP0154-NEW PrivateFileSet)
+compile_source(CMP0154-NEW PublicFileSet)
+compile_source(CMP0154-NEW NoFileSet)
+compile_source(CMP0154-NEW EmptyFileSet)
+compile_target(CMP0154-NEW EmptyFileSet)
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/NinjaPrivateDeps/header.h.in
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/NinjaPrivateDeps/header.h.in
diff --git a/Tests/RunCMake/NinjaPrivateDeps/hello.cpp b/Tests/RunCMake/NinjaPrivateDeps/hello.cpp
new file mode 100644
index 0000000..34fabe9
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/hello.cpp
@@ -0,0 +1,6 @@
+#include "hello_lib.h"
+
+int main()
+{
+  say_hello();
+}
diff --git a/Tests/RunCMake/NinjaPrivateDeps/hello_lib.cpp b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.cpp
new file mode 100644
index 0000000..d67113f
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.cpp
@@ -0,0 +1,8 @@
+#include "hello_lib.h"
+
+#include <iostream>
+
+void say_hello()
+{
+  std::cout << "hello" << std::endl;
+}
diff --git a/Tests/RunCMake/NinjaPrivateDeps/hello_lib.h b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.h
new file mode 100644
index 0000000..3b4775f
--- /dev/null
+++ b/Tests/RunCMake/NinjaPrivateDeps/hello_lib.h
@@ -0,0 +1 @@
+void say_hello();
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/NinjaPrivateDeps/source.cpp.in
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/NinjaPrivateDeps/source.cpp.in
diff --git a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake
index 3d60556..a1fa7b3 100644
--- a/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake
+++ b/Tests/RunCMake/ObjectLibrary/LinkObjLHSShared.cmake
@@ -14,8 +14,14 @@
 # Verify that our dependency on OtherLib generated its versioning symlinks.
 if(CMAKE_GENERATOR STREQUAL "Xcode" AND
    "${CMAKE_SYSTEM_NAME};${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "Darwin;arm64")
-  # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing.
-  # See CMake Issue 21845.
-  target_link_options(LinkObjLHSShared PRIVATE LINKER:-adhoc_codesign)
+  if(XCODE_VERSION VERSION_GREATER_EQUAL 15)
+    # Xcode 15+ enforces '-Xlinker -no_adhoc_codesign' after user flags,
+    # so we cannot convince the linker to add an adhoc signature.
+    add_custom_command(TARGET LinkObjLHSShared POST_BUILD COMMAND codesign --sign - --force "$<TARGET_FILE:LinkObjLHSShared>")
+  else()
+    # Xcode runs POST_BUILD before signing, so let the linker use ad-hoc signing.
+    # See CMake Issue 21845.
+    target_link_options(LinkObjLHSShared PRIVATE LINKER:-adhoc_codesign)
+  endif()
 endif()
 add_custom_command(TARGET LinkObjLHSShared POST_BUILD COMMAND LinkObjLHSShared)
diff --git a/Tests/RunCMake/ParseImplicitData/windows_arm64-C-Clang-17.0.1-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_arm64-C-Clang-17.0.1-MSVC.input
new file mode 100644
index 0000000..d28b970
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_arm64-C-Clang-17.0.1-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=C
+CMAKE_LINKER=
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=ARM64
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=Clang
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=17.0.1
+CMAKE_C_COMPILER_VERSION_INTERAL=
+CMAKE_C_SIMULATE_ID=MSVC
+clang version 17.0.1
+Target: aarch64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\clang-cl.exe" -cc1 -triple aarch64-pc-windows-msvc19.36.32532 -emit-obj -mrelax-all -mincremental-linker-compatible -dumpdir a- -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name CMakeCCompilerABI.c -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu generic -target-feature +neon -target-feature +v8a -target-abi aapcs -D_MT -flto-visibility-public-std --dependent-lib=libcmt --dependent-lib=oldnames -stack-protector 2 -fdiagnostics-format msvc -v "-fcoverage-compilation-dir=C:\\DoesNotExist\\Temp" -resource-dir "C:\\DoesNotExist\\LLVM\\lib\\clang\\17" -internal-isystem "C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\ATLMFC\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\VS\\include" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\ATLMFC\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\VS\\include" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" "-fdebug-compilation-dir=C:\\DoesNotExist\\Temp" -ferror-limit 19 -fno-use-cxa-atexit -fms-extensions -fms-compatibility -fms-compatibility-version=19.36.32532 -fdelayed-template-parsing -target-feature -fmv -faddrsig -o "C:\\DoesNotExist\\Temp\\CMakeCCompilerABI-742dcb.obj" -x c CMakeCCompilerABI.c
+ "C:\\DoesNotExist\\LLVM\\bin\\lld-link" -out:CMakeCCompilerABI.exe "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\lib\\windows" -nologo "C:\\DoesNotExist\\Temp\\CMakeCCompilerABI-742dcb.obj"
diff --git a/Tests/RunCMake/ParseImplicitData/windows_arm64-CXX-Clang-17.0.1-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_arm64-CXX-Clang-17.0.1-MSVC.input
new file mode 100644
index 0000000..e82a4fa
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_arm64-CXX-Clang-17.0.1-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=ARM64
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=Clang
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=17.0.1
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+CMAKE_CXX_SIMULATE_ID=MSVC
+clang version 17.0.1
+Target: aarch64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\clang-cl.exe" -cc1 -triple aarch64-pc-windows-msvc19.36.32532 -emit-obj -mrelax-all -mincremental-linker-compatible -dumpdir a- -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name CMakeCXXCompilerABI.cpp -mrelocation-model pic -pic-level 2 -mframe-pointer=none -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu generic -target-feature +neon -target-feature +v8a -target-abi aapcs -D_MT -flto-visibility-public-std --dependent-lib=libcmt --dependent-lib=oldnames -stack-protector 2 -fdiagnostics-format msvc -v "-fcoverage-compilation-dir=C:\\DoesNotExist\\Temp" -resource-dir "C:\\DoesNotExist\\LLVM\\lib\\clang\\17" -internal-isystem "C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\ATLMFC\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\VS\\include" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.36.32532\\ATLMFC\\include" -internal-isystem "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\VS\\include" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\include\\10.0.22621.0\\ucrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\um" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\shared" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\winrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\10\\\\include\\10.0.22621.0\\\\cppwinrt" -internal-isystem "C:\\Program Files (x86)\\Windows Kits\\NETFXSDK\\4.8\\include\\um" -fdeprecated-macro "-fdebug-compilation-dir=C:\\DoesNotExist\\Temp" -ferror-limit 19 -fno-use-cxa-atexit -fms-extensions -fms-compatibility -fms-compatibility-version=19.36.32532 -std=c++14 -fdelayed-template-parsing -target-feature -fmv -faddrsig -o "C:\\DoesNotExist\\Temp\\CMakeCXXCompilerABI-266f4c.obj" -x c++ CMakeCXXCompilerABI.cpp
+ "C:\\DoesNotExist\\LLVM\\bin\\lld-link" -out:CMakeCXXCompilerABI.exe "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\lib\\windows" -nologo "C:\\DoesNotExist\\Temp\\CMakeCXXCompilerABI-266f4c.obj"
diff --git a/Tests/RunCMake/ParseImplicitData/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.input
new file mode 100644
index 0000000..4937a41
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=ARM64
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=LLVMFlang
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=17.0.1
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+CMAKE_Fortran_SIMULATE_ID=MSVC
+flang-new version 17.0.1
+Target: aarch64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\flang-new" -fc1 -triple aarch64-pc-windows-msvc19.36.32532 -emit-obj -mrelocation-model pic -pic-level 2 -target-cpu generic -target-feature +neon -target-feature +v8a -o "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-ac5f0c.o" -x f95-cpp-input CMakeFortranCompilerABI.F
+ "C:\\DoesNotExist\\LLVM\\bin\\lld-link" -out:a.exe "-libpath:C:\\DoesNotExist\\LLVM\\lib" Fortran_main.lib FortranRuntime.lib FortranDecimal.lib /subsystem:console "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\lib\\windows" -nologo "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-ac5f0c.o"
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-Clang-17.0.1-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-Clang-17.0.1-MSVC.input
new file mode 100644
index 0000000..b823880
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-C-Clang-17.0.1-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=C
+CMAKE_LINKER=
+CMAKE_C_COMPILER_ABI=
+CMAKE_C_COMPILER_AR=
+CMAKE_C_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_C_COMPILER_ID=Clang
+CMAKE_C_COMPILER_LAUNCHER=
+CMAKE_C_COMPILER_LOADED=1
+CMAKE_C_COMPILER_RANLIB=
+CMAKE_C_COMPILER_TARGET=
+CMAKE_C_COMPILER_VERSION=17.0.1
+CMAKE_C_COMPILER_VERSION_INTERAL=
+CMAKE_C_SIMULATE_ID=MSVC
+clang version 17.0.1
+Target: x86_64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\clang-cl.exe" "-cc1" "-triple" "x86_64-pc-windows-msvc19.29.30152" "-emit-obj" "-mrelax-all" "-mincremental-linker-compatible" "-dumpdir" "a-" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "CMakeCCompilerABI.c" "-mrelocation-model" "pic" "-pic-level" "2" "-mframe-pointer=none" "-relaxed-aliasing" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-funwind-tables=2" "-target-cpu" "x86-64" "-mllvm" "-x86-asm-syntax=intel" "-tune-cpu" "generic" "-D_MT" "-flto-visibility-public-std" "--dependent-lib=libcmt" "--dependent-lib=oldnames" "-stack-protector" "2" "-fms-volatile" "-fdiagnostics-format" "msvc" "-fcoverage-compilation-dir=C:\\DoesNotExist\\Temp" "-resource-dir" "C:\\DoesNotExist\\LLVM\\lib\\clang\\17" "-internal-isystem" "C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\atlmfc\\include" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\ucrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\shared" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\um" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\winrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\cppwinrt" "-fdebug-compilation-dir=C:\\DoesNotExist\\Temp" "-ferror-limit" "19" "-fno-use-cxa-atexit" "-fms-extensions" "-fms-compatibility" "-fms-compatibility-version=19.29.30152" "-fdelayed-template-parsing" "-faddrsig" "-o" "C:\\DoesNotExist\\Temp\\CMakeCCompilerABI-ebc8cc.obj" "-x" "c" "CMakeCCompilerABI.c"
+ "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64\\link.exe" "-out:CMakeCCompilerABI.exe" "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\lib\\windows" "-nologo" "C:\\DoesNotExist\\Temp\\CMakeCCompilerABI-ebc8cc.obj"
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-Clang-17.0.1-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-Clang-17.0.1-MSVC.input
new file mode 100644
index 0000000..756fd13
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-CXX-Clang-17.0.1-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=CXX
+CMAKE_LINKER=
+CMAKE_CXX_COMPILER_ABI=
+CMAKE_CXX_COMPILER_AR=
+CMAKE_CXX_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_CXX_COMPILER_ID=Clang
+CMAKE_CXX_COMPILER_LAUNCHER=
+CMAKE_CXX_COMPILER_LOADED=1
+CMAKE_CXX_COMPILER_RANLIB=
+CMAKE_CXX_COMPILER_TARGET=
+CMAKE_CXX_COMPILER_VERSION=17.0.1
+CMAKE_CXX_COMPILER_VERSION_INTERAL=
+CMAKE_CXX_SIMULATE_ID=MSVC
+clang version 17.0.1
+Target: x86_64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\clang-cl.exe" "-cc1" "-triple" "x86_64-pc-windows-msvc19.29.30152" "-emit-obj" "-mrelax-all" "-mincremental-linker-compatible" "-dumpdir" "a-" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "CMakeCXXCompilerABI.cpp" "-mrelocation-model" "pic" "-pic-level" "2" "-mframe-pointer=none" "-relaxed-aliasing" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-funwind-tables=2" "-target-cpu" "x86-64" "-mllvm" "-x86-asm-syntax=intel" "-tune-cpu" "generic" "-D_MT" "-flto-visibility-public-std" "--dependent-lib=libcmt" "--dependent-lib=oldnames" "-stack-protector" "2" "-fms-volatile" "-fdiagnostics-format" "msvc" "-fcoverage-compilation-dir=C:\\DoesNotExist\\Temp" "-resource-dir" "C:\\DoesNotExist\\LLVM\\lib\\clang\\17" "-internal-isystem" "C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\include" "-internal-isystem" "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\atlmfc\\include" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\ucrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\shared" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\um" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\winrt" "-internal-isystem" "C:\\Program Files (x86)\\Windows Kits\\10\\Include\\10.0.19041.0\\cppwinrt" "-fdeprecated-macro" "-fdebug-compilation-dir=C:\\DoesNotExist\\Temp" "-ferror-limit" "19" "-fno-use-cxa-atexit" "-fms-extensions" "-fms-compatibility" "-fms-compatibility-version=19.29.30152" "-std=c++14" "-fdelayed-template-parsing" "-faddrsig" "-o" "C:\\DoesNotExist\\Temp\\CMakeCXXCompilerABI-9170e6.obj" "-x" "c++" "CMakeCXXCompilerABI.cpp"
+ "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64\\link.exe" "-out:CMakeCXXCompilerABI.exe" "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\lib\\windows" "-nologo" "C:\\DoesNotExist\\Temp\\CMakeCXXCompilerABI-9170e6.obj"
diff --git a/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.input b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.input
new file mode 100644
index 0000000..fb75b51
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitData/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.input
@@ -0,0 +1,20 @@
+CMAKE_LANG=Fortran
+CMAKE_LINKER=
+CMAKE_Fortran_COMPILER_ABI=
+CMAKE_Fortran_COMPILER_AR=
+CMAKE_Fortran_COMPILER_ARCHITECTURE_ID=x64
+CMAKE_Fortran_COMPILER_EXTERNAL_TOOLCHAIN=
+CMAKE_Fortran_COMPILER_ID=LLVMFlang
+CMAKE_Fortran_COMPILER_LAUNCHER=
+CMAKE_Fortran_COMPILER_LOADED=1
+CMAKE_Fortran_COMPILER_RANLIB=
+CMAKE_Fortran_COMPILER_TARGET=
+CMAKE_Fortran_COMPILER_VERSION=17.0.1
+CMAKE_Fortran_COMPILER_VERSION_INTERAL=
+CMAKE_Fortran_SIMULATE_ID=MSVC
+flang-new version 17.0.1
+Target: x86_64-pc-windows-msvc
+Thread model: posix
+InstalledDir: C:\DoesNotExist\LLVM\bin
+ "C:\\DoesNotExist\\LLVM\\bin\\flang-new" "-fc1" "-triple" "x86_64-pc-windows-msvc19.29.30152" "-emit-obj" "-mrelocation-model" "pic" "-pic-level" "2" "-target-cpu" "x86-64" "-o" "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-54be37.o" "-x" "f95-cpp-input" "CMakeFortranCompilerABI.F"
+ "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC\\14.29.30133\\bin\\Hostx64\\x64\\link.exe" "-out:a.exe" "-libpath:C:\\DoesNotExist\\LLVM\\lib" "Fortran_main.lib" "FortranRuntime.lib" "FortranDecimal.lib" "/subsystem:console" "-libpath:C:\\DoesNotExist\\LLVM\\lib\\clang\\17\\lib\\windows" "-nologo" "C:\\DoesNotExist\\Temp\\CMakeFortranCompilerABI-54be37.o"
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
index fa7bf07..04998a2 100644
--- a/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/ParseImplicitLinkInfo.cmake
@@ -42,6 +42,8 @@
     netbsd_nostdinc-C-GNU-4.8.5 netbsd_nostdinc-CXX-GNU-4.8.5
   openbsd-C-Clang-5.0.1 openbsd-CXX-Clang-5.0.1
   sunos-C-SunPro-5.13.0 sunos-CXX-SunPro-5.13.0 sunos-Fortran-SunPro-8.8.0
+  windows_x86_64-C-Clang-17.0.1-MSVC windows_x86_64-CXX-Clang-17.0.1-MSVC windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC
+  windows_arm64-C-Clang-17.0.1-MSVC windows_arm64-CXX-Clang-17.0.1-MSVC windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC
   )
 
 if(CMAKE_HOST_WIN32)
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output
new file mode 100644
index 0000000..df9ef98
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-C-Clang-17.0.1-MSVC.output
@@ -0,0 +1,2 @@
+libs=
+dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output
new file mode 100644
index 0000000..df9ef98
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-CXX-Clang-17.0.1-MSVC.output
@@ -0,0 +1,2 @@
+libs=
+dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output
new file mode 100644
index 0000000..65f3494
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_arm64-Fortran-LLVMFlang-17.0.1-MSVC.output
@@ -0,0 +1,2 @@
+libs=Fortran_main\.lib;FortranRuntime\.lib;FortranDecimal\.lib
+dirs=C:/DoesNotExist/LLVM/lib;C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output
new file mode 100644
index 0000000..df9ef98
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-C-Clang-17.0.1-MSVC.output
@@ -0,0 +1,2 @@
+libs=
+dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output
new file mode 100644
index 0000000..df9ef98
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-CXX-Clang-17.0.1-MSVC.output
@@ -0,0 +1,2 @@
+libs=
+dirs=C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
diff --git a/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output
new file mode 100644
index 0000000..65f3494
--- /dev/null
+++ b/Tests/RunCMake/ParseImplicitLinkInfo/results/windows_x86_64-Fortran-LLVMFlang-17.0.1-MSVC.output
@@ -0,0 +1,2 @@
+libs=Fortran_main\.lib;FortranRuntime\.lib;FortranDecimal\.lib
+dirs=C:/DoesNotExist/LLVM/lib;C:/DoesNotExist/LLVM/lib/clang/17/lib/windows
diff --git a/Tests/RunCMake/RunCMake.cmake b/Tests/RunCMake/RunCMake.cmake
index bc4a330..fcf904e 100644
--- a/Tests/RunCMake/RunCMake.cmake
+++ b/Tests/RunCMake/RunCMake.cmake
@@ -24,6 +24,8 @@
   if(EXISTS ${top_src}/${test}-result.txt)
     file(READ ${top_src}/${test}-result.txt expect_result)
     string(REGEX REPLACE "\n+$" "" expect_result "${expect_result}")
+  elseif(DEFINED RunCMake_TEST_EXPECT_RESULT)
+    set(expect_result "${RunCMake_TEST_EXPECT_RESULT}")
   else()
     set(expect_result 0)
   endif()
@@ -53,6 +55,11 @@
       unset(expect_${o})
     endif()
   endforeach()
+  foreach(o IN ITEMS stdout stderr config)
+    if(DEFINED RunCMake_TEST_NOT_EXPECT_${o})
+      string(REGEX REPLACE "\n+$" "" not_expect_${o} "${RunCMake_TEST_NOT_EXPECT_${o}}")
+    endif()
+  endforeach()
   if (NOT expect_stderr)
     if (NOT RunCMake_DEFAULT_stderr)
       set(RunCMake_DEFAULT_stderr "^$")
@@ -145,7 +152,19 @@
     ${maybe_timeout}
     ${maybe_input_file}
     )]])
+  if(DEFINED ENV{PWD})
+    set(old_pwd "$ENV{PWD}")
+  else()
+    set(old_pwd)
+  endif()
+  # Emulate a shell using this directory.
+  set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}")
   cmake_language(EVAL CODE "${_code}")
+  if(DEFINED old_pwd)
+    set(ENV{PWD} "${old_pwd}")
+  else()
+    set(ENV{PWD})
+  endif()
   set(msg "")
   if(NOT "${actual_result}" MATCHES "${expect_result}")
     string(APPEND msg "Result is [${actual_result}], not [${expect_result}].\n")
@@ -182,6 +201,8 @@
     "|ic(p?c|l): remark #10441: The Intel\\(R\\) C\\+\\+ Compiler Classic \\(ICC\\) is deprecated"
 
     "|[^\n]*install_name_tool: warning: changes being made to the file will invalidate the code signature in:"
+    "|[^\n]*(createItemModels|_NSMainThread|Please file a bug at)"
+    "|[^\n]*xcodebuild[^\n]*DVTAssertions: Warning"
     "|[^\n]*xcodebuild[^\n]*DVTCoreDeviceEnabledState: DVTCoreDeviceEnabledState_Disabled set via user default"
     "|[^\n]*xcodebuild[^\n]*DVTPlugInManager"
     "|[^\n]*xcodebuild[^\n]*DVTSDK: Warning: SDK path collision for path"
@@ -216,6 +237,11 @@
         string(APPEND msg "${o} does not match that expected.\n")
       endif()
     endif()
+    if(DEFINED not_expect_${o})
+      if("${actual_${o}}" MATCHES "${not_expect_${o}}")
+        string(APPEND msg "${o} matches that not expected.\n")
+      endif()
+    endif()
   endforeach()
   unset(RunCMake_TEST_FAILED)
   if(RunCMake-check-file AND EXISTS ${top_src}/${RunCMake-check-file})
diff --git a/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake b/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake
index 58a111a..12f004b 100644
--- a/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake
+++ b/Tests/RunCMake/SymlinkTrees/RunCMakeTest.cmake
@@ -29,8 +29,6 @@
 
   # Test running in binary directory.
   set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
-  # Emulate a shell using this directory.
-  set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}")
 
   # Pass absolute path to the source tree, plain.
   set(RunCMake_TEST_VARIANT_DESCRIPTION " $abs/${name}/${src}")
@@ -50,8 +48,6 @@
 
   # Test running in source directory.
   set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}")
-  # Emulate a shell using this directory.
-  set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}")
 
   # Pass absolute path to the binary tree with -B.
   set(RunCMake_TEST_VARIANT_DESCRIPTION " -B $abs/${name}/${bin}")
@@ -63,8 +59,6 @@
 
   # Test running in another directory.
   set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${RunCMake_BINARY_DIR}/${name}")
-  # Emulate a shell using this directory.
-  set(ENV{PWD} "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}")
 
   # Pass absolute paths to the source and binary trees.
   set(RunCMake_TEST_VARIANT_DESCRIPTION " -S $abs/${name}/${src} -B $abs/${name}/${bin}")
diff --git a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
index 0d8e4c9..c2187ae 100644
--- a/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
+++ b/Tests/RunCMake/TargetPolicies/PolicyList-stderr.txt
@@ -37,6 +37,8 @@
    \* CMP0119
    \* CMP0131
    \* CMP0142
+   \* CMP0154
+   \* CMP0155
 
 Call Stack \(most recent call first\):
   CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/UseSWIG/RunCMakeTest.cmake b/Tests/RunCMake/UseSWIG/RunCMakeTest.cmake
index c7a118f..537f67d 100644
--- a/Tests/RunCMake/UseSWIG/RunCMakeTest.cmake
+++ b/Tests/RunCMake/UseSWIG/RunCMakeTest.cmake
@@ -6,7 +6,7 @@
 
 run_cmake(CMP0086-WARN)
 
-if (CMake_TEST_FindPython)
+if (CMake_TEST_FindPython2 OR CMake_TEST_FindPython3)
 
   macro(run_cmake_target test subtest target)
     set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt
new file mode 100644
index 0000000..53f603e
--- /dev/null
+++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-Direct-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0120-OLD-Direct\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0120 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt
new file mode 100644
index 0000000..fea708f
--- /dev/null
+++ b/Tests/RunCMake/WriteCompilerDetectionHeader/CMP0120-OLD-stderr.txt
@@ -0,0 +1,10 @@
+^CMake Deprecation Warning at CMP0120-OLD\.cmake:[0-9]+ \(cmake_policy\):
+  The OLD behavior for policy CMP0120 will be removed from a future version
+  of CMake\.
+
+  The cmake-policies\(7\) manual explains that the OLD behaviors of all
+  policies are deprecated and that a policy should be set to OLD only under
+  specific short-term circumstances\.  Projects should be ported to the NEW
+  behavior and not rely on setting a policy to OLD\.
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/XcFramework/CMakeLists.txt b/Tests/RunCMake/XcFramework/CMakeLists.txt
new file mode 100644
index 0000000..54a4d62
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.26)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/XcFramework/RunCMakeTest.cmake b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake
new file mode 100644
index 0000000..9a13892
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/RunCMakeTest.cmake
@@ -0,0 +1,121 @@
+include(RunCMake)
+
+function(create_library type platform system_name archs sysroot)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-${type}-${platform}-build)
+  run_cmake_with_options(create-${type}-${platform} -DCMAKE_SYSTEM_NAME=${system_name} -DCMAKE_OSX_ARCHITECTURES=${archs} -DCMAKE_OSX_SYSROOT=${sysroot} -DCMAKE_INSTALL_PREFIX=${RunCMake_TEST_BINARY_DIR}/install)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(create-${type}-${platform}-build ${CMAKE_COMMAND} --build . --config Release)
+  run_cmake_command(create-${type}-${platform}-install ${CMAKE_COMMAND} --install . --config Release)
+endfunction()
+
+function(create_libraries type)
+  create_library(${type} macos Darwin "${macos_archs_2}" macosx)
+  create_library(${type} ios iOS "arm64" iphoneos)
+  create_library(${type} tvos tvOS "arm64" appletvos)
+  create_library(${type} watchos watchOS "armv7k\\\\;arm64_32" watchos)
+  #FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+  #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+  #  create_library(${type} visionos visionOS "arm64" xros)
+  #endif()
+  create_library(${type} ios-simulator iOS "${macos_archs_2}" iphonesimulator)
+  create_library(${type} tvos-simulator tvOS "${macos_archs_2}" appletvsimulator)
+  create_library(${type} watchos-simulator watchOS "${watch_sim_archs_2}" watchsimulator)
+  #FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+  #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+  #  create_library(${type} visionos-simulator visionOS "${macos_archs_2}" xrsimulator)
+  #endif()
+endfunction()
+
+function(create_xcframework name type platforms)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-xcframework-${name}-build)
+  if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+    # 'xcodebuild -create-xcframework' fails on symlinked paths.
+    file(REAL_PATH "${RunCMake_SOURCE_DIR}" src_dir)
+    file(REAL_PATH "${RunCMake_BINARY_DIR}" bld_dir)
+  else()
+    set(src_dir "${RunCMake_SOURCE_DIR}")
+    set(bld_dir "${RunCMake_BINARY_DIR}")
+  endif()
+  set(args)
+  foreach(platform IN LISTS platforms)
+    set(lib_dir "${bld_dir}/create-${type}-${platform}-build/install/lib")
+    if(type STREQUAL "framework")
+      list(APPEND args -framework ${lib_dir}/mylib.framework)
+    else()
+      list(APPEND args -library ${lib_dir}/libmylib.a -headers ${src_dir}/mylib/include)
+    endif()
+  endforeach()
+  run_cmake_command(create-xcframework-${name} xcodebuild -create-xcframework ${args} -output ${RunCMake_TEST_BINARY_DIR}/mylib.xcframework)
+endfunction()
+
+function(create_executable name xcfname system_name archs sysroot)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-executable-${name}-build)
+  run_cmake_with_options(create-executable-${name} -DCMAKE_SYSTEM_NAME=${system_name} -DCMAKE_OSX_ARCHITECTURES=${archs} -DCMAKE_OSX_SYSROOT=${sysroot} -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-${xcfname}-build/mylib.xcframework)
+
+  set(RunCMake_TEST_NO_CLEAN 1)
+  run_cmake_command(create-executable-${name}-build ${CMAKE_COMMAND} --build . --config Release)
+endfunction()
+
+function(create_executables name type)
+  create_executable(${name}-macos ${type} Darwin "${macos_archs_2}" macosx)
+  create_executable(${name}-ios ${type} iOS "arm64" iphoneos)
+  create_executable(${name}-tvos ${type} tvOS "arm64" appletvos)
+  create_executable(${name}-watchos ${type} watchOS "armv7k\\\\;arm64_32" watchos)
+  #FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+  #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+  #  create_executable(${name}-visionos ${type} visionOS "arm64" xros)
+  #endif()
+  create_executable(${name}-ios-simulator ${type} iOS "${macos_archs_2}" iphonesimulator)
+  create_executable(${name}-tvos-simulator ${type} tvOS "${macos_archs_2}" appletvsimulator)
+  create_executable(${name}-watchos-simulator ${type} watchOS "${watch_sim_archs_2}" watchsimulator)
+  #FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+  #if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+  #  create_executable(${name}-visionos-simulator ${type} visionOS "${macos_archs_2}" xrsimulator)
+  #endif()
+endfunction()
+
+set(xcframework_platforms macos ios tvos watchos ios-simulator tvos-simulator watchos-simulator)
+#FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+#if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 15)
+#  list(APPEND xcframework_platforms visionos visionos-simulator)
+#endif()
+if(CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12)
+  set(macos_archs_1 "x86_64\\;arm64")
+  set(macos_archs_2 "x86_64\\\\;arm64")
+  set(watch_sim_archs_2 "x86_64")
+else()
+  set(macos_archs_1 "x86_64")
+  set(macos_archs_2 "x86_64")
+  set(watch_sim_archs_2 "i386")
+endif()
+
+create_libraries(library)
+create_libraries(framework)
+create_xcframework(library library "${xcframework_platforms}")
+create_xcframework(framework framework "${xcframework_platforms}")
+create_xcframework(incomplete framework "tvos;watchos")
+create_executables(library library)
+create_executables(framework framework)
+run_cmake_with_options(create-executable-incomplete -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+create_executables(target-library library)
+create_executables(target-framework framework)
+run_cmake_with_options(create-executable-target-incomplete -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+if(RunCMake_GENERATOR STREQUAL "Xcode" AND CMake_TEST_XCODE_VERSION VERSION_GREATER_EQUAL 12)
+  create_executables(library-link-phase library)
+  create_executables(framework-link-phase framework)
+  run_cmake_with_options(create-executable-incomplete-link-phase -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+  create_executables(target-library-link-phase library)
+  create_executables(target-framework-link-phase framework)
+  run_cmake_with_options(create-executable-target-incomplete-link-phase -DCMAKE_SYSTEM_NAME=Darwin "-DCMAKE_OSX_ARCHITECTURES=${macos_archs_1}" -DMYLIB_LIBRARY=${RunCMake_BINARY_DIR}/create-xcframework-incomplete-build/mylib.xcframework)
+endif()
+
+# Ensure that .xcframework is found before .framework
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/create-xcframework-framework-build)
+set(RunCMake_TEST_NO_CLEAN 1)
+run_cmake_command(copy-framework ${CMAKE_COMMAND} -E copy_directory ${RunCMake_BINARY_DIR}/create-framework-macos-build/install/lib/mylib.framework ${RunCMake_TEST_BINARY_DIR}/mylib.framework)
+unset(RunCMake_TEST_NO_CLEAN)
+unset(RunCMake_TEST_BINARY_DIR)
+
+run_cmake(find-library)
+run_cmake_command(find-library-script ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/find-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-ios-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-ios.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-ios.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-ios.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-macos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-macos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-link-phase-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-macos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-macos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-tvos-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-tvos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-visionos-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-visionos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-watchos-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-framework-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-framework-watchos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-framework-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-result.txt
diff --git a/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-stderr.txt b/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-stderr.txt
new file mode 100644
index 0000000..5b43e19
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase-stderr.txt
@@ -0,0 +1,11 @@
+CMake Error at create-executable\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-link-phase\.cmake:[0-9]+ \(include\)
+  create-executable-incomplete-link-phase\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase.cmake b/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-incomplete-link-phase.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/XcFramework/create-executable-incomplete-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/XcFramework/create-executable-incomplete-result.txt
diff --git a/Tests/RunCMake/XcFramework/create-executable-incomplete-stderr.txt b/Tests/RunCMake/XcFramework/create-executable-incomplete-stderr.txt
new file mode 100644
index 0000000..66b7d62
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-incomplete-stderr.txt
@@ -0,0 +1,10 @@
+CMake Error at create-executable\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-incomplete\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/XcFramework/create-executable-incomplete.cmake b/Tests/RunCMake/XcFramework/create-executable-incomplete.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-incomplete.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-ios-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-library-ios.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-ios.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-ios.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-macos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-macos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos-simulator.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos.cmake
new file mode 100644
index 0000000..2888c85
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-link-phase-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-macos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-macos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-tvos-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-tvos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-visionos-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-visionos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-library-watchos-simulator.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-library-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-library-watchos.cmake
new file mode 100644
index 0000000..760d9d4
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-library-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-link-phase.cmake b/Tests/RunCMake/XcFramework/create-executable-link-phase.cmake
new file mode 100644
index 0000000..9884781
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-link-phase.cmake
@@ -0,0 +1,2 @@
+include(create-executable.cmake)
+set_property(TARGET myexe PROPERTY XCODE_LINK_BUILD_PHASE_MODE "KNOWN_LOCATION")
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-ios-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-ios.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-ios.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-ios.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-macos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-macos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-link-phase-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-macos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-macos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-tvos-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-tvos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-visionos-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-visionos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-watchos-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-framework-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-framework-watchos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-framework-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-result.txt
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-stderr.txt b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-stderr.txt
new file mode 100644
index 0000000..1308933
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase-stderr.txt
@@ -0,0 +1,11 @@
+CMake Error at create-executable-target\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-target-link-phase\.cmake:[0-9]+ \(include\)
+  create-executable-target-incomplete-link-phase\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase.cmake b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-link-phase.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/XcFramework/create-executable-target-incomplete-result.txt
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-incomplete-stderr.txt b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-stderr.txt
new file mode 100644
index 0000000..716b17d
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-incomplete-stderr.txt
@@ -0,0 +1,10 @@
+CMake Error at create-executable-target\.cmake:[0-9]+ \(target_link_libraries\):
+  Unable to find suitable library in:
+
+    [^
+]*/Tests/RunCMake/XcFramework/create-xcframework-incomplete-build/mylib\.xcframework/Info\.plist
+
+  for system name "Darwin"
+Call Stack \(most recent call first\):
+  create-executable-target-incomplete\.cmake:[0-9]+ \(include\)
+  CMakeLists\.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-incomplete.cmake b/Tests/RunCMake/XcFramework/create-executable-target-incomplete.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-incomplete.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-ios-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-ios.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-ios.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-ios.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-macos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-macos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos-simulator.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos.cmake
new file mode 100644
index 0000000..dfeccb9
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-link-phase-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable-target-link-phase.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-macos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-macos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-macos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-tvos-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-tvos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-tvos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-tvos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-visionos-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-visionos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-visionos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-visionos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-watchos-simulator.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-library-watchos.cmake b/Tests/RunCMake/XcFramework/create-executable-target-library-watchos.cmake
new file mode 100644
index 0000000..b2e3469
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-library-watchos.cmake
@@ -0,0 +1 @@
+include(create-executable-target.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-executable-target-link-phase.cmake b/Tests/RunCMake/XcFramework/create-executable-target-link-phase.cmake
new file mode 100644
index 0000000..9c0b0d5
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target-link-phase.cmake
@@ -0,0 +1,2 @@
+include(create-executable-target.cmake)
+set_property(TARGET myexe PROPERTY XCODE_LINK_BUILD_PHASE_MODE "KNOWN_LOCATION")
diff --git a/Tests/RunCMake/XcFramework/create-executable-target.cmake b/Tests/RunCMake/XcFramework/create-executable-target.cmake
new file mode 100644
index 0000000..0cc356c
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable-target.cmake
@@ -0,0 +1,21 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_library(mylib IMPORTED STATIC)
+set_property(TARGET mylib PROPERTY IMPORTED_LOCATION ${MYLIB_LIBRARY})
+
+add_executable(myexe myexe/myexe.c)
+target_link_libraries(myexe PRIVATE mylib)
diff --git a/Tests/RunCMake/XcFramework/create-executable.cmake b/Tests/RunCMake/XcFramework/create-executable.cmake
new file mode 100644
index 0000000..6706b9f
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-executable.cmake
@@ -0,0 +1,18 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_executable(myexe myexe/myexe.c)
+target_link_libraries(myexe PRIVATE ${MYLIB_LIBRARY})
diff --git a/Tests/RunCMake/XcFramework/create-framework-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-framework-ios-simulator.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-ios.cmake b/Tests/RunCMake/XcFramework/create-framework-ios.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-ios.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-macos.cmake b/Tests/RunCMake/XcFramework/create-framework-macos.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-macos.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-framework-tvos-simulator.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-tvos.cmake b/Tests/RunCMake/XcFramework/create-framework-tvos.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-tvos.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-framework-visionos-simulator.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-visionos.cmake b/Tests/RunCMake/XcFramework/create-framework-visionos.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-visionos.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-framework-watchos-simulator.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework-watchos.cmake b/Tests/RunCMake/XcFramework/create-framework-watchos.cmake
new file mode 100644
index 0000000..8b7df9b
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework-watchos.cmake
@@ -0,0 +1 @@
+include(create-framework.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-framework.cmake b/Tests/RunCMake/XcFramework/create-framework.cmake
new file mode 100644
index 0000000..f4406e6
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-framework.cmake
@@ -0,0 +1,3 @@
+set(CMAKE_FRAMEWORK ON)
+include(create-library-common.cmake)
+install(FILES mylib/include/mylib/mylib.h DESTINATION lib/mylib.framework/Headers)
diff --git a/Tests/RunCMake/XcFramework/create-library-common.cmake b/Tests/RunCMake/XcFramework/create-library-common.cmake
new file mode 100644
index 0000000..958660d
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-common.cmake
@@ -0,0 +1,12 @@
+enable_language(C)
+
+if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+endif()
+
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
+endif()
+
+add_library(mylib STATIC mylib/mylib.c)
+install(TARGETS mylib DESTINATION lib)
diff --git a/Tests/RunCMake/XcFramework/create-library-ios-simulator.cmake b/Tests/RunCMake/XcFramework/create-library-ios-simulator.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-ios-simulator.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-ios.cmake b/Tests/RunCMake/XcFramework/create-library-ios.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-ios.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-macos.cmake b/Tests/RunCMake/XcFramework/create-library-macos.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-macos.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-tvos-simulator.cmake b/Tests/RunCMake/XcFramework/create-library-tvos-simulator.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-tvos-simulator.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-tvos.cmake b/Tests/RunCMake/XcFramework/create-library-tvos.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-tvos.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-visionos-simulator.cmake b/Tests/RunCMake/XcFramework/create-library-visionos-simulator.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-visionos-simulator.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-visionos.cmake b/Tests/RunCMake/XcFramework/create-library-visionos.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-visionos.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-watchos-simulator.cmake b/Tests/RunCMake/XcFramework/create-library-watchos-simulator.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-watchos-simulator.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library-watchos.cmake b/Tests/RunCMake/XcFramework/create-library-watchos.cmake
new file mode 100644
index 0000000..a9f5dee
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library-watchos.cmake
@@ -0,0 +1 @@
+include(create-library.cmake)
diff --git a/Tests/RunCMake/XcFramework/create-library.cmake b/Tests/RunCMake/XcFramework/create-library.cmake
new file mode 100644
index 0000000..f2a5249
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/create-library.cmake
@@ -0,0 +1 @@
+include(create-library-common.cmake)
diff --git a/Tests/RunCMake/XcFramework/find-library.cmake b/Tests/RunCMake/XcFramework/find-library.cmake
new file mode 100644
index 0000000..1bc7672
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/find-library.cmake
@@ -0,0 +1,6 @@
+find_library(MYLIB_XCFRAMEWORK mylib NO_DEFAULT_PATH PATHS "${CMAKE_BINARY_DIR}/../create-xcframework-framework-build")
+get_filename_component(bin_parent "${CMAKE_BINARY_DIR}" PATH)
+set(expected_path "${bin_parent}/create-xcframework-framework-build/mylib.xcframework")
+if(NOT MYLIB_XCFRAMEWORK STREQUAL expected_path)
+  message(FATAL_ERROR "Expected value of MYLIB_XCFRAMEWORK:\n  ${expected_path}\nActual value:\n  ${MYLIB_XCFRAMEWORK}")
+endif()
diff --git a/Tests/RunCMake/XcFramework/myexe/myexe.c b/Tests/RunCMake/XcFramework/myexe/myexe.c
new file mode 100644
index 0000000..d04efbd
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/myexe/myexe.c
@@ -0,0 +1,7 @@
+#include <mylib/mylib.h>
+
+int main(void)
+{
+  mylib();
+  return 0;
+}
diff --git a/Tests/RunCMake/XcFramework/mylib/include/mylib/mylib.h b/Tests/RunCMake/XcFramework/mylib/include/mylib/mylib.h
new file mode 100644
index 0000000..1de07aa
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/mylib/include/mylib/mylib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+extern void mylib(void);
diff --git a/Tests/RunCMake/XcFramework/mylib/mylib.c b/Tests/RunCMake/XcFramework/mylib/mylib.c
new file mode 100644
index 0000000..4489684
--- /dev/null
+++ b/Tests/RunCMake/XcFramework/mylib/mylib.c
@@ -0,0 +1,3 @@
+void mylib(void)
+{
+}
diff --git a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c
index 5e0f40f..c00fce7 100644
--- a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c
+++ b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.c
@@ -5,6 +5,10 @@
 #  if __MAC_OS_X_VERSION_MIN_REQUIRED != __MAC_10_11
 #    error macOS deployment version mismatch
 #  endif
+#elif TARGET_OS_XR
+#  if __XR_OS_VERSION_MIN_REQUIRED != __XROS_1_0
+#    error visionOS deployment version mismatch
+#  endif
 #elif TARGET_OS_IOS
 #  if __IPHONE_OS_VERSION_MIN_REQUIRED != __IPHONE_9_1
 #    error iOS deployment version mismatch
diff --git a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.cmake b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.cmake
index 234ceef..80e3877 100644
--- a/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.cmake
+++ b/Tests/RunCMake/XcodeProject-Device/DeploymentTarget.cmake
@@ -7,6 +7,12 @@
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
   set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
+elseif(CMAKE_SYSTEM_NAME STREQUAL "visionOS")
+  set(CMAKE_OSX_DEPLOYMENT_TARGET "1.0")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
+  set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
+  set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "YES")
 elseif(CMAKE_SYSTEM_NAME STREQUAL "watchOS")
   set(CMAKE_OSX_DEPLOYMENT_TARGET "2.0")
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
diff --git a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake
index e2ed045..e4dbb90 100644
--- a/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject-Device/RunCMakeTest.cmake
@@ -93,6 +93,26 @@
   unset(RunCMake_TEST_OPTIONS)
 endif()
 
+#FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+#if(NOT XCODE_VERSION VERSION_LESS 15)
+#  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XcodeBundlesVisionOS-build)
+#  set(RunCMake_TEST_NO_CLEAN 1)
+#  set(RunCMake_TEST_OPTIONS
+#    "-DCMAKE_SYSTEM_NAME=visionOS"
+#    "-DCMAKE_INSTALL_PREFIX:PATH=${RunCMake_TEST_BINARY_DIR}/_install")
+#
+#  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+#  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+#
+#  run_cmake(XcodeBundles)
+#  run_cmake_command(XcodeBundles-build-visionOS ${CMAKE_COMMAND} --build .)
+#  run_cmake_command(XcodeBundles-install-visionOS ${CMAKE_COMMAND} --build . --target install)
+#
+#  unset(RunCMake_TEST_BINARY_DIR)
+#  unset(RunCMake_TEST_NO_CLEAN)
+#  unset(RunCMake_TEST_OPTIONS)
+#endif()
+
 if(NOT XCODE_VERSION VERSION_LESS 7)
   set(RunCMake_TEST_OPTIONS "-DCMAKE_TOOLCHAIN_FILE=${RunCMake_SOURCE_DIR}/osx.cmake")
   run_cmake(XcodeTbdStub)
@@ -242,6 +262,11 @@
   deployment_target_test(tvOS appletvsimulator)
   deployment_target_test(watchOS watchos)
   deployment_target_test(watchOS watchsimulator)
+  #FIXME(#25266): Xcode 15.0 does not have visionOS.  Improve this condition.
+  #if(XCODE_VERSION VERSION_GREATER_EQUAL 15)
+  #  deployment_target_test(visionOS xros)
+  #  deployment_target_test(visionOS xrsimulator)
+  #endif()
 endif()
 
 if(XCODE_VERSION VERSION_GREATER_EQUAL 8)
@@ -288,9 +313,11 @@
   endfunction()
 
   if(XCODE_VERSION VERSION_GREATER_EQUAL 12)
-    xctest_add_bundle_test(Darwin macosx "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns")
     xctest_add_bundle_test(Darwin macosx "12" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns")
-    xctest_add_bundle_test(iOS iphonesimulator "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns")
+    if(XCODE_VERSION VERSION_LESS 14)
+      xctest_add_bundle_test(Darwin macosx "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns")
+      xctest_add_bundle_test(iOS iphonesimulator "1" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>/PlugIns")
+    endif()
     if (XCODE_VERSION VERSION_LESS 12.5)
       xctest_add_bundle_test(iOS iphonesimulator "12" "$<TARGET_BUNDLE_CONTENT_DIR:TestedApp>")
     else()
diff --git a/Tests/RunCMake/XcodeProject-Device/XcodeBundles.cmake b/Tests/RunCMake/XcodeProject-Device/XcodeBundles.cmake
index a9fafd2..376a7fc 100644
--- a/Tests/RunCMake/XcodeProject-Device/XcodeBundles.cmake
+++ b/Tests/RunCMake/XcodeProject-Device/XcodeBundles.cmake
@@ -9,7 +9,7 @@
   set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE "NO")
 endif()
 
-if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
+if(CMAKE_SYSTEM_NAME STREQUAL "tvOS" OR CMAKE_SYSTEM_NAME STREQUAL "visionOS" OR CMAKE_SYSTEM_NAME STREQUAL "watchOS")
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO")
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO")
   set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "")
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns-macOS-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns-macOS-check.cmake
index 576be11..66db44e 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns-macOS-check.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns-macOS-check.cmake
@@ -1,4 +1,3 @@
 include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake)
 
-findAttribute(${test} "RemoveHeadersOnCopy" TRUE)
 findAttribute(${test} "CodeSignOnCopy" FALSE)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns.cmake
index 1bd1bd0..f5c9364 100644
--- a/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedPlugIns.cmake
@@ -1,4 +1,4 @@
-add_executable(plug_in MACOS_BUNDLE Empty.txt)
+add_executable(plug_in MACOSX_BUNDLE Empty.txt)
 set_target_properties(plug_in PROPERTIES
   LINKER_LANGUAGE CXX
   XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedResources-iOS-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-iOS-check.cmake
new file mode 100644
index 0000000..75aaa91
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-iOS-check.cmake
@@ -0,0 +1,3 @@
+include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake)
+
+findAttribute(${test} "Embed Resources" TRUE)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedResources-iOS.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-iOS.cmake
new file mode 100644
index 0000000..54f9fc8
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-iOS.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/EmbedResources.cmake)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedResources-macOS-check.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-macOS-check.cmake
new file mode 100644
index 0000000..75aaa91
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-macOS-check.cmake
@@ -0,0 +1,3 @@
+include(${CMAKE_CURRENT_LIST_DIR}/findAttribute.cmake)
+
+findAttribute(${test} "Embed Resources" TRUE)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedResources-macOS.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-macOS.cmake
new file mode 100644
index 0000000..54f9fc8
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedResources-macOS.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/EmbedResources.cmake)
diff --git a/Tests/RunCMake/XcodeProject-Embed/EmbedResources.cmake b/Tests/RunCMake/XcodeProject-Embed/EmbedResources.cmake
new file mode 100644
index 0000000..0638037
--- /dev/null
+++ b/Tests/RunCMake/XcodeProject-Embed/EmbedResources.cmake
@@ -0,0 +1,18 @@
+add_executable(app MACOSX_BUNDLE main.m)
+
+set(EMBED_RESOURCES_FOLDER ${CMAKE_BINARY_DIR}/runtime/shaders)
+
+# ensure embed resources folder exists
+if (NOT (IS_DIRECTORY ${EMBED_RESOURCES_FOLDER}))
+    file(MAKE_DIRECTORY ${EMBED_RESOURCES_FOLDER})
+endif()
+
+set_target_properties(app PROPERTIES
+    XCODE_EMBED_RESOURCES_PATH ${EMBED_RESOURCES_FOLDER}
+)
+
+set_target_properties(app PROPERTIES
+  XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO"
+  XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY ""
+  MACOSX_BUNDLE_GUI_IDENTIFIER "com.example.app"
+)
diff --git a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
index a7bccee..3798ddc 100644
--- a/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
+++ b/Tests/RunCMake/XcodeProject-Embed/RunCMakeTest.cmake
@@ -83,6 +83,25 @@
   )
 endfunction()
 
+function(TestEmbedCommon what platform)
+  set(testName Embed${what}-${platform})
+  if(NOT platform STREQUAL "macOS")
+    set(RunCMake_TEST_OPTIONS -DCMAKE_SYSTEM_NAME=${platform})
+  endif()
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${testName}-build)
+
+  file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
+  file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
+
+  run_cmake(${testName})
+  run_cmake_command(${testName}-build
+    ${CMAKE_COMMAND} --build ${RunCMake_TEST_BINARY_DIR}
+                     --config Debug
+                     --target app
+  )
+endfunction()
+
 # Isolate device tests from host architecture selection.
 unset(ENV{CMAKE_OSX_ARCHITECTURES})
 
@@ -100,4 +119,7 @@
   # defaults, which is to remove headers on copy, but not code sign.
   TestAppExtension(macOS)
   TestAppExtension(iOS)
+  TestEmbedCommon(Resources macOS)
+  TestEmbedCommon(Resources iOS)
+  TestEmbedCommon(PlugIns macOS)
 endif()
diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty-stdout.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty-stdout.txt
new file mode 100644
index 0000000..d5a0ca8
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty-stdout.txt
@@ -0,0 +1 @@
+MSYSTEM_PREFIX=''
diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty.cmake b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty.cmake
new file mode 100644
index 0000000..ac36c8d
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Empty.cmake
@@ -0,0 +1,3 @@
+unset(ENV{MSYSTEM})
+cmake_host_system_information(RESULT result QUERY MSYSTEM_PREFIX)
+message(STATUS "MSYSTEM_PREFIX='${result}'")
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-result.txt
diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-stderr.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-stderr.txt
new file mode 100644
index 0000000..89c4e9b
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing-stderr.txt
@@ -0,0 +1,3 @@
+^CMake Error at [^
+]*/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake:[0-9]+ \(cmake_host_system_information\):
+  cmake_host_system_information does not recognize <key> MSYSTEM_PREFIX$
diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake
new file mode 100644
index 0000000..dc1def3
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-Missing.cmake
@@ -0,0 +1,2 @@
+unset(ENV{MSYSTEM})
+cmake_host_system_information(RESULT result QUERY MSYSTEM_PREFIX)
diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-stdout.txt
new file mode 100644
index 0000000..f6e2549
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX-stdout.txt
@@ -0,0 +1,2 @@
+-- MSYSTEM_PREFIX='[^
+]+'
diff --git a/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX.cmake
new file mode 100644
index 0000000..d1c996b
--- /dev/null
+++ b/Tests/RunCMake/cmake_host_system_information/MSYSTEM_PREFIX.cmake
@@ -0,0 +1,7 @@
+cmake_host_system_information(RESULT result QUERY MSYSTEM_PREFIX)
+message(STATUS "MSYSTEM_PREFIX='${result}'")
+if(CMake_TEST_MSYSTEM_PREFIX)
+  if(NOT "${result}" STREQUAL "${CMake_TEST_MSYSTEM_PREFIX}")
+    message(FATAL_ERROR "Actual result:\n ${result}\nis not expected result:\n ${CMake_TEST_MSYSTEM_PREFIX}")
+  endif()
+endif()
diff --git a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
index 9122470..0b3576d 100644
--- a/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
+++ b/Tests/RunCMake/cmake_host_system_information/RunCMakeTest.cmake
@@ -22,6 +22,17 @@
   run_cmake(VsMSBuildMissing)
 endif()
 
+if(CMAKE_HOST_WIN32)
+  run_cmake_script(MSYSTEM_PREFIX-Empty)
+  if("$ENV{MSYSTEM}" MATCHES "(MSYS|MINGW32|MINGW64|UCRT64)")
+    set(RunCMake_TEST_VARIANT_DESCRIPTION "-$ENV{MSYSTEM}")
+    run_cmake_script(MSYSTEM_PREFIX -DCMake_TEST_MSYSTEM_PREFIX=${CMake_TEST_MSYSTEM_PREFIX})
+    unset(RunCMake_TEST_VARIANT_DESCRIPTION)
+  endif()
+else()
+  run_cmake_script(MSYSTEM_PREFIX-Missing)
+endif()
+
 # WINDOWS_REGISTRY tests
 run_cmake(Registry_NoArgs)
 run_cmake(Registry_BadQuery1)
diff --git a/Tests/CTestTestFailure/badCode.cxx b/Tests/RunCMake/ctest_build/BuildFailure.cxx
similarity index 100%
rename from Tests/CTestTestFailure/badCode.cxx
rename to Tests/RunCMake/ctest_build/BuildFailure.cxx
diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
index 12525f2..af56ead 100644
--- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake
@@ -13,18 +13,18 @@
 run_ctest_build(BuildQuiet QUIET)
 run_ctest_build(ParallelLevel PARALLEL_LEVEL 1)
 
-function(run_BuildFailure)
-  set(CASE_CMAKELISTS_SUFFIX_CODE [[
-add_custom_target(BuildFailure ALL COMMAND command-does-not-exist)
-]])
+block()
+  set(LANG CXX)
+  configure_file("${RunCMake_SOURCE_DIR}/BuildFailure.cxx" "${RunCMake_BINARY_DIR}/BuildFailure/BuildFailure.cxx" COPYONLY)
+  set(CASE_CMAKELISTS_SUFFIX_CODE [=[
+    add_executable(BuildFailure BuildFailure.cxx)
+  ]=])
   set(CASE_CMAKELISTS_PREFIX_CODE [[
 if(NOT CTEST_USE_LAUNCHERS)
   message(FATAL_ERROR "CTEST_USE_LAUNCHERS not set")
 endif()
 ]])
-  set(CASE_TEST_PREFIX_CODE [[
-cmake_policy(SET CMP0061 NEW)
-]])
+  set(CASE_TEST_PREFIX_CODE "")
   set(CASE_TEST_SUFFIX_CODE [[
 if (ctest_build_return_value)
   message("ctest_build returned non-zero")
@@ -35,13 +35,16 @@
   run_ctest(BuildFailure)
 
   if (RunCMake_GENERATOR MATCHES "Makefiles")
+    set(LANG NONE)
     set(CASE_TEST_PREFIX_CODE [[
 cmake_policy(VERSION 3.2)
 ]])
+    set(CASE_CMAKELISTS_SUFFIX_CODE [[
+add_custom_target(BuildFailure ALL COMMAND command-does-not-exist)
+]])
     run_ctest(BuildFailure-CMP0061-OLD)
   endif()
-endfunction()
-run_BuildFailure()
+endblock()
 
 function(run_BuildChangeId)
   set(CASE_TEST_PREFIX_CODE [[
diff --git a/Tests/RunCMake/ctest_test/NotRun-result.txt b/Tests/RunCMake/ctest_test/NotRun-result.txt
new file mode 100644
index 0000000..b57e2de
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/NotRun-result.txt
@@ -0,0 +1 @@
+(-1|255)
diff --git a/Tests/RunCMake/ctest_test/NotRun-stderr.txt b/Tests/RunCMake/ctest_test/NotRun-stderr.txt
new file mode 100644
index 0000000..85907f3
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/NotRun-stderr.txt
@@ -0,0 +1,2 @@
+.*Unable to find executable[^
+]*does_not_exist
diff --git a/Tests/RunCMake/ctest_test/NotRun-stdout.txt b/Tests/RunCMake/ctest_test/NotRun-stdout.txt
new file mode 100644
index 0000000..8d60833
--- /dev/null
+++ b/Tests/RunCMake/ctest_test/NotRun-stdout.txt
@@ -0,0 +1,7 @@
+.*Could not find executable[^
+]*does_not_exist
+.*
+50% tests passed, 1 tests failed out of 2
+.*
+The following tests FAILED:
+.*testNotRun \(Not Run\)
diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
index 242a059..d2f3da3 100644
--- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
+++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake
@@ -53,6 +53,13 @@
 unset(CASE_CTEST_TEST_LOAD)
 unset(RunCTest_VERBOSE_FLAG)
 
+block()
+  set(CASE_CMAKELISTS_SUFFIX_CODE [[
+    add_test(NAME testNotRun COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist)
+  ]])
+  run_ctest_test(NotRun)
+endblock()
+
 function(run_TestChangeId)
   set(CASE_TEST_PREFIX_CODE [[
     set(CTEST_CHANGE_ID "<>1")
@@ -131,8 +138,7 @@
 
 # test repeat and not run tests interact correctly
 set(CASE_CMAKELISTS_SUFFIX_CODE [[
-add_test(NAME testNotRun
-  COMMAND ${CMAKE_COMMAND}/doesnt_exist)
+  add_test(NAME testNotRun COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist)
   set_property(TEST testNotRun PROPERTY TIMEOUT 5)
   ]])
 run_TestRepeat(NotRun RETURN_VALUE:1 REPEAT UNTIL_PASS:3)
diff --git a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt
index a69932d..85907f3 100644
--- a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt
+++ b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stderr.txt
@@ -1 +1,2 @@
-.*Unable to find executable.*
+.*Unable to find executable[^
+]*does_not_exist
diff --git a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt
index 72c98bc..8d60833 100644
--- a/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt
+++ b/Tests/RunCMake/ctest_test/TestRepeatNotRun-stdout.txt
@@ -1,5 +1,7 @@
+.*Could not find executable[^
+]*does_not_exist
 .*
 50% tests passed, 1 tests failed out of 2
 .*
 The following tests FAILED:
-.*testNotRun.*Not Run.*
+.*testNotRun \(Not Run\)
diff --git a/Tests/RunCMake/detect_jobserver.c b/Tests/RunCMake/detect_jobserver.c
new file mode 100644
index 0000000..8cbfe2e
--- /dev/null
+++ b/Tests/RunCMake/detect_jobserver.c
@@ -0,0 +1,179 @@
+#ifndef _CRT_SECURE_NO_WARNINGS
+#  define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1928
+#  pragma warning(disable : 5105) /* macro expansion warning in windows.h */
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_MESSAGE_LENGTH 1023
+#define USAGE "Usage: %s <output_file>\n"
+
+// Extracts the jobserver details from the MAKEFLAGS environment variable.
+//
+// Returns a pointer to either a string of the form "R,W" where R and W are fds
+// or "fifo:PATH".
+//
+// Returns NULL if MAKEFLAGS is not set or does not contain recognized
+// jobserver flags.
+char* jobserver_auth(char* message)
+{
+  const char* jobserver_flags[3] = { "--jobserver-auth=", "--jobserver-fds=",
+                                     "-J" };
+  char* start = NULL;
+  char* end;
+  char* result;
+  size_t len;
+  int i;
+
+  char* makeflags = getenv("MAKEFLAGS");
+  if (makeflags == NULL) {
+    strncpy(message, "MAKEFLAGS not set", MAX_MESSAGE_LENGTH);
+    return NULL;
+  }
+
+  fprintf(stdout, "MAKEFLAGS: %s\n", makeflags);
+
+  for (i = 0; i < 3; i++) {
+    start = strstr(makeflags, jobserver_flags[i]);
+    if (start != NULL) {
+      start += strlen(jobserver_flags[i]);
+      break;
+    }
+  }
+
+  if (start == NULL) {
+    strncpy(message, "No jobserver flags found", MAX_MESSAGE_LENGTH);
+    return NULL;
+  }
+
+  // Skip leading white space
+  while (*start == ' ' || *start == '\t') {
+    start++;
+  }
+
+  end = strchr(start, ' ');
+  if (end == NULL) {
+    end = start + strlen(start);
+  }
+  len = (size_t)(end - start);
+  result = (char*)malloc(len + 1);
+  strncpy(result, start, len);
+  result[len] = '\0';
+
+  return result;
+}
+
+#if defined(_WIN32)
+#  include <windows.h>
+
+int windows_semaphore(const char* semaphore, char* message)
+{
+  // Open the semaphore
+  HANDLE hSemaphore = OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, FALSE, semaphore);
+
+  if (hSemaphore == NULL) {
+#  if defined(_MSC_VER) && _MSC_VER < 1900
+    sprintf(message, "Error opening semaphore: %s (%ld)\n", semaphore,
+            GetLastError());
+#  else
+    snprintf(message, MAX_MESSAGE_LENGTH,
+             "Error opening semaphore: %s (%ld)\n", semaphore, GetLastError());
+#  endif
+    return 1;
+  }
+
+  strncpy(message, "Success", MAX_MESSAGE_LENGTH);
+  return 0;
+}
+#else
+#  include <errno.h>
+#  include <fcntl.h>
+
+int test_fd(int read_fd, int write_fd, char* message)
+{
+  // Detect if the file descriptors are valid
+  int read_good = fcntl(read_fd, F_GETFD) != -1;
+  int read_error = errno;
+
+  int write_good = fcntl(write_fd, F_GETFD) != -1;
+  int write_error = errno;
+
+  if (!read_good || !write_good) {
+    snprintf(message, MAX_MESSAGE_LENGTH,
+             "Error opening file descriptors: %d (%s), %d (%s)\n", read_fd,
+             strerror(read_error), write_fd, strerror(write_error));
+    return 1;
+  }
+
+  snprintf(message, MAX_MESSAGE_LENGTH, "Success\n");
+  return 0;
+}
+
+int posix(const char* jobserver, char* message)
+{
+  int read_fd;
+  int write_fd;
+  const char* path;
+
+  // First try to parse as "R,W" file descriptors
+  if (sscanf(jobserver, "%d,%d", &read_fd, &write_fd) == 2) {
+    return test_fd(read_fd, write_fd, message);
+  }
+
+  // Then try to parse as "fifo:PATH"
+  if (strncmp(jobserver, "fifo:", 5) == 0) {
+    path = jobserver + 5;
+    read_fd = open(path, O_RDONLY);
+    write_fd = open(path, O_WRONLY);
+    return test_fd(read_fd, write_fd, message);
+  }
+
+  // We don't understand the format
+  snprintf(message, MAX_MESSAGE_LENGTH, "Unrecognized jobserver format: %s\n",
+           jobserver);
+  return 1;
+}
+#endif
+
+// Takes 1 argument: an outfile to write results to.
+int main(int argc, char** argv)
+{
+  char message[MAX_MESSAGE_LENGTH + 1];
+  char* output_file;
+  FILE* fp;
+  char* jobserver;
+  int result;
+
+  if (argc != 2) {
+    fprintf(stderr, USAGE, argv[0]);
+    return 2;
+  }
+
+  output_file = argv[1];
+  fp = fopen(output_file, "w");
+  if (fp == NULL) {
+    fprintf(stderr, "Error opening output file: %s\n", output_file);
+    return 2;
+  }
+
+  jobserver = jobserver_auth(message);
+  if (jobserver == NULL) {
+    fprintf(stderr, "%s\n", message);
+    return 1;
+  }
+
+#if defined(_WIN32)
+  result = windows_semaphore(jobserver, message);
+#else
+  result = posix(jobserver, message);
+#endif
+  free(jobserver);
+  message[MAX_MESSAGE_LENGTH] = '\0';
+
+  return result;
+}
diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
index c2f9144..1f89829 100644
--- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake
+++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake
@@ -34,6 +34,7 @@
 run_cmake_command(LastCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandError.cmake)
 run_cmake_command(LastCommandTimeout ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandTimeout.cmake)
 run_cmake_command(LastCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandGood.cmake)
+run_cmake_command(Stdin ${CMAKE_COMMAND} -DPRINT_STDIN_EXE=${PRINT_STDIN_EXE} -P ${RunCMake_SOURCE_DIR}/Stdin.cmake)
 
 if(UNIX AND Python_EXECUTABLE)
   run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPython_EXECUTABLE=${Python_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake)
diff --git a/Tests/RunCMake/execute_process/Stdin-stdin.txt b/Tests/RunCMake/execute_process/Stdin-stdin.txt
new file mode 100644
index 0000000..cd08755
--- /dev/null
+++ b/Tests/RunCMake/execute_process/Stdin-stdin.txt
@@ -0,0 +1 @@
+Hello world!
diff --git a/Tests/RunCMake/execute_process/Stdin-stdout.txt b/Tests/RunCMake/execute_process/Stdin-stdout.txt
new file mode 100644
index 0000000..04bd136
--- /dev/null
+++ b/Tests/RunCMake/execute_process/Stdin-stdout.txt
@@ -0,0 +1 @@
+^Hello world!$
diff --git a/Tests/RunCMake/execute_process/Stdin.cmake b/Tests/RunCMake/execute_process/Stdin.cmake
new file mode 100644
index 0000000..e8a2098
--- /dev/null
+++ b/Tests/RunCMake/execute_process/Stdin.cmake
@@ -0,0 +1 @@
+execute_process(COMMAND ${PRINT_STDIN_EXE})
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
index 10b7b82..61ab542 100644
--- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-all-check.cmake
@@ -1,4 +1,4 @@
-if(CMAKE_C_COMPILER_ID STREQUAL "Borland")
+if(CMAKE_C_COMPILER_ID MATCHES "^(Borland|OrangeC)$")
   # Borland upper-cases dll names referenced in import libraries.
   set(conflict_dll [[CONFLICT\.DLL]])
   set(unresolved_dll [[UNRESOLVED\.DLL]])
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-OrangeC.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-OrangeC.txt
new file mode 100644
index 0000000..607e4b8
--- /dev/null
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-all-stderr-OrangeC.txt
@@ -0,0 +1,7 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Multiple conflicting paths found for PATH\.DLL:
+
+    [^
+]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test1/path\.dll
+    [^
+]*/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-conflict-build/root-all/lib/test2/path\.dll$
diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-OrangeC.txt b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-OrangeC.txt
new file mode 100644
index 0000000..fea1083
--- /dev/null
+++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/windows-unresolved-all-stderr-OrangeC.txt
@@ -0,0 +1,4 @@
+^CMake Error at cmake_install\.cmake:[0-9]+ \(file\):
+  file Could not resolve runtime dependencies:
+
+    UNRESOLVED\.DLL$
diff --git a/Tests/RunCMake/file/REAL_PATH.cmake b/Tests/RunCMake/file/REAL_PATH.cmake
index 9c5d4ea..0485655 100644
--- a/Tests/RunCMake/file/REAL_PATH.cmake
+++ b/Tests/RunCMake/file/REAL_PATH.cmake
@@ -1,18 +1,57 @@
 
 if (NOT WIN32 OR CYGWIN)
+  file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}" real_binary_dir)
+
   file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
   file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/test.sym")
   file(CREATE_LINK  "test.txt" "${CMAKE_CURRENT_BINARY_DIR}/test.sym" SYMBOLIC)
 
   file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/test.sym" real_path)
-  if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
-    message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"")
+  if (NOT real_path STREQUAL "${real_binary_dir}/test.txt")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/test.txt\"")
   endif()
 
   file(REAL_PATH "test.sym" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
-  if (NOT real_path STREQUAL "${CMAKE_CURRENT_BINARY_DIR}/test.txt")
-    message(SEND_ERROR "real path is \"${real_path}\", should be \"${CMAKE_CURRENT_BINARY_DIR}/test.txt\"")
+  if (NOT real_path STREQUAL "${real_binary_dir}/test.txt")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/test.txt\"")
   endif()
+
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/")
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/")
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin/")
+  file(CREATE_LINK  "${CMAKE_CURRENT_BINARY_DIR}/dir/nested/bin" "${CMAKE_CURRENT_BINARY_DIR}/dir/bin" SYMBOLIC)
+
+  cmake_policy(SET CMP0152 NEW)
+  file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../" real_path)
+  if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested\"")
+  endif()
+
+  file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../bin" real_path)
+  if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested/bin")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested/bin\"")
+  endif()
+
+  file(REAL_PATH "dir/bin/../bin" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
+  if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested/bin")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested/bin\"")
+  endif()
+
+  file(REAL_PATH "../bin" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/" )
+  if (NOT real_path STREQUAL "${real_binary_dir}/dir/nested/bin")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested/bin\"")
+  endif()
+
+  cmake_policy(SET CMP0152 OLD)
+  file(REAL_PATH "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/../" real_path)
+  if (NOT real_path STREQUAL "${real_binary_dir}/dir")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir/nested\"")
+  endif()
+  file(REAL_PATH "../" real_path BASE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/dir/bin/")
+  if (NOT real_path STREQUAL "${real_binary_dir}/dir")
+    message(SEND_ERROR "real path is \"${real_path}\", should be \"${real_binary_dir}/dir\"")
+  endif()
+
 endif()
 
 
@@ -24,12 +63,14 @@
 else()
   set(HOME_DIR "$ENV{HOME}")
 endif()
+file(REAL_PATH "${HOME_DIR}" HOME_DIR)
 
 file(REAL_PATH "~" real_path EXPAND_TILDE)
 if (NOT real_path STREQUAL "${HOME_DIR}")
   message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}\"")
 endif()
 
+file(TOUCH "${HOME_DIR}/test.txt")
 file(REAL_PATH "~/test.txt" real_path EXPAND_TILDE)
 if (NOT real_path STREQUAL "${HOME_DIR}/test.txt")
   message(SEND_ERROR "real path is \"${real_path}\", should be \"${HOME_DIR}/test.txt\"")
diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt
deleted file mode 100644
index 6912bdf..0000000
--- a/Tests/RunCMake/find_file/FromPATHEnv-stdout-cygwin.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-msys.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-msys.txt
deleted file mode 100644
index 6912bdf..0000000
--- a/Tests/RunCMake/find_file/FromPATHEnv-stdout-msys.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt
deleted file mode 100644
index 6912bdf..0000000
--- a/Tests/RunCMake/find_file/FromPATHEnv-stdout-windows.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-cygwin.txt b/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-cygwin.txt
deleted file mode 100644
index 6912bdf..0000000
--- a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-cygwin.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-msys.txt b/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-msys.txt
deleted file mode 100644
index 6912bdf..0000000
--- a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-msys.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-windows.txt b/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-windows.txt
deleted file mode 100644
index 6912bdf..0000000
--- a/Tests/RunCMake/find_file/FromPATHEnvDebugVar-stdout-windows.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
--- PrefixInPATH_File='PrefixInPATH_File-NOTFOUND'
diff --git a/Tests/RunCMake/find_file/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_file/MSYSTEM_PREFIX-stdout.txt
new file mode 100644
index 0000000..ab9b9d5
--- /dev/null
+++ b/Tests/RunCMake/find_file/MSYSTEM_PREFIX-stdout.txt
@@ -0,0 +1 @@
+-- MSYSTEM_PREFIX_H='[^']*/Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h'
diff --git a/Tests/RunCMake/find_file/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_file/MSYSTEM_PREFIX.cmake
new file mode 100644
index 0000000..08e4127
--- /dev/null
+++ b/Tests/RunCMake/find_file/MSYSTEM_PREFIX.cmake
@@ -0,0 +1,5 @@
+set(ENV{MSYSTEM} "FAKEMSYS")
+set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX")
+enable_language(C)
+find_file(MSYSTEM_PREFIX_H NAMES msystem_prefix.h)
+message(STATUS "MSYSTEM_PREFIX_H='${MSYSTEM_PREFIX_H}'")
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/find_file/MSYSTEM_PREFIX/include/msystem_prefix.h
diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt
deleted file mode 100644
index d73bc1d..0000000
--- a/Tests/RunCMake/find_file/PrefixInPATH-stdout-cygwin.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-msys.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-msys.txt
deleted file mode 100644
index d73bc1d..0000000
--- a/Tests/RunCMake/find_file/PrefixInPATH-stdout-msys.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
diff --git a/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt
deleted file mode 100644
index d73bc1d..0000000
--- a/Tests/RunCMake/find_file/PrefixInPATH-stdout-windows.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_file/include/PrefixInPATH.h'
diff --git a/Tests/RunCMake/find_file/RunCMakeTest.cmake b/Tests/RunCMake/find_file/RunCMakeTest.cmake
index 296bb71..2d59e38 100644
--- a/Tests/RunCMake/find_file/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_file/RunCMakeTest.cmake
@@ -12,6 +12,10 @@
 run_cmake(VALIDATOR-specify-macro)
 run_cmake(VALIDATOR)
 
+if(CMAKE_HOST_WIN32 AND MINGW)
+  run_cmake(MSYSTEM_PREFIX)
+endif()
+
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=PrefixInPATH_File)
 
 if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt
deleted file mode 100644
index 01e2720..0000000
--- a/Tests/RunCMake/find_library/FromPATHEnv-stdout-cygwin.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-msys.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-msys.txt
deleted file mode 100644
index 01e2720..0000000
--- a/Tests/RunCMake/find_library/FromPATHEnv-stdout-msys.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt
deleted file mode 100644
index 01e2720..0000000
--- a/Tests/RunCMake/find_library/FromPATHEnv-stdout-windows.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnv-build/lib/libcreated.a'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-cygwin.txt b/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-cygwin.txt
deleted file mode 100644
index 48f36cc..0000000
--- a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-cygwin.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-msys.txt b/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-msys.txt
deleted file mode 100644
index 48f36cc..0000000
--- a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-msys.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-windows.txt b/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-windows.txt
deleted file mode 100644
index 48f36cc..0000000
--- a/Tests/RunCMake/find_library/FromPATHEnvDebugVar-stdout-windows.txt
+++ /dev/null
@@ -1,6 +0,0 @@
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a'
--- CREATED_LIBRARY='[^']*/Tests/RunCMake/find_library/FromPATHEnvDebugVar-build/lib/libcreated.a'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
--- CREATED_LIBRARY='CREATED_LIBRARY-NOTFOUND'
diff --git a/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt b/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt
index 185720b..b0bf460 100644
--- a/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt
+++ b/Tests/RunCMake/find_library/FromScriptMode-stderr-darwin.txt
@@ -1,4 +1,4 @@
 .*find_library considered the following locations.*
-.*\(lib\)library_no_exist\(\\.tbd\|\\.dylib\|\\.so\|\\.a\).*
+.*liblibrary_no_exist\(\\.tbd\|\\.dylib\|\\.so\|\\.a\).*
 .*The item was found at.*
 .*lib/libcreated.a.*
diff --git a/Tests/RunCMake/find_library/FromScriptMode-stderr.txt b/Tests/RunCMake/find_library/FromScriptMode-stderr.txt
index 046f680..6f25184 100644
--- a/Tests/RunCMake/find_library/FromScriptMode-stderr.txt
+++ b/Tests/RunCMake/find_library/FromScriptMode-stderr.txt
@@ -1,4 +1,4 @@
 .*find_library considered the following locations.*
-.*\(lib\)library_no_exist\(\\.so\|\\.a\).*
+.*liblibrary_no_exist\(\\.so\|\\.a\).*
 .*The item was found at.*
 .*lib/libcreated.a.*
diff --git a/Tests/RunCMake/find_library/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_library/MSYSTEM_PREFIX-stdout.txt
new file mode 100644
index 0000000..b19bcce
--- /dev/null
+++ b/Tests/RunCMake/find_library/MSYSTEM_PREFIX-stdout.txt
@@ -0,0 +1 @@
+-- MSYSTEM_PREFIX_LIB='[^']*/Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a'
diff --git a/Tests/RunCMake/find_library/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_library/MSYSTEM_PREFIX.cmake
new file mode 100644
index 0000000..ef4ce2f
--- /dev/null
+++ b/Tests/RunCMake/find_library/MSYSTEM_PREFIX.cmake
@@ -0,0 +1,5 @@
+set(ENV{MSYSTEM} "FAKEMSYS")
+set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX")
+enable_language(C)
+find_library(MSYSTEM_PREFIX_LIB NAMES msystem_prefix)
+message(STATUS "MSYSTEM_PREFIX_LIB='${MSYSTEM_PREFIX_LIB}'")
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/find_library/MSYSTEM_PREFIX/lib/libmsystem_prefix.a
diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt
deleted file mode 100644
index 1ab884c..0000000
--- a/Tests/RunCMake/find_library/PrefixInPATH-stdout-cygwin.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-msys.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-msys.txt
deleted file mode 100644
index 1ab884c..0000000
--- a/Tests/RunCMake/find_library/PrefixInPATH-stdout-msys.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
diff --git a/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt
deleted file mode 100644
index 1ab884c..0000000
--- a/Tests/RunCMake/find_library/PrefixInPATH-stdout-windows.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_LIBRARY='PrefixInPATH_LIBRARY-NOTFOUND'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
--- PrefixInPATH_LIBRARY='.*/Tests/RunCMake/find_library/lib/libPrefixInPATH.a'
diff --git a/Tests/RunCMake/find_library/RunCMakeTest.cmake b/Tests/RunCMake/find_library/RunCMakeTest.cmake
index 8b223b4..0bed252 100644
--- a/Tests/RunCMake/find_library/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_library/RunCMakeTest.cmake
@@ -20,6 +20,10 @@
 run_cmake(VALIDATOR-specify-macro)
 run_cmake(VALIDATOR)
 
+if(CMAKE_HOST_WIN32 AND MINGW)
+  run_cmake(MSYSTEM_PREFIX)
+endif()
+
 run_cmake_script(FromScriptMode "-DTEMP_DIR=${RunCMake_BINARY_DIR}/FromScriptMode-temp")
 
 run_cmake_with_options(FromPATHEnvDebugVar --debug-find-var=CREATED_LIBRARY)
diff --git a/Tests/RunCMake/find_package/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_package/MSYSTEM_PREFIX-stdout.txt
new file mode 100644
index 0000000..b1c8346
--- /dev/null
+++ b/Tests/RunCMake/find_package/MSYSTEM_PREFIX-stdout.txt
@@ -0,0 +1 @@
+-- MsysPfx_DIR='[^']*/Tests/RunCMake/find_package/MSYSTEM_PREFIX'
diff --git a/Tests/RunCMake/find_package/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_package/MSYSTEM_PREFIX.cmake
new file mode 100644
index 0000000..2826e53
--- /dev/null
+++ b/Tests/RunCMake/find_package/MSYSTEM_PREFIX.cmake
@@ -0,0 +1,5 @@
+set(ENV{MSYSTEM} "FAKEMSYS")
+set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX")
+enable_language(C)
+find_package(MsysPfx QUIET)
+message(STATUS "MsysPfx_DIR='${MsysPfx_DIR}'")
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake b/Tests/RunCMake/find_package/MSYSTEM_PREFIX/MsysPfxConfig.cmake
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-ON.cmake
copy to Tests/RunCMake/find_package/MSYSTEM_PREFIX/MsysPfxConfig.cmake
diff --git a/Tests/RunCMake/find_package/RunCMakeTest.cmake b/Tests/RunCMake/find_package/RunCMakeTest.cmake
index 006757a..a93b811 100644
--- a/Tests/RunCMake/find_package/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_package/RunCMakeTest.cmake
@@ -72,6 +72,10 @@
 run_cmake(REGISTRY_VIEW-wrong-view)
 run_cmake(REGISTRY_VIEW-propagated)
 
+if(CMAKE_HOST_WIN32 AND MINGW)
+  run_cmake(MSYSTEM_PREFIX)
+endif()
+
 if(CMAKE_HOST_WIN32)
   run_cmake(CMP0144-WARN-CaseInsensitive)
   run_cmake(CMP0144-OLD-CaseInsensitive)
diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt
deleted file mode 100644
index 8f3e7ca..0000000
--- a/Tests/RunCMake/find_path/FromPATHEnv-stdout-cygwin.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-msys.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-msys.txt
deleted file mode 100644
index 8f3e7ca..0000000
--- a/Tests/RunCMake/find_path/FromPATHEnv-stdout-msys.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt b/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt
deleted file mode 100644
index 8f3e7ca..0000000
--- a/Tests/RunCMake/find_path/FromPATHEnv-stdout-windows.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH-NOTFOUND'
diff --git a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-cygwin.txt b/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-cygwin.txt
deleted file mode 100644
index a502d78..0000000
--- a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-cygwin.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH=''
--- PATH_IN_ENV_PATH=''
--- PATH_IN_ENV_PATH=''
diff --git a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-msys.txt b/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-msys.txt
deleted file mode 100644
index a502d78..0000000
--- a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-msys.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH=''
--- PATH_IN_ENV_PATH=''
--- PATH_IN_ENV_PATH=''
diff --git a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-windows.txt b/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-windows.txt
deleted file mode 100644
index a502d78..0000000
--- a/Tests/RunCMake/find_path/FromPATHEnvDebugVar-stdout-windows.txt
+++ /dev/null
@@ -1,9 +0,0 @@
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='.*/Tests/RunCMake/find_path/include'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH='PATH_IN_ENV_PATH_A-NOTFOUND'
--- PATH_IN_ENV_PATH=''
--- PATH_IN_ENV_PATH=''
--- PATH_IN_ENV_PATH=''
diff --git a/Tests/RunCMake/find_path/MSYSTEM_PREFIX-stdout.txt b/Tests/RunCMake/find_path/MSYSTEM_PREFIX-stdout.txt
new file mode 100644
index 0000000..87ee490
--- /dev/null
+++ b/Tests/RunCMake/find_path/MSYSTEM_PREFIX-stdout.txt
@@ -0,0 +1 @@
+-- MSYSTEM_PREFIX_INCLUDE_DIR='[^']*/Tests/RunCMake/find_path/MSYSTEM_PREFIX/include'
diff --git a/Tests/RunCMake/find_path/MSYSTEM_PREFIX.cmake b/Tests/RunCMake/find_path/MSYSTEM_PREFIX.cmake
new file mode 100644
index 0000000..d17a52f
--- /dev/null
+++ b/Tests/RunCMake/find_path/MSYSTEM_PREFIX.cmake
@@ -0,0 +1,5 @@
+set(ENV{MSYSTEM} "FAKEMSYS")
+set(ENV{MSYSTEM_PREFIX} "${CMAKE_CURRENT_LIST_DIR}/MSYSTEM_PREFIX")
+enable_language(C)
+find_path(MSYSTEM_PREFIX_INCLUDE_DIR NAMES msystem_prefix.h)
+message(STATUS "MSYSTEM_PREFIX_INCLUDE_DIR='${MSYSTEM_PREFIX_INCLUDE_DIR}'")
diff --git a/Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake b/Tests/RunCMake/find_path/MSYSTEM_PREFIX/include/msystem_prefix.h
similarity index 100%
copy from Tests/RunCMake/CommandLine/DeprecateVS11-WARN-OFF.cmake
copy to Tests/RunCMake/find_path/MSYSTEM_PREFIX/include/msystem_prefix.h
diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt
deleted file mode 100644
index bb2ceb7..0000000
--- a/Tests/RunCMake/find_path/PrefixInPATH-stdout-cygwin.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-msys.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-msys.txt
deleted file mode 100644
index bb2ceb7..0000000
--- a/Tests/RunCMake/find_path/PrefixInPATH-stdout-msys.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
diff --git a/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt b/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt
deleted file mode 100644
index bb2ceb7..0000000
--- a/Tests/RunCMake/find_path/PrefixInPATH-stdout-windows.txt
+++ /dev/null
@@ -1,4 +0,0 @@
--- PrefixInPATH_INCLUDE_DIR='PrefixInPATH_INCLUDE_DIR-NOTFOUND'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
--- PrefixInPATH_INCLUDE_DIR='.*/Tests/RunCMake/find_path/include'
diff --git a/Tests/RunCMake/find_path/RunCMakeTest.cmake b/Tests/RunCMake/find_path/RunCMakeTest.cmake
index 9c76f2e..2ed4d7d 100644
--- a/Tests/RunCMake/find_path/RunCMakeTest.cmake
+++ b/Tests/RunCMake/find_path/RunCMakeTest.cmake
@@ -12,6 +12,10 @@
 run_cmake(VALIDATOR-specify-macro)
 run_cmake(VALIDATOR)
 
+if(CMAKE_HOST_WIN32 AND MINGW)
+  run_cmake(MSYSTEM_PREFIX)
+endif()
+
 if(APPLE)
   run_cmake(FrameworksWithSubdirs)
 endif()
diff --git a/Tests/RunCMake/get_property/test_properties-stderr.txt b/Tests/RunCMake/get_property/test_properties-stderr.txt
index a447280..9f5a10f 100644
--- a/Tests/RunCMake/get_property/test_properties-stderr.txt
+++ b/Tests/RunCMake/get_property/test_properties-stderr.txt
@@ -1,6 +1,12 @@
-^get_test_property: --><--
+^get_test_property: -->value<--
+get_property: -->value<--
+get_test_property: --><--
 get_property: --><--
 get_test_property: -->value<--
 get_property: -->value<--
 get_test_property: -->NOTFOUND<--
-get_property: --><--$
+get_property: --><--
+get_test_property: -->anotherValue<--
+get_property: -->anotherValue<--
+get_test_property: -->anotherValue<--
+get_property: -->anotherValue<--$
diff --git a/Tests/RunCMake/get_property/test_properties.cmake b/Tests/RunCMake/get_property/test_properties.cmake
index 1d0295c..f1cbca4 100644
--- a/Tests/RunCMake/get_property/test_properties.cmake
+++ b/Tests/RunCMake/get_property/test_properties.cmake
@@ -1,7 +1,11 @@
-function (check_test_property test prop)
-  get_test_property("${test}" "${prop}" gtp_val)
+function (check_test_property test prop dir)
+  set(dir_args)
+  if(dir)
+    set(dir_args DIRECTORY ${dir})
+  endif()
+  get_test_property("${test}" "${prop}" ${dir_args} gtp_val)
   get_property(gp_val
-    TEST "${test}"
+    TEST "${test}" ${dir_args}
     PROPERTY "${prop}")
 
   message("get_test_property: -->${gtp_val}<--")
@@ -11,7 +15,10 @@
 include(CTest)
 add_test(NAME test COMMAND "${CMAKE_COMMAND}" --help)
 set_tests_properties(test PROPERTIES empty "" custom value)
+add_subdirectory(test_properties)
 
-check_test_property(test empty)
-check_test_property(test custom)
-check_test_property(test noexist)
+check_test_property(test empty "")
+check_test_property(test custom "")
+check_test_property(test noexist "")
+check_test_property(test custom test_properties)
+check_test_property(test custom ${CMAKE_BINARY_DIR}/test_properties)
diff --git a/Tests/RunCMake/get_property/test_properties/CMakeLists.txt b/Tests/RunCMake/get_property/test_properties/CMakeLists.txt
new file mode 100644
index 0000000..ee90344
--- /dev/null
+++ b/Tests/RunCMake/get_property/test_properties/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_test(NAME test COMMAND "${CMAKE_COMMAND}" --help)
+set_tests_properties(test PROPERTIES empty "" custom anotherValue)
+
+check_test_property(test custom ..)
diff --git a/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake b/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake
index d099469..97b9b5d 100644
--- a/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake
+++ b/Tests/RunCMake/install/TARGETS-Defaults-Cache-all-check.cmake
@@ -1,7 +1,7 @@
 if(WIN32)
   set(_check_files
     [[lib3]]
-    [[lib3/(lib)?lib3\.(dll\.a|lib)]]
+    [[lib3/(lib)?lib3\.(dll\.a|lib|l)]]
     [[lib4]]
     [[lib4/(lib)?lib4\.dll]]
     [[mybin]]
@@ -10,8 +10,8 @@
     [[myinclude]]
     [[myinclude/obj3\.h]]
     [[mylib]]
-    [[mylib/(lib)?lib1\.(dll\.a|lib)]]
-    [[mylib/(lib)?lib2\.(a|lib)]]
+    [[mylib/(lib)?lib1\.(dll\.a|lib|l)]]
+    [[mylib/(lib)?lib2\.(a|lib|l)]]
     )
 elseif(MSYS)
   set(_check_files
diff --git a/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake b/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake
index 6e13b84..f357c1e 100644
--- a/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake
+++ b/Tests/RunCMake/install/TARGETS-Defaults-all-check.cmake
@@ -8,10 +8,10 @@
     [[include/obj2\.h]]
     [[include/obj3\.h]]
     [[lib]]
-    [[lib/(lib)?lib1\.(dll\.a|lib)]]
-    [[lib/(lib)?lib2\.(a|lib)]]
+    [[lib/(lib)?lib1\.(dll\.a|lib|l)]]
+    [[lib/(lib)?lib2\.(a|lib|l)]]
     [[lib3]]
-    [[lib3/(lib)?lib3\.(dll\.a|lib)]]
+    [[lib3/(lib)?lib3\.(dll\.a|lib|l)]]
     [[lib4]]
     [[lib4/(lib)?lib4\.dll]]
     )
diff --git a/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake b/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake
index 91d5ef0..919ccf1 100644
--- a/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake
+++ b/Tests/RunCMake/install/TARGETS-OUTPUT_NAME-all-check.cmake
@@ -1,7 +1,7 @@
 if(WIN32)
   set(test123 [[bin/test1out\.exe;bin/test2deb\.exe;bin/test3exc\.exe]])
   set(libtest45 [[bin/libtest4\.dll;bin/libtest4\.dll\.a;bin/libtest5ar\.a]])
-  set(test45 [[bin/test4\.dll;bin/test4\.lib;bin/test5ar\.lib]])
+  set(test45 [[bin/test4\.dll;bin/test4\.(lib|l);bin/test5ar\.(lib|l)]])
 
   check_installed("^bin;(${libtest45};${test123})|(${test123};${test45})\$")
 elseif(MSYS)
diff --git a/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake b/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake
index 6245839..8137e92 100644
--- a/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake
+++ b/Tests/RunCMake/install/TARGETS-Parts-all-check.cmake
@@ -1 +1 @@
-check_installed([[^include;include/obj1\.h;lib;lib/(mylib\.lib|(lib|cyg)mylib\.a)$]])
+check_installed([[^include;include/obj1\.h;lib;lib/(mylib\.(lib|l)|(lib|cyg)mylib\.a)$]])
diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake
index dafc2a4..38635c3 100644
--- a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake
+++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake
@@ -1 +1 @@
-check_installed([[^static;static/(liblib\.a|lib\.lib)$]])
+check_installed([[^static;static/(liblib\.a|lib\.(lib|l))$]])
diff --git a/Tests/RunCMake/property_init/Always.cmake b/Tests/RunCMake/property_init/Always.cmake
new file mode 100644
index 0000000..db23563
--- /dev/null
+++ b/Tests/RunCMake/property_init/Always.cmake
@@ -0,0 +1,15 @@
+set(properties
+  # property                      expected  alias
+  # Test a property which should never be initialized.
+  "notset"                        "<UNSET>" "<SAME>"
+
+  # Build graph properties
+  "VERIFY_INTERFACE_HEADER_SETS"  "TRUE"    "<SAME>"
+
+  # Metadata
+  "FOLDER"                        "folder"  "<SAME>"
+  )
+
+prepare_target_types(always ${all_target_types})
+
+run_property_tests(always properties)
diff --git a/Tests/RunCMake/property_init/CMakeLists.txt b/Tests/RunCMake/property_init/CMakeLists.txt
new file mode 100644
index 0000000..51883af
--- /dev/null
+++ b/Tests/RunCMake/property_init/CMakeLists.txt
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.12)
+project(${RunCMake_TEST} C)
+
+set(main_sources main.c)
+set(library_sources library.c)
+
+include(util.cmake)
+
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/property_init/CompileSources.cmake b/Tests/RunCMake/property_init/CompileSources.cmake
new file mode 100644
index 0000000..e8c5554
--- /dev/null
+++ b/Tests/RunCMake/property_init/CompileSources.cmake
@@ -0,0 +1,274 @@
+set(dir "${CMAKE_CURRENT_BINARY_DIR}")
+
+set(properties
+  # property                                expected            alias
+  # Compilation properties
+  "COMPILE_WARNING_AS_ERROR"                "ON"                "<SAME>"
+  "INTERPROCEDURAL_OPTIMIZATION"            "OFF"               "<SAME>"
+  "NO_SYSTEM_FROM_IMPORTED"                 "ON"                "<SAME>"
+  "VISIBILITY_INLINES_HIDDEN"               "ON"                "<SAME>"
+  ## Features
+  ### PCH
+  "DISABLE_PRECOMPILE_HEADERS"              "ON"                "<SAME>"
+  "PCH_WARN_INVALID"                        "OFF"               "<SAME>"
+  "PCH_INSTANTIATE_TEMPLATES"               "OFF"               "<SAME>"
+  ## Platforms
+  ### Android
+  "ANDROID_API"                             "9"                 "<SAME>"
+  "ANDROID_API_MIN"                         "9"                 "<SAME>"
+  "ANDROID_ARCH"                            "arm64-v8a"         "<SAME>"
+  "ANDROID_ASSETS_DIRECTORIES"              "${dir}"            "<SAME>"
+  "ANDROID_JAVA_SOURCE_DIR"                 "${dir}"            "<SAME>"
+  "ANDROID_STL_TYPE"                        "system"            "<SAME>"
+  ### macOS
+  "OSX_ARCHITECTURES"                       "arm64"             "<SAME>"
+  ### Windows
+  "MSVC_DEBUG_INFORMATION_FORMAT"           "Embedded"          "<SAME>"
+  "MSVC_RUNTIME_LIBRARY"                    "MultiThreaded"     "<SAME>"
+  "VS_JUST_MY_CODE_DEBUGGING"               "ON"                "<SAME>"
+  ### OpenWatcom
+  "WATCOM_RUNTIME_LIBRARY"                  "MultiThreaded"     "<SAME>"
+  ## Language
+  ### CUDA
+  "CUDA_SEPARABLE_COMPILATION"              "ON"                "<SAME>"
+  "CUDA_ARCHITECTURES"                      "naive"             "<SAME>"
+  ### Fortran
+  "Fortran_FORMAT"                          "FREE"              "<SAME>"
+  "Fortran_MODULE_DIRECTORY"                "${dir}"            "<SAME>"
+  "Fortran_COMPILER_LAUNCHER"               "ccache"            "<SAME>"
+  "Fortran_PREPROCESS"                      "ON"                "<SAME>"
+  "Fortran_VISIBILITY_PRESET"               "hidden"            "<SAME>"
+  ### HIP
+  "HIP_ARCHITECTURES"                       "gfx801"            "<SAME>"
+  ### ISPC
+  "ISPC_COMPILER_LAUNCHER"                  "ccache"            "<SAME>"
+  "ISPC_HEADER_DIRECTORY"                   "${dir}"            "<SAME>"
+  "ISPC_HEADER_SUFFIX"                      "_i.h"              "<SAME>"
+  "ISPC_INSTRUCTION_SETS"                   "avx2-i32x4"        "<SAME>"
+  ### Swift
+  "Swift_LANGUAGE_VERSION"                  "2.3"               "<SAME>"
+  "Swift_MODULE_DIRECTORY"                  "${dir}"            "<SAME>"
+  ### moc
+  "AUTOMOC"                                 "OFF"               "<SAME>"
+  "AUTOMOC_COMPILER_PREDEFINES"             "OFF"               "<SAME>"
+  "AUTOMOC_MACRO_NAMES"                     "MOC_CLASS"         "<SAME>"
+  "AUTOMOC_MOC_OPTIONS"                     "-v"                "<SAME>"
+  "AUTOMOC_PATH_PREFIX"                     "moc_"              "<SAME>"
+  "AUTOMOC_EXECUTABLE"                      "automoc"           "<SAME>"
+  ### uic
+  "AUTOUIC"                                 "OFF"               "<SAME>"
+  "AUTOUIC_OPTIONS"                         "-v"                "<SAME>"
+  "AUTOUIC_SEARCH_PATHS"                    "${dir}"            "<SAME>"
+  "AUTOUIC_EXECUTABLE"                      "autouic"           "<SAME>"
+  ### rcc
+  "AUTORCC"                                 "OFF"               "<SAME>"
+  "AUTORCC_OPTIONS"                         "-v"                "<SAME>"
+  "AUTORCC_EXECUTABLE"                      "autorcc"           "<SAME>"
+
+  # Linking properties
+  "LINK_SEARCH_START_STATIC"                "-Bstatic"          "<SAME>"
+  "LINK_SEARCH_END_STATIC"                  "-Bdynamic"         "<SAME>"
+  ## Dependent library lookup
+  "MACOSX_RPATH"                            "@loader_path/"     "<SAME>"
+  ### Build
+  "BUILD_RPATH"                             "../lib"            "<SAME>"
+  "BUILD_RPATH_USE_ORIGIN"                  "ON"                "<SAME>"
+  "SKIP_BUILD_RPATH"                        "ON"                "<SAME>"
+  "BUILD_WITH_INSTALL_RPATH"                "ON"                "<SAME>"
+  "BUILD_WITH_INSTALL_NAME_DIR"             "@rpath/"           "<SAME>"
+  ### Install
+  "INSTALL_NAME_DIR"                        "@rpath/"           "<SAME>"
+  "INSTALL_REMOVE_ENVIRONMENT_RPATH"        "ON"                "<SAME>"
+  "INSTALL_RPATH"                           "@rpath/"           "<SAME>"
+  "INSTALL_RPATH_USE_LINK_PATH"             "ON"                "<SAME>"
+  ## Platforms
+  ### Android
+  "ANDROID_JAR_DIRECTORIES"                 "${dir}"            "<SAME>"
+  "ANDROID_JAR_DEPENDENCIES"                "${dir}/foo.jar"    "<SAME>"
+  "ANDROID_NATIVE_LIB_DIRECTORIES"          "${dir}"            "<SAME>"
+  "ANDROID_NATIVE_LIB_DEPENDENCIES"         "${dir}/native.a"   "<SAME>"
+  "ANDROID_PROGUARD"                        "ON"                "<SAME>"
+  "ANDROID_PROGUARD_CONFIG_PATH"            "proguard.props"    "<SAME>"
+  "ANDROID_SECURE_PROPS_PATH"               "secure.props"      "<SAME>"
+  ### iOS
+  "IOS_INSTALL_COMBINED"                    "ON"                "<SAME>"
+  ### Windows
+  "GNUtoMS"                                 "ON"                "<SAME>"
+  "WIN32_EXECUTABLE"                        "OFF"               "<SAME>"
+  ## Languages
+  ### C
+  "C_LINKER_LAUNCHER"                       "ccache"            "<SAME>"
+  ### C++
+  "CXX_LINKER_LAUNCHER"                     "ccache"            "<SAME>"
+  ### CUDA
+  "CUDA_RESOLVE_DEVICE_SYMBOLS"             "ON"                "<SAME>"
+  "CUDA_RUNTIME_LIBRARY"                    "Static"            "<SAME>"
+  ### HIP
+  "HIP_RUNTIME_LIBRARY"                     "SHARED"            "<SAME>"
+  ### Objective C
+  "OBJC_LINKER_LAUNCHER"                    "ccache"            "<SAME>"
+  ### Objective C++
+  "OBJCXX_LINKER_LAUNCHER"                  "ccache"            "<SAME>"
+
+  # Static analysis
+  ## C
+  "C_CLANG_TIDY"                            "clang-tidy"        "<SAME>"
+  "C_CLANG_TIDY_EXPORT_FIXES_DIR"           "${dir}"            "<SAME>"
+  "C_CPPLINT"                               "cpplint"           "<SAME>"
+  "C_CPPCHECK"                              "cppcheck"          "<SAME>"
+  "C_INCLUDE_WHAT_YOU_USE"                  "iwyu"              "<SAME>"
+  ## C++
+  "CXX_CLANG_TIDY"                          "clang-tidy"        "<SAME>"
+  "CXX_CLANG_TIDY_EXPORT_FIXES_DIR"         "${dir}"            "<SAME>"
+  "CXX_CPPLINT"                             "cpplint"           "<SAME>"
+  "CXX_CPPCHECK"                            "cppcheck"          "<SAME>"
+  "CXX_INCLUDE_WHAT_YOU_USE"                "iwyu"              "<SAME>"
+  ## Objective C
+  "OBJC_CLANG_TIDY"                         "clang-tidy"        "<SAME>"
+  "OBJC_CLANG_TIDY_EXPORT_FIXES_DIR"        "${dir}"            "<SAME>"
+  ## Objective C++
+  "OBJCXX_CLANG_TIDY"                       "clang-tidy"        "<SAME>"
+  "OBJCXX_CLANG_TIDY_EXPORT_FIXES_DIR"      "${dir}"            "<SAME>"
+  ## Linking
+  "LINK_WHAT_YOU_USE"                       "lwyu"              "<SAME>"
+
+  # Build graph properties
+  "LINK_DEPENDS_NO_SHARED"                  "OFF"               "<SAME>"
+  "UNITY_BUILD"                             "OFF"               "<SAME>"
+  "UNITY_BUILD_UNIQUE_ID"                   "unity"             "<SAME>"
+  "UNITY_BUILD_BATCH_SIZE"                  "10"                "<SAME>"
+  "UNITY_BUILD_MODE"                        "GROUP"             "<SAME>"
+  "OPTIMIZE_DEPENDENCIES"                   "ON"                "<SAME>"
+  ## Android
+  "ANDROID_ANT_ADDITIONAL_OPTIONS"          "-v"                "<SAME>"
+  "ANDROID_PROCESS_MAX"                     "2"                 "<SAME>"
+  "ANDROID_SKIP_ANT_STEP"                   "ON"                "<SAME>"
+  ## Autogen
+  "AUTOGEN_ORIGIN_DEPENDS"                  "OFF"               "<SAME>"
+  "AUTOGEN_PARALLEL"                        "ON"                "<SAME>"
+  "AUTOGEN_USE_SYSTEM_INCLUDE"              "ON"                "<SAME>"
+  ## moc
+  "AUTOMOC_DEPEND_FILTERS"                  "FIRST<SEMI>SECOND" "<SAME>"
+  ## C++
+  "CXX_SCAN_FOR_MODULES"                    "ON"                "<SAME>"
+  ## Ninja
+  "JOB_POOL_COMPILE"                        "compile_pool"      "<SAME>"
+  "JOB_POOL_LINK"                           "link_pool"         "<SAME>"
+  "JOB_POOL_PRECOMPILE_HEADER"              "pch_pool"          "<SAME>"
+  ## Visual Studio
+  "VS_NO_COMPILE_BATCHING"                  "ON"                "<SAME>"
+  "VS_WINDOWS_TARGET_PLATFORM_MIN_VERSION"  "10.0.10240.0"      "<SAME>"
+
+  # Output location properties
+  "ARCHIVE_OUTPUT_DIRECTORY"                "${dir}"            "<SAME>"
+  "COMPILE_PDB_OUTPUT_DIRECTORY"            "${dir}"            "<SAME>"
+  "LIBRARY_OUTPUT_DIRECTORY"                "${dir}"            "<SAME>"
+  "PDB_OUTPUT_DIRECTORY"                    "${dir}"            "<SAME>"
+  "RUNTIME_OUTPUT_DIRECTORY"                "${dir}"            "<SAME>"
+
+  # macOS bundle properties
+  "FRAMEWORK"                               "OFF"               "<SAME>"
+  "FRAMEWORK_MULTI_CONFIG_POSTFIX"          ".mcpostfix"        "<SAME>"
+  "MACOSX_BUNDLE"                           "OFF"               "<SAME>"
+
+  # Usage requirement properties
+  "LINK_INTERFACE_LIBRARIES"                "c"                 "<SAME>"
+
+  # Metadata
+  "EXPORT_COMPILE_COMMANDS"                 "OFF"               "<SAME>"
+  )
+
+if (CMAKE_HOST_APPLE) # compile-guarded in CMake
+  if (CMAKE_GENERATOR STREQUAL "Xcode")
+    list(APPEND properties
+      # property                                        expected      alias
+      # Xcode properties
+      "XCODE_SCHEME_ADDRESS_SANITIZER"                  "ON"          "<SAME>"
+      "XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN" "ON"          "<SAME>"
+      "XCODE_SCHEME_DEBUG_DOCUMENT_VERSIONING"          "ON"          "<SAME>"
+      "XCODE_SCHEME_ENABLE_GPU_FRAME_CAPTURE_MODE"      "ON"          "<SAME>"
+      "XCODE_SCHEME_THREAD_SANITIZER"                   "ON"          "<SAME>"
+      "XCODE_SCHEME_THREAD_SANITIZER_STOP"              "ON"          "<SAME>"
+      "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER"      "ON"          "<SAME>"
+      "XCODE_SCHEME_UNDEFINED_BEHAVIOUR_SANITIZER_STOP" "ON"          "<SAME>"
+      "XCODE_SCHEME_LAUNCH_CONFIGURATION"               "ON"          "<SAME>"
+      "XCODE_SCHEME_ENABLE_GPU_API_VALIDATION"          "ON"          "<SAME>"
+      "XCODE_SCHEME_ENABLE_GPU_SHADER_VALIDATION"       "ON"          "<SAME>"
+      "XCODE_SCHEME_WORKING_DIRECTORY"                  "ON"          "<SAME>"
+      "XCODE_SCHEME_DISABLE_MAIN_THREAD_CHECKER"        "ON"          "<SAME>"
+      "XCODE_SCHEME_MAIN_THREAD_CHECKER_STOP"           "ON"          "<SAME>"
+      "XCODE_SCHEME_MALLOC_SCRIBBLE"                    "ON"          "<SAME>"
+      "XCODE_SCHEME_MALLOC_GUARD_EDGES"                 "ON"          "<SAME>"
+      "XCODE_SCHEME_GUARD_MALLOC"                       "ON"          "<SAME>"
+      "XCODE_SCHEME_LAUNCH_MODE"                        "ON"          "<SAME>"
+      "XCODE_SCHEME_ZOMBIE_OBJECTS"                     "ON"          "<SAME>"
+      "XCODE_SCHEME_MALLOC_STACK"                       "ON"          "<SAME>"
+      "XCODE_SCHEME_DYNAMIC_LINKER_API_USAGE"           "ON"          "<SAME>"
+      "XCODE_SCHEME_DYNAMIC_LIBRARY_LOADS"              "ON"          "<SAME>"
+      "XCODE_SCHEME_ENVIRONMENT"                        "ON"          "<SAME>"
+      "XCODE_LINK_BUILD_PHASE_MODE"                     "BUILT_ONLY"  "<SAME>"
+      )
+  endif ()
+endif ()
+
+macro (add_language_properties lang std)
+  list(APPEND properties
+    # property                      expected  alias
+    "${lang}_COMPILER_LAUNCHER"     "ccache"  "<SAME>"
+    "${lang}_STANDARD"              "${std}"  "<SAME>"
+    "${lang}_STANDARD_REQUIRED"     "TRUE"    "<SAME>"
+    "${lang}_EXTENSIONS"            "FALSE"   "<SAME>"
+    "${lang}_VISIBILITY_PRESET"     "hidden"  "<SAME>"
+    )
+endmacro ()
+
+# Mock up knowing the standard flag. This doesn't actually build, so nothing
+# should care at this point.
+set(CMAKE_Cc_std_11_STANDARD_COMPILE_OPTION "-std=c11")
+
+add_language_properties(C c_std_11)
+add_language_properties(CXX cxx_std_11)
+add_language_properties(CUDA cuda_std_11)
+add_language_properties(HIP hip_std_11)
+add_language_properties(OBJC c_std_99)
+add_language_properties(OBJCXX cxx_std_11)
+
+# Set up pools for properties set above.
+if (CMAKE_GENERATOR MATCHES "Ninja")
+  set_property(GLOBAL APPEND
+    PROPERTY
+      JOB_POOLS
+        compile_pool=1
+        link_pool=1
+        pch_pool=1)
+endif ()
+
+prepare_target_types(can_compile_sources
+  EXECUTABLE SHARED STATIC MODULE OBJECT)
+
+run_property_tests(can_compile_sources properties)
+
+set(properties_with_defaults
+  # property                      expected  alias
+  "PCH_WARN_INVALID"              "ON"      "<SAME>"
+  "PCH_INSTANTIATE_TEMPLATES"     "ON"      "<SAME>"
+  "ISPC_HEADER_SUFFIX"            "_ispc.h" "<SAME>"
+  "SKIP_BUILD_RPATH"              "OFF"     "<SAME>"
+  "BUILD_WITH_INSTALL_RPATH"      "OFF"     "<SAME>"
+  "INSTALL_RPATH"                 ""        "<SAME>"
+  "INSTALL_RPATH_USE_LINK_PATH"   "OFF"     "<SAME>"
+  "UNITY_BUILD_BATCH_SIZE"        "8"       "<SAME>"
+  "UNITY_BUILD_MODE"              "BATCH"   "<SAME>"
+  )
+
+if (CMAKE_HOST_APPLE)
+  if (CMAKE_GENERATOR STREQUAL "Xcode")
+    list(APPEND properties_with_defaults
+      # property                      expected  alias
+      "XCODE_LINK_BUILD_PHASE_MODE"   "NONE"    "<SAME>"
+      )
+  endif ()
+endif ()
+
+set(with_defaults 1)
+run_property_tests(can_compile_sources properties_with_defaults)
diff --git a/Tests/RunCMake/property_init/Executable.cmake b/Tests/RunCMake/property_init/Executable.cmake
new file mode 100644
index 0000000..ede0e4b
--- /dev/null
+++ b/Tests/RunCMake/property_init/Executable.cmake
@@ -0,0 +1,25 @@
+set(dir "${CMAKE_CURRENT_BINARY_DIR}")
+
+set(properties
+  # property                      expected  alias
+  # Compilation properties
+  ## Platforms
+  ### Windows
+  "VS_DEBUGGER_COMMAND"           "vsdbg"   "<SAME>"
+  "VS_DEBUGGER_COMMAND_ARGUMENTS" "/?"      "<SAME>"
+  "VS_DEBUGGER_ENVIRONMENT"       "env=val" "<SAME>"
+  "VS_DEBUGGER_WORKING_DIRECTORY" "${dir}"  "<SAME>"
+
+  # Linking properties
+  ## Platforms
+  ### Android
+  "ANDROID_GUI"                   "OFF"     "<SAME>"
+
+  # Metadata
+  "CROSSCOMPILING_EMULATOR"       "emu"     "<SAME>"
+  )
+
+prepare_target_types(executable
+           EXECUTABLE
+  IMPORTED_EXECUTABLE)
+run_property_tests(executable properties)
diff --git a/Tests/RunCMake/property_init/ImportedTargets.cmake b/Tests/RunCMake/property_init/ImportedTargets.cmake
new file mode 100644
index 0000000..0b51998
--- /dev/null
+++ b/Tests/RunCMake/property_init/ImportedTargets.cmake
@@ -0,0 +1,9 @@
+set(properties
+  # property        expected  alias
+  "SYSTEM"          "ON"      "<SAME>"
+  )
+
+prepare_target_types(imported
+  IMPORTED_EXECUTABLE IMPORTED_INTERFACE IMPORTED_MODULE IMPORTED_OBJECT IMPORTED_SHARED IMPORTED_STATIC)
+set(with_defaults 1)
+run_property_tests(imported properties)
diff --git a/Tests/RunCMake/property_init/LibraryArtifact.cmake b/Tests/RunCMake/property_init/LibraryArtifact.cmake
new file mode 100644
index 0000000..942b433
--- /dev/null
+++ b/Tests/RunCMake/property_init/LibraryArtifact.cmake
@@ -0,0 +1,10 @@
+per_config(properties
+  # property              expected  alias
+  # Linking properties
+  "_POSTFIX"              "test"    "<UNSET>"
+  )
+
+prepare_target_types(library_with_artifact
+           MODULE          SHARED          STATIC
+  IMPORTED_MODULE IMPORTED_SHARED IMPORTED_STATIC)
+run_property_tests(library_with_artifact properties)
diff --git a/Tests/RunCMake/property_init/Linkable.cmake b/Tests/RunCMake/property_init/Linkable.cmake
new file mode 100644
index 0000000..e5d75d1
--- /dev/null
+++ b/Tests/RunCMake/property_init/Linkable.cmake
@@ -0,0 +1,12 @@
+per_config(properties
+  # property                        expected  alias
+  # Linking properties
+  ## Platforms
+  ### macOS
+  "FRAMEWORK_MULTI_CONFIG_POSTFIX_" ".fw"     "<UNSET>"
+  )
+
+prepare_target_types(linkable
+           EXECUTABLE          SHARED          STATIC
+  IMPORTED_EXECUTABLE IMPORTED_SHARED IMPORTED_STATIC)
+run_property_tests(linkable properties)
diff --git a/Tests/RunCMake/property_init/NonImportedNormalTarget.cmake b/Tests/RunCMake/property_init/NonImportedNormalTarget.cmake
new file mode 100644
index 0000000..cf3b726
--- /dev/null
+++ b/Tests/RunCMake/property_init/NonImportedNormalTarget.cmake
@@ -0,0 +1,9 @@
+set(properties
+  # property                      expected  alias
+  # Linking properties
+  "LINK_LIBRARIES_ONLY_TARGETS"   "OFF"     "<SAME>"
+  )
+
+prepare_target_types(normal_non_imported
+  EXECUTABLE SHARED STATIC MODULE OBJECT INTERFACE)
+run_property_tests(normal_non_imported properties)
diff --git a/Tests/RunCMake/property_init/NonImportedTarget.cmake b/Tests/RunCMake/property_init/NonImportedTarget.cmake
new file mode 100644
index 0000000..7e2e22c
--- /dev/null
+++ b/Tests/RunCMake/property_init/NonImportedTarget.cmake
@@ -0,0 +1,11 @@
+set(properties
+  # property                      expected            alias
+  # Compilation properties
+  ## Language
+  ### CSharp
+  "DOTNET_SDK"                    "Microsoft.NET.Sdk" "<SAME>"
+  )
+
+prepare_target_types(non_imported
+  EXECUTABLE SHARED STATIC MODULE OBJECT INTERFACE CUSTOM)
+run_property_tests(non_imported properties)
diff --git a/Tests/RunCMake/property_init/NormalTarget.cmake b/Tests/RunCMake/property_init/NormalTarget.cmake
new file mode 100644
index 0000000..99507cf
--- /dev/null
+++ b/Tests/RunCMake/property_init/NormalTarget.cmake
@@ -0,0 +1,10 @@
+per_config(properties
+  # property                      expected  alias
+  # Usage requirement properties
+  "MAP_IMPORTED_CONFIG_"          "Release" "<UNSET>"
+  )
+
+prepare_target_types(normal
+           EXECUTABLE          INTERFACE          MODULE          OBJECT          SHARED          STATIC
+  IMPORTED_EXECUTABLE IMPORTED_INTERFACE IMPORTED_MODULE IMPORTED_OBJECT IMPORTED_SHARED IMPORTED_STATIC)
+run_property_tests(normal properties)
diff --git a/Tests/RunCMake/property_init/PICTargets.cmake b/Tests/RunCMake/property_init/PICTargets.cmake
new file mode 100644
index 0000000..6c99505
--- /dev/null
+++ b/Tests/RunCMake/property_init/PICTargets.cmake
@@ -0,0 +1,21 @@
+set(properties
+  # property                                expected            alias
+  # Compilation properties
+  "POSITION_INDEPENDENT_CODE"               "True"              "<SAME>"
+  )
+
+prepare_target_types(pic_targets
+           EXECUTABLE          MODULE          OBJECT          SHARED          STATIC
+                      IMPORTED_MODULE                 IMPORTED_SHARED)
+run_property_tests(pic_targets properties)
+
+set(APPEND properties_with_defaults
+  # property                      expected  alias
+  "POSITION_INDEPENDENT_CODE"     "True"    "<SAME>"
+  )
+
+prepare_target_types(pic_default_targets
+           MODULE          SHARED
+  IMPORTED_MODULE IMPORTED_SHARED)
+set(with_defaults 1)
+run_property_tests(pic_default_targets properties_with_defaults)
diff --git a/Tests/RunCMake/property_init/RunCMakeTest.cmake b/Tests/RunCMake/property_init/RunCMakeTest.cmake
new file mode 100644
index 0000000..310da72
--- /dev/null
+++ b/Tests/RunCMake/property_init/RunCMakeTest.cmake
@@ -0,0 +1,16 @@
+include(RunCMake)
+
+run_cmake(Always)
+run_cmake(CompileSources)
+run_cmake(Executable)
+run_cmake(ImportedTargets)
+run_cmake(LibraryArtifact)
+run_cmake(Linkable)
+run_cmake(NonImportedNormalTarget)
+run_cmake(NonImportedTarget)
+run_cmake(NormalTarget)
+run_cmake(PICTargets)
+run_cmake(SharedLibrary)
+run_cmake(TargetsWithArtifact)
+run_cmake(TargetsWithCommands)
+run_cmake(TargetsWithExports)
diff --git a/Tests/RunCMake/property_init/SharedLibrary.cmake b/Tests/RunCMake/property_init/SharedLibrary.cmake
new file mode 100644
index 0000000..49715a4
--- /dev/null
+++ b/Tests/RunCMake/property_init/SharedLibrary.cmake
@@ -0,0 +1,12 @@
+set(dir "${CMAKE_CURRENT_BINARY_DIR}")
+
+set(properties
+  # property                      expected  alias
+  # Linking properties
+  "DLL_NAME_WITH_SOVERSION"       "OFF"     "<SAME>"
+  )
+
+prepare_target_types(shared_library
+           SHARED
+  IMPORTED_SHARED)
+run_property_tests(shared_library properties)
diff --git a/Tests/RunCMake/property_init/TargetsWithArtifact.cmake b/Tests/RunCMake/property_init/TargetsWithArtifact.cmake
new file mode 100644
index 0000000..0c19ea3
--- /dev/null
+++ b/Tests/RunCMake/property_init/TargetsWithArtifact.cmake
@@ -0,0 +1,19 @@
+set(dir "${CMAKE_CURRENT_BINARY_DIR}")
+
+per_config(properties
+  # property                      expected  alias
+  # Compilation properties
+  "INTERPROCEDURAL_OPTIMIZATION_" "OFF"     "<UNSET>"
+
+  # Output location properties
+  "ARCHIVE_OUTPUT_DIRECTORY_"     "${dir}"  "<UNSET>"
+  "COMPILE_PDB_OUTPUT_DIRECTORY_" "${dir}"  "<UNSET>"
+  "LIBRARY_OUTPUT_DIRECTORY_"     "${dir}"  "<UNSET>"
+  "PDB_OUTPUT_DIRECTORY_"         "${dir}"  "<UNSET>"
+  "RUNTIME_OUTPUT_DIRECTORY_"     "${dir}"  "<UNSET>"
+  )
+
+prepare_target_types(with_artifact
+           EXECUTABLE          MODULE          SHARED          STATIC
+  IMPORTED_EXECUTABLE IMPORTED_MODULE IMPORTED_SHARED IMPORTED_STATIC)
+run_property_tests(with_artifact properties)
diff --git a/Tests/RunCMake/property_init/TargetsWithCommands.cmake b/Tests/RunCMake/property_init/TargetsWithCommands.cmake
new file mode 100644
index 0000000..4db0ca3
--- /dev/null
+++ b/Tests/RunCMake/property_init/TargetsWithCommands.cmake
@@ -0,0 +1,13 @@
+set(properties
+  # property                        expected        alias
+  # Compilation properties
+  ## Language
+  ### CSharp
+  "DOTNET_TARGET_FRAMEWORK"         "netcoreapp2.1" "<SAME>"
+  "DOTNET_TARGET_FRAMEWORK_VERSION" "v4.5"          "<SAME>"
+  )
+
+prepare_target_types(with_commands
+           EXECUTABLE          MODULE          OBJECT          SHARED          STATIC CUSTOM
+  IMPORTED_EXECUTABLE IMPORTED_MODULE IMPORTED_OBJECT IMPORTED_SHARED IMPORTED_STATIC)
+run_property_tests(with_commands properties)
diff --git a/Tests/RunCMake/property_init/TargetsWithExports.cmake b/Tests/RunCMake/property_init/TargetsWithExports.cmake
new file mode 100644
index 0000000..9b2e213
--- /dev/null
+++ b/Tests/RunCMake/property_init/TargetsWithExports.cmake
@@ -0,0 +1,51 @@
+set(properties
+  # property                      expected  alias
+  # Linking properties
+  ## Platforms
+  ### AIX
+  "AIX_EXPORT_ALL_SYMBOLS"        "OFF"     "<SAME>"
+  ### Windows
+  "WINDOWS_EXPORT_ALL_SYMBOLS"    "OFF"     "<SAME>"
+  )
+
+prepare_target_types(symbol_export_target
+           EXECUTABLE          SHARED
+  IMPORTED_EXECUTABLE IMPORTED_SHARED)
+run_property_tests(symbol_export_target properties)
+
+# `ENABLE_EXPORTS` has a more complicated initialization.
+set(properties
+  # property                      expected  alias
+  # Linking properties
+  "ENABLE_EXPORTS"                "OFF"     "<SAME>"
+  )
+
+prepare_target_types(executable
+           EXECUTABLE
+  IMPORTED_EXECUTABLE)
+set(iteration "-ENABLE_EXPORTS")
+run_property_tests(executable_target properties)
+
+set(with_defaults 1)
+
+set(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS OFF)
+set(properties
+  # property                      expected  alias
+  # Linking properties
+  "ENABLE_EXPORTS"                "OFF"     "<SAME>"
+  )
+
+set(iteration "-SHARED_LIBRARY_ENABLE_EXPORTS")
+run_property_tests(shared_library_target properties)
+unset(CMAKE_SHARED_LIBRARY_ENABLE_EXPORTS)
+
+set(CMAKE_EXECUTABLE_ENABLE_EXPORTS OFF)
+set(properties
+  # property                      expected  alias
+  # Linking properties
+  "ENABLE_EXPORTS"                "OFF"     "<SAME>"
+  )
+
+set(iteration "-EXECUTABLE_ENABLE_EXPORTS")
+run_property_tests(executable_target properties)
+unset(CMAKE_EXECUTABLE_ENABLE_EXPORTS)
diff --git a/Tests/RunCMake/property_init/library.c b/Tests/RunCMake/property_init/library.c
new file mode 100644
index 0000000..ad6a649
--- /dev/null
+++ b/Tests/RunCMake/property_init/library.c
@@ -0,0 +1,4 @@
+int foo(int arg)
+{
+  return arg;
+}
diff --git a/Tests/RunCMake/property_init/main.c b/Tests/RunCMake/property_init/main.c
new file mode 100644
index 0000000..14917b7
--- /dev/null
+++ b/Tests/RunCMake/property_init/main.c
@@ -0,0 +1,4 @@
+int main(int argc, char* argv[])
+{
+  return argc - 1;
+}
diff --git a/Tests/RunCMake/property_init/util.cmake b/Tests/RunCMake/property_init/util.cmake
new file mode 100644
index 0000000..7edc6f9
--- /dev/null
+++ b/Tests/RunCMake/property_init/util.cmake
@@ -0,0 +1,191 @@
+set(all_target_types
+  "EXECUTABLE"
+
+  "IMPORTED_EXECUTABLE"
+
+  "INTERFACE"
+  "MODULE"
+  "OBJECT"
+  "SHARED"
+  "STATIC"
+
+  "IMPORTED_INTERFACE"
+  "IMPORTED_MODULE"
+  "IMPORTED_OBJECT"
+  "IMPORTED_SHARED"
+  "IMPORTED_STATIC"
+
+  "CUSTOM")
+
+function (prepare_target_types name)
+  set("${name}" "${ARGN}" PARENT_SCOPE)
+  list(REMOVE_ITEM all_target_types ${ARGN})
+  set("not_${name}" "${all_target_types}" PARENT_SCOPE)
+endfunction ()
+
+function (per_config variable)
+  prepare_properties("${property_table}" properties expected_values expected_alias)
+
+  get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+  if (is_multi_config)
+    set(configs "${CMAKE_CONFIGURATION_TYPES}")
+  else ()
+    if (NOT CMAKE_BUILD_TYPE STREQUAL "")
+      set(configs "${CMAKE_BUILD_TYPE}")
+    endif ()
+  endif ()
+
+  foreach (property expected alias IN ZIP_LISTS expected_properties expected_values expected_alias)
+    if (property MATCHES "^_")
+      set(prepend 1)
+    elseif (property MATCHES "_$")
+      set(prepend 0)
+    else ()
+      message(SEND_ERROR
+        "Per-config properties must have a `_` at one end of their name: '${property}'")
+    endif ()
+    foreach (config IN LISTS configs)
+      if (prepend)
+        list(APPEND "${variable}"
+          "${config}_${property}" "${value}/${config}" "${alias}")
+      else ()
+        list(APPEND "${variable}"
+          "${property}_${config}" "${value}/${config}" "${alias}")
+      endif ()
+    endforeach ()
+  endforeach ()
+
+  set("${variable}" "${${variable}}" PARENT_SCOPE)
+endfunction ()
+
+function (make_target name type)
+  if (type STREQUAL "EXECUTABLE")
+    add_executable("${name}")
+    target_sources("${name}" PRIVATE ${main_sources})
+  elseif (type STREQUAL "IMPORTED_EXECUTABLE")
+    add_executable("${name}" IMPORTED)
+    set_property(TARGET "${name}" PROPERTY IMPORTED_LOCATION "${CMAKE_COMMAND}")
+  elseif (type STREQUAL "CUSTOM")
+    add_custom_target("${name}" COMMAND "${CMAKE_EXECUTABLE}" -E echo "${name}")
+  elseif (type MATCHES "IMPORTED_")
+    string(REPLACE "IMPORTED_" "" type "${type}")
+    add_library("${name}" IMPORTED ${type})
+    if (NOT type STREQUAL "INTERFACE")
+      set_property(TARGET "${name}" PROPERTY IMPORTED_LOCATION "${default_library_location}")
+    endif ()
+  else ()
+    add_library("${name}" ${type})
+    target_sources("${name}" PRIVATE ${library_sources})
+  endif ()
+
+  if (type MATCHES "EXECUTABLE")
+    add_executable("alias::${name}" ALIAS "${name}")
+  elseif (NOT type STREQUAL "CUSTOM")
+    add_library("alias::${name}" ALIAS "${name}")
+  endif ()
+endfunction ()
+
+function (check_property target property expected)
+  if (NOT TARGET "${target}")
+    message(SEND_ERROR
+      "No such target '${target}'")
+    return ()
+  endif ()
+
+  get_property(is_set TARGET "${target}" PROPERTY "${property}" SET)
+  if (is_set)
+    get_property(actual TARGET "${target}" PROPERTY "${property}")
+  endif ()
+  if (expected STREQUAL "<UNSET>")
+    if (is_set)
+      message(SEND_ERROR
+        "Target '${target}' should not have '${property}' set at all, but is '${actual}'")
+    endif ()
+  elseif (is_set AND NOT expected STREQUAL actual)
+    message(SEND_ERROR
+      "Target '${target}' should have '${property}' set to '${expected}', but is '${actual}'")
+  elseif (NOT is_set)
+    message(SEND_ERROR
+      "Target '${target}' should have '${property}' set to '${expected}', but is not set at all")
+  endif ()
+endfunction ()
+
+function (prepare_properties table output_properties output_expected output_alias)
+  set(_properties)
+  set(_expected)
+  set(_alias)
+
+  set(variable "_properties")
+  foreach (item IN LISTS "${table}")
+    list(APPEND "${variable}" "${item}")
+    if (variable STREQUAL "_properties")
+      set(variable "_expected")
+    elseif (variable STREQUAL "_expected")
+      set(variable "_alias")
+    elseif (variable STREQUAL "_alias")
+      set(variable "_properties")
+    else ()
+      message(FATAL_ERROR
+        "Failed to track property table parsing")
+    endif ()
+  endforeach ()
+  if (NOT variable STREQUAL "_properties")
+    message(FATAL_ERROR
+      "Table does not have a multiple of 3 items")
+  endif ()
+
+  set("${output_properties}" "${_properties}" PARENT_SCOPE)
+  set("${output_expected}" "${_expected}" PARENT_SCOPE)
+  set("${output_alias}" "${_alias}" PARENT_SCOPE)
+endfunction ()
+
+# Contextual variables:
+#   iteration: make unique target names
+#   with_defaults: if set, do not set variables, but instead test internal
+#                  default calculations
+function (run_property_tests applied_types property_table)
+  prepare_properties("${property_table}" expected_properties expected_values expected_alias)
+
+  if (NOT with_defaults)
+    foreach (property expected IN ZIP_LISTS expected_properties expected_values)
+      string(REPLACE "<SEMI>" ";" expected "${expected}")
+      set("CMAKE_${property}" "${expected}")
+    endforeach ()
+  endif ()
+
+  foreach (target_type IN LISTS "${applied_types}")
+    set(target_name "${RunCMake_TEST}${iteration}-${target_type}")
+    if (with_defaults)
+      string(APPEND target_name "-defaults")
+    endif ()
+    make_target("${target_name}" "${target_type}")
+    foreach (property expected alias IN ZIP_LISTS expected_properties expected_values expected_alias)
+      string(REPLACE "<SEMI>" ";" expected "${expected}")
+      check_property("${target_name}" "${property}" "${expected}")
+      if (NOT target_type STREQUAL "CUSTOM")
+        if (alias STREQUAL "<SAME>")
+          check_property("alias::${target_name}" "${property}" "${expected}")
+        elseif (alias STREQUAL "<UNSET>")
+          check_property("alias::${target_name}" "${property}" "<UNSET>")
+        else ()
+          message(FATAL_ERROR
+            "Invalid `alias` entry for property '${property}': '${alias}'")
+        endif ()
+      endif ()
+    endforeach ()
+  endforeach ()
+
+  foreach (target_type IN LISTS "not_${applied_types}")
+    set(target_name "${RunCMake_TEST}${iteration}-${target_type}-unset")
+    if (with_defaults)
+      string(APPEND target_name "-defaults")
+    endif ()
+    make_target("${target_name}" "${target_type}")
+    foreach (property IN LISTS expected_properties)
+      check_property("${target_name}" "${property}" "<UNSET>")
+      if (NOT target_type STREQUAL "CUSTOM")
+        check_property("alias::${target_name}" "${property}" "<UNSET>")
+      endif ()
+    endforeach ()
+  endforeach ()
+endfunction ()
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/set/CacheErrors-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/set/CacheErrors-result.txt
diff --git a/Tests/RunCMake/set/CacheErrors-stderr.txt b/Tests/RunCMake/set/CacheErrors-stderr.txt
new file mode 100644
index 0000000..9983160
--- /dev/null
+++ b/Tests/RunCMake/set/CacheErrors-stderr.txt
@@ -0,0 +1,19 @@
+^CMake Error at CacheErrors\.cmake:1 \(set\):
+  set given invalid arguments for CACHE mode: missing type and docstring
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at CacheErrors\.cmake:2 \(set\):
+  set given invalid arguments for CACHE mode: missing type or docstring
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at CacheErrors\.cmake:3 \(set\):
+  set given invalid arguments for CACHE mode: missing type or docstring
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
++
+CMake Error at CacheErrors\.cmake:4 \(set\):
+  set given invalid arguments: FORCE specified without CACHE
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/set/CacheErrors.cmake b/Tests/RunCMake/set/CacheErrors.cmake
new file mode 100644
index 0000000..d177474
--- /dev/null
+++ b/Tests/RunCMake/set/CacheErrors.cmake
@@ -0,0 +1,4 @@
+set (var val CACHE)
+set (var val CACHE STRING)
+set (var val CACHE "")
+set (var val CACH3 STRING "" FORCE)
diff --git a/Tests/RunCMake/set/RunCMakeTest.cmake b/Tests/RunCMake/set/RunCMakeTest.cmake
index b3bd0a4..c785450 100644
--- a/Tests/RunCMake/set/RunCMakeTest.cmake
+++ b/Tests/RunCMake/set/RunCMakeTest.cmake
@@ -1,5 +1,6 @@
 include(RunCMake)
 
+run_cmake(CacheErrors)
 run_cmake(ParentScope)
 run_cmake(ParentPulling)
 run_cmake(ParentPullingRecursive)
diff --git a/Tests/RunCMake/set_property/RunCMakeTest.cmake b/Tests/RunCMake/set_property/RunCMakeTest.cmake
index 692c6b9..1a5498d 100644
--- a/Tests/RunCMake/set_property/RunCMakeTest.cmake
+++ b/Tests/RunCMake/set_property/RunCMakeTest.cmake
@@ -10,6 +10,12 @@
 run_cmake(LINK_LIBRARIES)
 run_cmake(SOURCES)
 run_cmake(SOURCE_FILE)
+run_cmake(TEST-invalid)
 run_cmake(TYPE)
 run_cmake(USER_PROP)
 run_cmake(USER_PROP_INHERITED)
+
+set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/TEST-build")
+run_cmake(TEST)
+set(RunCMake_TEST_NO_CLEAN 1)
+run_cmake_command(TEST-test ${CMAKE_CTEST_COMMAND} -C Debug)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/set_property/TEST-invalid-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/set_property/TEST-invalid-result.txt
diff --git a/Tests/RunCMake/set_property/TEST-invalid-stderr.txt b/Tests/RunCMake/set_property/TEST-invalid-stderr.txt
new file mode 100644
index 0000000..c0a40d6
--- /dev/null
+++ b/Tests/RunCMake/set_property/TEST-invalid-stderr.txt
@@ -0,0 +1,11 @@
+^CMake Error at TEST-invalid\.cmake:[0-9]+ \(set_property\):
+  set_property called with incorrect number of arguments no value provided to
+  the DIRECTORY option
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at TEST-invalid\.cmake:[0-9]+ \(set_property\):
+  set_property given non-existent DIRECTORY nonexistent
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/set_property/TEST-invalid.cmake b/Tests/RunCMake/set_property/TEST-invalid.cmake
new file mode 100644
index 0000000..6828c96
--- /dev/null
+++ b/Tests/RunCMake/set_property/TEST-invalid.cmake
@@ -0,0 +1,4 @@
+enable_testing()
+
+set_property(TEST t DIRECTORY PROPERTY PASS_REGULAR_EXPRESSION "Invalid")
+set_property(TEST t DIRECTORY nonexistent PROPERTY PASS_REGULAR_EXPRESSION "Invalid")
diff --git a/Tests/RunCMake/set_property/TEST-subdir1/CMakeLists.txt b/Tests/RunCMake/set_property/TEST-subdir1/CMakeLists.txt
new file mode 100644
index 0000000..b1fad66
--- /dev/null
+++ b/Tests/RunCMake/set_property/TEST-subdir1/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_test(NAME t COMMAND ${CMAKE_COMMAND} -E echo "Subdirectory")
+add_test(NAME t2 COMMAND ${CMAKE_COMMAND} -E echo "Subdirectory")
+add_test(NAME t3 COMMAND ${CMAKE_COMMAND} -E echo "Subdirectory")
diff --git a/Tests/RunCMake/set_property/TEST-subdir2/CMakeLists.txt b/Tests/RunCMake/set_property/TEST-subdir2/CMakeLists.txt
new file mode 100644
index 0000000..8621b00
--- /dev/null
+++ b/Tests/RunCMake/set_property/TEST-subdir2/CMakeLists.txt
@@ -0,0 +1 @@
+set_property(TEST t3 DIRECTORY ../TEST-subdir1 PROPERTY PASS_REGULAR_EXPRESSION "Subdirectory")
diff --git a/Tests/RunCMake/set_property/TEST.cmake b/Tests/RunCMake/set_property/TEST.cmake
new file mode 100644
index 0000000..7ef5aa3
--- /dev/null
+++ b/Tests/RunCMake/set_property/TEST.cmake
@@ -0,0 +1,9 @@
+enable_testing()
+
+add_test(NAME t COMMAND ${CMAKE_COMMAND} -E echo "Top directory")
+add_subdirectory(TEST-subdir1)
+add_subdirectory(TEST-subdir2)
+
+set_property(TEST t PROPERTY PASS_REGULAR_EXPRESSION "Top directory")
+set_property(TEST t DIRECTORY TEST-subdir1 PROPERTY PASS_REGULAR_EXPRESSION "Subdirectory")
+set_property(TEST t2 DIRECTORY "${CMAKE_BINARY_DIR}/TEST-subdir1" PROPERTY PASS_REGULAR_EXPRESSION "Subdirectory")
diff --git a/Tests/RunCMake/set_tests_properties/CMakeLists.txt b/Tests/RunCMake/set_tests_properties/CMakeLists.txt
new file mode 100644
index 0000000..922aad6
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.27)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt b/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid-result.txt
similarity index 100%
copy from Tests/RunCMake/CXXModules/NoCXX20ModuleFlag-result.txt
copy to Tests/RunCMake/set_tests_properties/DIRECTORY-invalid-result.txt
diff --git a/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid-stderr.txt b/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid-stderr.txt
new file mode 100644
index 0000000..e219399
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid-stderr.txt
@@ -0,0 +1,13 @@
+^CMake Error at DIRECTORY-invalid\.cmake:[0-9]+ \(set_tests_properties\):
+  Error after keyword "DIRECTORY":
+
+    missing required value
+
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)
+
+
+CMake Error at DIRECTORY-invalid\.cmake:[0-9]+ \(set_tests_properties\):
+  set_tests_properties given non-existent DIRECTORY nonexistent
+Call Stack \(most recent call first\):
+  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid.cmake b/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid.cmake
new file mode 100644
index 0000000..4d87df1
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/DIRECTORY-invalid.cmake
@@ -0,0 +1,4 @@
+enable_testing()
+
+set_tests_properties(t DIRECTORY PROPERTIES PASS_REGULAR_EXPRESSION "Top directory")
+set_tests_properties(t DIRECTORY nonexistent PROPERTIES PASS_REGULAR_EXPRESSION "Top directory")
diff --git a/Tests/RunCMake/set_tests_properties/DIRECTORY-subdir1/CMakeLists.txt b/Tests/RunCMake/set_tests_properties/DIRECTORY-subdir1/CMakeLists.txt
new file mode 100644
index 0000000..b1fad66
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/DIRECTORY-subdir1/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_test(NAME t COMMAND ${CMAKE_COMMAND} -E echo "Subdirectory")
+add_test(NAME t2 COMMAND ${CMAKE_COMMAND} -E echo "Subdirectory")
+add_test(NAME t3 COMMAND ${CMAKE_COMMAND} -E echo "Subdirectory")
diff --git a/Tests/RunCMake/set_tests_properties/DIRECTORY-subdir2/CMakeLists.txt b/Tests/RunCMake/set_tests_properties/DIRECTORY-subdir2/CMakeLists.txt
new file mode 100644
index 0000000..8859597
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/DIRECTORY-subdir2/CMakeLists.txt
@@ -0,0 +1 @@
+set_tests_properties(t3 DIRECTORY ../DIRECTORY-subdir1 PROPERTIES PASS_REGULAR_EXPRESSION "Subdirectory")
diff --git a/Tests/RunCMake/set_tests_properties/DIRECTORY.cmake b/Tests/RunCMake/set_tests_properties/DIRECTORY.cmake
new file mode 100644
index 0000000..87d13e3
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/DIRECTORY.cmake
@@ -0,0 +1,9 @@
+enable_testing()
+
+add_test(NAME t COMMAND ${CMAKE_COMMAND} -E echo "Top directory")
+add_subdirectory(DIRECTORY-subdir1)
+add_subdirectory(DIRECTORY-subdir2)
+
+set_tests_properties(t PROPERTIES PASS_REGULAR_EXPRESSION "Top directory")
+set_tests_properties(t DIRECTORY DIRECTORY-subdir1 PROPERTIES PASS_REGULAR_EXPRESSION "Subdirectory")
+set_tests_properties(t2 DIRECTORY "${CMAKE_BINARY_DIR}/DIRECTORY-subdir1" PROPERTIES PASS_REGULAR_EXPRESSION "Subdirectory")
diff --git a/Tests/RunCMake/set_tests_properties/RunCMakeTest.cmake b/Tests/RunCMake/set_tests_properties/RunCMakeTest.cmake
new file mode 100644
index 0000000..b49158f
--- /dev/null
+++ b/Tests/RunCMake/set_tests_properties/RunCMakeTest.cmake
@@ -0,0 +1,8 @@
+include(RunCMake)
+
+run_cmake(DIRECTORY-invalid)
+
+set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DIRECTORY-build)
+run_cmake(DIRECTORY)
+set(RunCMake_TEST_NO_CLEAN 1)
+run_cmake_command(DIRECTORY-test ${CMAKE_CTEST_COMMAND} -C Debug)
diff --git a/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake b/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake
index 4a0f068..65c708c 100644
--- a/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake
+++ b/Tests/RunCMake/target_link_libraries-ALIAS/AliasTargets.cmake
@@ -14,8 +14,14 @@
 set_property(TARGET import-local PROPERTY IMPORTED_IMPLIB "${binary_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}func${CMAKE_IMPORT_LIBRARY_SUFFIX}")
 add_library(alias::local ALIAS import-local)
 
+if(NOT DEFINED CMAKE_IMPORT_LIBRARY_SUFFIX)
+  add_library(import-local-stub SHARED IMPORTED)
+  set_property(TARGET import-local-stub PROPERTY IMPORTED_IMPLIB "${binary_dir}/${CMAKE_STATIC_LIBRARY_PREFIX}func${CMAKE_SHARED_LIBRARY_SUFFIX}")
+  add_library(alias::local-stub ALIAS import-local-stub)
+endif()
+
 add_library (lib-local SHARED lib.c)
-target_link_libraries (lib-local PRIVATE alias::local)
+target_link_libraries (lib-local PRIVATE alias::local $<TARGET_NAME_IF_EXISTS:alias::local-stub>)
 
 add_executable (main-local main.c)
 target_link_libraries (main-local PRIVATE alias::local)
diff --git a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
index 9b6581c..0f3a6b7 100644
--- a/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries-LINK_LIBRARY/RunCMakeTest.cmake
@@ -31,7 +31,7 @@
     set(LINK_SHARED_LIBRARY_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
     set(LINK_SHARED_LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
   endif()
-  if (MINGW OR MSYS OR CYGWIN)
+  if (MINGW OR MSYS OR CYGWIN OR CMAKE_C_COMPILER_ID STREQUAL "OrangeC")
     set(LINK_EXTERN_LIBRARY_SUFFIX "")
   else()
     set(LINK_EXTERN_LIBRARY_SUFFIX "${CMAKE_IMPORT_LIBRARY_SUFFIX}")
@@ -126,7 +126,7 @@
       ((DEFINED MSVC_VERSION AND MSVC_VERSION GREATER "1900") OR (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" AND NOT CMAKE_C_SIMULATE_ID STREQUAL "MSVC")))
     OR (CMAKE_SYSTEM_NAME STREQUAL "SunOS" AND
       (NOT CMAKE_C_COMPILER_ID STREQUAL "SunPro" OR CMAKE_C_COMPILER_VERSION GREATER "5.9"))
-    OR CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|watchOS|Linux|BSD|MSYS|CYGWIN")
+    OR CMAKE_SYSTEM_NAME MATCHES "Darwin|iOS|tvOS|visionOS|watchOS|Linux|BSD|MSYS|CYGWIN")
   run_cmake(feature-WHOLE_ARCHIVE)
   run_cmake_target(feature-WHOLE_ARCHIVE link-exe main)
 endif()
diff --git a/Tests/RunCMake/target_link_libraries/ImportedTargetStub.cmake b/Tests/RunCMake/target_link_libraries/ImportedTargetStub.cmake
new file mode 100644
index 0000000..04f9cfb
--- /dev/null
+++ b/Tests/RunCMake/target_link_libraries/ImportedTargetStub.cmake
@@ -0,0 +1,2 @@
+add_library(SharedStubImportedGlobal SHARED IMPORTED GLOBAL)
+set_target_properties(SharedStubImportedGlobal PROPERTIES IMPORTED_IMPLIB z)
diff --git a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
index 7c5d77d..0e3877a 100644
--- a/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_libraries/RunCMakeTest.cmake
@@ -23,6 +23,7 @@
 run_cmake(CMP0108-OLD-self-link)
 run_cmake(CMP0108-NEW-self-link)
 run_cmake(ImportedTarget)
+run_cmake(ImportedTargetStub)
 run_cmake(ImportedTargetFailure)
 run_cmake(MixedSignature)
 run_cmake(Separate-PRIVATE-LINK_PRIVATE-uses)
diff --git a/Tests/RunCMake/target_link_options/LINK_OPTIONS-dollar-option-check.cmake b/Tests/RunCMake/target_link_options/LINK_OPTIONS-dollar-option-check.cmake
new file mode 100644
index 0000000..0f897fe
--- /dev/null
+++ b/Tests/RunCMake/target_link_options/LINK_OPTIONS-dollar-option-check.cmake
@@ -0,0 +1,4 @@
+
+if (NOT actual_stdout MATCHES "BADFLAG_\\$dollar")
+  set (RunCMake_TEST_FAILED "Not found expected 'BADFLAG_$dollar'.")
+endif()
diff --git a/Tests/RunCMake/target_link_options/LINK_OPTIONS-dollar-option-result.txt b/Tests/RunCMake/target_link_options/LINK_OPTIONS-dollar-option-result.txt
new file mode 100644
index 0000000..8d98f9d
--- /dev/null
+++ b/Tests/RunCMake/target_link_options/LINK_OPTIONS-dollar-option-result.txt
@@ -0,0 +1 @@
+.*
diff --git a/Tests/RunCMake/target_link_options/LINK_OPTIONS.cmake b/Tests/RunCMake/target_link_options/LINK_OPTIONS.cmake
index bb04841..879151b 100644
--- a/Tests/RunCMake/target_link_options/LINK_OPTIONS.cmake
+++ b/Tests/RunCMake/target_link_options/LINK_OPTIONS.cmake
@@ -53,3 +53,7 @@
 # executable with generator expression
 add_executable(LinkOptions_exe LinkOptionsExe.c)
 target_link_options(LinkOptions_exe PRIVATE $<$<CONFIG:Release>:${pre}BADFLAG_RELEASE${obj}>)
+
+# executable with dollar character
+add_executable(LinkOptions_dollar_exe LinkOptionsExe.c)
+target_link_options(LinkOptions_dollar_exe PRIVATE "${pre}BADFLAG_$dollar${obj}")
diff --git a/Tests/RunCMake/target_link_options/RunCMakeTest.cmake b/Tests/RunCMake/target_link_options/RunCMakeTest.cmake
index 1a29ecf..ff0c5a8 100644
--- a/Tests/RunCMake/target_link_options/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_link_options/RunCMakeTest.cmake
@@ -30,7 +30,7 @@
   run_cmake_target(LINK_OPTIONS shared LinkOptions_shared --config Release)
   run_cmake_target(LINK_OPTIONS mod LinkOptions_mod --config Release)
   run_cmake_target(LINK_OPTIONS exe LinkOptions_exe --config Release)
-
+  run_cmake_target(LINK_OPTIONS dollar-option LinkOptions_dollar_exe --config Release)
 
   run_cmake(genex_LINK_LANGUAGE)
 
diff --git a/Tests/RunCMake/target_sources/FileSetAbsoluteInstallIncludeDirExport.cmake b/Tests/RunCMake/target_sources/FileSetAbsoluteInstallIncludeDirExport.cmake
new file mode 100644
index 0000000..f049d91
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetAbsoluteInstallIncludeDirExport.cmake
@@ -0,0 +1,16 @@
+enable_language(C)
+
+# According to https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html#module:GNUInstallDirs
+# relative CMAKE_INSTALL_<dir> are encouraged, but absolute path's are also allowed.
+# Construct an absolute CMAKE_INSTALL_INCLUDEDIR.
+set(CMAKE_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include")
+
+add_library(lib1)
+target_sources(lib1
+    PRIVATE lib1.c
+    PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES h1.h)
+# Expect install(TARGETS) to respect absolute CMAKE_INSTALL_INCLUDEDIR
+# when installing the HEADERS.
+# Must not prepend the CMAKE_INSTALL_PREFIX in the <pkg>-config.cmake.
+install(TARGETS lib1 EXPORT lib1-config FILE_SET HEADERS)
+install(EXPORT lib1-config NAMESPACE lib1:: DESTINATION share/lib1)
diff --git a/Tests/RunCMake/target_sources/FileSetAbsoluteInstallIncludeDirImport.cmake b/Tests/RunCMake/target_sources/FileSetAbsoluteInstallIncludeDirImport.cmake
new file mode 100644
index 0000000..123d6ae
--- /dev/null
+++ b/Tests/RunCMake/target_sources/FileSetAbsoluteInstallIncludeDirImport.cmake
@@ -0,0 +1,9 @@
+enable_language(CXX)
+
+get_filename_component(CMAKE_PREFIX_PATH "${CMAKE_BINARY_DIR}" DIRECTORY)
+string(APPEND CMAKE_PREFIX_PATH "/FileSetAbsoluteInstallIncludeDirExport-build/install")
+
+find_package(lib1 REQUIRED)
+
+add_executable(exe main.cpp)
+target_link_libraries(exe PRIVATE lib1::lib1)
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt
index faf0f5a..c13c8a8 100644
--- a/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt
+++ b/Tests/RunCMake/target_sources/FileSetDefaultWrongType-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error at FileSetDefaultWrongType\.cmake:[0-9]+ \(target_sources\):
-  target_sources File set TYPE may only be "HEADERS"
+  target_sources File set TYPE may only be "HEADERS" or "CXX_MODULES"
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt
deleted file mode 100644
index 5356b07..0000000
--- a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental-stderr.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-^CMake Warning \(dev\) at FileSetDefaultWrongTypeExperimental.cmake:6 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
-CMake Error at FileSetDefaultWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\):
-  target_sources File set TYPE may only be "HEADERS" or "CXX_MODULES"
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake
deleted file mode 100644
index 44f1626..0000000
--- a/Tests/RunCMake/target_sources/FileSetDefaultWrongTypeExperimental.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-enable_language(C)
-
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
-add_library(lib1 STATIC empty.c)
-target_sources(lib1 PRIVATE FILE_SET UNKNOWN)
diff --git a/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt
index 8ffa786..9cc764f 100644
--- a/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt
+++ b/Tests/RunCMake/target_sources/FileSetWrongType-stderr.txt
@@ -1,4 +1,4 @@
 ^CMake Error at FileSetWrongType\.cmake:[0-9]+ \(target_sources\):
-  target_sources File set TYPE may only be "HEADERS"
+  target_sources File set TYPE may only be "HEADERS" or "CXX_MODULES"
 Call Stack \(most recent call first\):
   CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt
deleted file mode 100644
index 5c75000..0000000
--- a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental-stderr.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-^CMake Warning \(dev\) at FileSetWrongTypeExperimental.cmake:6 \(target_sources\):
-  CMake's C\+\+ module support is experimental.  It is meant only for
-  experimentation and feedback to CMake developers.
-Call Stack \(most recent call first\):
-  CMakeLists.txt:3 \(include\)
-This warning is for project developers.  Use -Wno-dev to suppress it.
-
-CMake Error at FileSetWrongTypeExperimental\.cmake:[0-9]+ \(target_sources\):
-  target_sources File set TYPE may only be "HEADERS" or "CXX_MODULES"
-Call Stack \(most recent call first\):
-  CMakeLists\.txt:[0-9]+ \(include\)$
diff --git a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake b/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake
deleted file mode 100644
index adf1185..0000000
--- a/Tests/RunCMake/target_sources/FileSetWrongTypeExperimental.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-enable_language(C)
-
-set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
-
-add_library(lib1 STATIC empty.c)
-target_sources(lib1 PRIVATE FILE_SET a TYPE UNKNOWN)
diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
index 90915cd..4d3d4fe 100644
--- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake
+++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake
@@ -27,8 +27,6 @@
 run_cmake(FileSetNoType)
 run_cmake(FileSetWrongType)
 run_cmake(FileSetDefaultWrongType)
-run_cmake(FileSetWrongTypeExperimental)
-run_cmake(FileSetDefaultWrongTypeExperimental)
 run_cmake(FileSetChangeScope)
 run_cmake(FileSetChangeType)
 run_cmake(FileSetWrongBaseDirs)
@@ -99,3 +97,4 @@
 endfunction()
 
 run_export_import(FileSet)
+run_export_import(FileSetAbsoluteInstallIncludeDir)
diff --git a/Tests/SourceGroups/CMakeLists.txt b/Tests/SourceGroups/CMakeLists.txt
index d726395..550fe9e 100644
--- a/Tests/SourceGroups/CMakeLists.txt
+++ b/Tests/SourceGroups/CMakeLists.txt
@@ -63,3 +63,5 @@
                             ${tree_files_with_prefix} ${tree_files_without_prefix}
                             ${tree_files_with_empty_prefix} README.txt
                             nested.c)
+
+add_subdirectory(sub2)
diff --git a/Tests/SourceGroups/sub2/CMakeLists.txt b/Tests/SourceGroups/sub2/CMakeLists.txt
new file mode 100644
index 0000000..e457bc4
--- /dev/null
+++ b/Tests/SourceGroups/sub2/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_executable(SourceGroups2 main.c
+                             qux.c subsub/qax.c)
+
+source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" ) #PREFIX TheSubDir2 )
diff --git a/Tests/SourceGroups/sub2/main.c b/Tests/SourceGroups/sub2/main.c
new file mode 100644
index 0000000..4cd8ae0
--- /dev/null
+++ b/Tests/SourceGroups/sub2/main.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+extern int qax(void);
+extern int qux(void);
+
+int main()
+{
+  printf("qux: %d qax: %d\n", qux(), qax());
+
+  return 0;
+}
diff --git a/Tests/SourceGroups/sub2/qux.c b/Tests/SourceGroups/sub2/qux.c
new file mode 100644
index 0000000..1a8b6f9
--- /dev/null
+++ b/Tests/SourceGroups/sub2/qux.c
@@ -0,0 +1,4 @@
+int qux(void)
+{
+  return 1234;
+}
diff --git a/Tests/SourceGroups/sub2/subsub/qax.c b/Tests/SourceGroups/sub2/subsub/qax.c
new file mode 100644
index 0000000..c1b1042
--- /dev/null
+++ b/Tests/SourceGroups/sub2/subsub/qax.c
@@ -0,0 +1,4 @@
+int qax(void)
+{
+  return 123;
+}
diff --git a/Tests/TestsWorkingDirectory/main.c b/Tests/TestsWorkingDirectory/main.c
index ca72f21..f856339 100644
--- a/Tests/TestsWorkingDirectory/main.c
+++ b/Tests/TestsWorkingDirectory/main.c
@@ -5,7 +5,7 @@
 
 #if defined(_WIN32) &&                                                        \
   (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__BORLANDC__) ||      \
-   defined(__MINGW32__))
+   defined(__MINGW32__) || defined(__ORANGEC__))
 
 #  include <direct.h>
 #  include <io.h>
diff --git a/Tests/UseSWIG/AlternateLibraryName/CMakeLists.txt b/Tests/UseSWIG/AlternateLibraryName/CMakeLists.txt
index a2c239c..f20593c 100644
--- a/Tests/UseSWIG/AlternateLibraryName/CMakeLists.txt
+++ b/Tests/UseSWIG/AlternateLibraryName/CMakeLists.txt
@@ -7,7 +7,7 @@
 find_package(SWIG REQUIRED)
 include(${SWIG_USE_FILE})
 
-find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
+find_package(Python REQUIRED COMPONENTS Interpreter Development)
 
 # Path separator
 if (WIN32)
@@ -27,9 +27,9 @@
 set_target_properties (example_python PROPERTIES
   INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/.."
   SWIG_USE_TARGET_INCLUDE_DIRECTORIES TRUE)
-target_link_libraries(example_python PRIVATE Python2::Python)
+target_link_libraries(example_python PRIVATE Python::Python)
 
 
 add_test (NAME AlternateLibraryName.example1
   COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}${PS}$<TARGET_FILE_DIR:example_python>"
-  "${Python2_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
+  "${Python_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
diff --git a/Tests/UseSWIG/CMakeLists.txt b/Tests/UseSWIG/CMakeLists.txt
index 7c4925e..3d80270 100644
--- a/Tests/UseSWIG/CMakeLists.txt
+++ b/Tests/UseSWIG/CMakeLists.txt
@@ -147,16 +147,18 @@
   --build-options ${build_options}
   --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
-add_test(NAME UseSWIG.MultiplePython COMMAND
-  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-  --build-and-test
-  "${CMake_SOURCE_DIR}/Tests/UseSWIG/MultiplePython"
-  "${CMake_BINARY_DIR}/Tests/UseSWIG/MultiplePython"
-  ${build_generator_args}
-  --build-project TestMultiplePython
-  --build-options ${build_options}
-  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+if(CMake_TEST_FindPython2 AND CMake_TEST_FindPython3)
+  add_test(NAME UseSWIG.MultiplePython COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/UseSWIG/MultiplePython"
+    "${CMake_BINARY_DIR}/Tests/UseSWIG/MultiplePython"
+    ${build_generator_args}
+    --build-project TestMultiplePython
+    --build-options ${build_options}
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
+endif()
 add_test(NAME UseSWIG.MultipleFiles COMMAND
   ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
   --build-and-test
@@ -165,20 +167,20 @@
   ${build_generator_args}
   --build-project TestMultipleFiles
   --build-options ${build_options}
+)
+
+if(CMake_TEST_FindPython2 OR CMake_TEST_FindPython3)
+  add_test(NAME UseSWIG.ModuleVersion2 COMMAND
+    ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+    --build-and-test
+    "${CMake_SOURCE_DIR}/Tests/UseSWIG/ModuleVersion2"
+    "${CMake_BINARY_DIR}/Tests/UseSWIG/ModuleVersion2"
+    ${build_generator_args}
+    --build-project TestModuleVersion2
+    --build-options ${build_options}
+    --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
   )
-
-
-add_test(NAME UseSWIG.ModuleVersion2 COMMAND
-  ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
-  --build-and-test
-  "${CMake_SOURCE_DIR}/Tests/UseSWIG/ModuleVersion2"
-  "${CMake_BINARY_DIR}/Tests/UseSWIG/ModuleVersion2"
-  ${build_generator_args}
-  --build-project TestModuleVersion2
-  --build-options ${build_options}
-  --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
-  )
-
+endif()
 
 add_test(NAME UseSWIG.UseTargetINCLUDE_DIRECTORIES COMMAND
   ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
diff --git a/Tests/UseSWIG/ModuleName/CMakeLists.txt b/Tests/UseSWIG/ModuleName/CMakeLists.txt
index 435b441..c62319f 100644
--- a/Tests/UseSWIG/ModuleName/CMakeLists.txt
+++ b/Tests/UseSWIG/ModuleName/CMakeLists.txt
@@ -8,14 +8,7 @@
 cmake_policy(SET CMP0086 NEW)
 include(${SWIG_USE_FILE})
 
-find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
-
-# Path separator
-if (WIN32)
-  set (PS "$<SEMICOLON>")
-else()
-  set (PS ":")
-endif()
+find_package(Python REQUIRED COMPONENTS Interpreter Development)
 
 unset(CMAKE_SWIG_FLAGS)
 
@@ -34,9 +27,9 @@
   LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1"
   ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1"
   RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1")
-target_link_libraries(example1 PRIVATE Python2::Module)
+target_link_libraries(example1 PRIVATE Python::Module)
 
 
 add_test (NAME ModuleName.example1
-  COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}/Python2${PS}$<TARGET_FILE_DIR:example1>"
-  "${Python2_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/runme.py")
+  COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_FILE_DIR:example1>"
+  "${Python_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/runme.py")
diff --git a/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt b/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt
index 093e858..317ed47 100644
--- a/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt
+++ b/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt
@@ -7,9 +7,6 @@
 find_package(SWIG REQUIRED)
 include(${SWIG_USE_FILE})
 
-find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
-find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
-
 if (WIN32)
   set (PS $<SEMICOLON>)
 else()
@@ -25,32 +22,69 @@
 set_property(SOURCE "../example.i"
   PROPERTY GENERATED_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/..")
 
-swig_add_library(example1
-                 LANGUAGE python
-                 SOURCES ../example.i ../example.cxx)
-set_target_properties (example1 PROPERTIES
-  OUTPUT_NAME example
-  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2"
-  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2"
-  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2")
-target_link_libraries(example1 PRIVATE Python2::Module)
+if(CMake_TEST_FindPython2)
+  find_package(Python2 REQUIRED COMPONENTS Interpreter Development)
 
-# re-use sample interface file for another plugin
-swig_add_library(example2
-                 LANGUAGE python
-                 SOURCES ../example.i ../example.cxx)
-set_target_properties (example2 PROPERTIES
-  OUTPUT_NAME example
-  LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3"
-  ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3"
-  RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3")
-target_link_libraries(example2 PRIVATE Python3::Module)
+  swig_add_library(example1
+                   LANGUAGE python
+                   SOURCES ../example.i ../example.cxx)
+  set_target_properties (example1 PROPERTIES
+    OUTPUT_NAME example
+    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2-1"
+    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2-1"
+    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2-1")
+  target_link_libraries(example1 PRIVATE Python2::Module)
+
+  add_test (NAME ModuleVersion2.example1
+    COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_PROPERTY:example1,SWIG_SUPPORT_FILES_DIRECTORY>${PS}$<TARGET_FILE_DIR:example1>"
+    "${Python2_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
+
+  # re-use sample interface file for another plugin
+  swig_add_library(example2
+                   LANGUAGE python
+                   SOURCES ../example.i ../example.cxx)
+  set_target_properties (example2 PROPERTIES
+    OUTPUT_NAME example
+    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2-2"
+    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2-2"
+    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2-2")
+  target_link_libraries(example2 PRIVATE Python2::Module)
+
+  add_test (NAME ModuleVersion2.example2
+    COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_PROPERTY:example2,SWIG_SUPPORT_FILES_DIRECTORY>${PS}$<TARGET_FILE_DIR:example2>"
+    "${Python2_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
+endif()
+
+if(CMake_TEST_FindPython3)
+  find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
+
+  swig_add_library(example3
+                   LANGUAGE python
+                   SOURCES ../example.i ../example.cxx)
+  set_target_properties (example3 PROPERTIES
+    OUTPUT_NAME example
+    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3-1"
+    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3-1"
+    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3-1")
+  target_link_libraries(example3 PRIVATE Python3::Module)
+
+  add_test (NAME ModuleVersion2.example3
+    COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_PROPERTY:example3,SWIG_SUPPORT_FILES_DIRECTORY>${PS}$<TARGET_FILE_DIR:example3>"
+    "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
 
 
-add_test (NAME ModuleVersion2.example1
-  COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_PROPERTY:example1,SWIG_SUPPORT_FILES_DIRECTORY>${PS}$<TARGET_FILE_DIR:example1>"
-  "${Python2_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
+  # re-use sample interface file for another plugin
+  swig_add_library(example4
+                   LANGUAGE python
+                   SOURCES ../example.i ../example.cxx)
+  set_target_properties (example2 PROPERTIES
+    OUTPUT_NAME example
+    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3-2"
+    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3-2"
+    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3-2")
+  target_link_libraries(example4 PRIVATE Python3::Module)
 
-add_test (NAME ModuleVersion2.example2
-  COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_PROPERTY:example2,SWIG_SUPPORT_FILES_DIRECTORY>${PS}$<TARGET_FILE_DIR:example2>"
-  "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
+  add_test (NAME ModuleVersion2.example4
+    COMMAND "${CMAKE_COMMAND}" -E env "PYTHONPATH=$<TARGET_PROPERTY:example4,SWIG_SUPPORT_FILES_DIRECTORY>${PS}$<TARGET_FILE_DIR:example4>"
+    "${Python3_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/../runme.py")
+endif()
diff --git a/Tests/UseSWIG/MultipleFiles/CMakeLists.txt b/Tests/UseSWIG/MultipleFiles/CMakeLists.txt
index bf3d946..36734f9 100644
--- a/Tests/UseSWIG/MultipleFiles/CMakeLists.txt
+++ b/Tests/UseSWIG/MultipleFiles/CMakeLists.txt
@@ -11,7 +11,7 @@
 unset(SWIG_LANG_OPTIONS)
 unset(SWIG_LANG_LIBRARIES)
 
-find_package(Python3 REQUIRED COMPONENTS Development)
+find_package(Python REQUIRED COMPONENTS Development)
 
 set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/add.i" PROPERTY CPLUSPLUS ON)
 set_property(SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/sub.i" PROPERTY CPLUSPLUS ON)
@@ -27,4 +27,4 @@
                          "${CMAKE_CURRENT_SOURCE_DIR}/add.cxx"
                          "${CMAKE_CURRENT_SOURCE_DIR}/sub.cxx")
 target_include_directories(example PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
-target_link_libraries(example PRIVATE Python3::Module)
+target_link_libraries(example PRIVATE Python::Module)
diff --git a/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt b/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt
index 80a2e16..6cdf987 100644
--- a/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt
+++ b/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt
@@ -7,7 +7,7 @@
 find_package(SWIG REQUIRED)
 include(${SWIG_USE_FILE})
 
-find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
+find_package(Python REQUIRED COMPONENTS Interpreter Development)
 
 unset(CMAKE_SWIG_FLAGS)
 
@@ -25,7 +25,7 @@
   LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1"
   ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1"
   RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1")
-target_link_libraries(example1 PRIVATE Python3::Module)
+target_link_libraries(example1 PRIVATE Python::Module)
 
 
 # Check that source property override target property
@@ -42,4 +42,4 @@
   LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example2"
   ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example2"
   RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example2")
-target_link_libraries(example2 PRIVATE Python3::Module)
+target_link_libraries(example2 PRIVATE Python::Module)
diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt
index fee21b6..12b5407 100644
--- a/Utilities/Doxygen/CMakeLists.txt
+++ b/Utilities/Doxygen/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeDeveloperReference_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index 0f03c33..6056030 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/Utilities/IWYU/mapping.imp
@@ -1,4 +1,6 @@
 [
+  # https://github.com/include-what-you-use/include-what-you-use/blob/master/docs/IWYUMappings.md
+
   # C++ alternatives to C standard headers
   { include: [ "<assert.h>", public, "<cassert>", public ] },
   { include: [ "<complex.h>", public, "<ccomplex>", public ] },
@@ -30,7 +32,7 @@
   { include: [ "<bits/std_abs.h>", private, "<stdlib.h>", public ] },
   { include: [ "<bits/stdint-intn.h>", private, "<stdint.h>", public ] },
   { include: [ "<bits/stdint-uintn.h>", private, "<stdint.h>", public ] },
-  { include: [ "<bits/string_view.tcc>", private, "<string_view>", private ] },
+  { include: [ "<bits/string_view.tcc>", private, "<string_view>", public ] },
   { include: [ "<bits/time.h>", private, "<time.h>", public ] },
   { include: [ "<bits/types/clock_t.h>", private, "<time.h>", public ] },
   { include: [ "<bits/types/mbstate_t.h>", private, "<wchar.h>", public ] },
@@ -41,70 +43,16 @@
 
   # HACK: check whether this can be removed with next iwyu release.
   { symbol: [ "__GLIBC__", private, "<stdlib.h>", public ] },
-  { symbol: [ "_Noreturn", private, "<stdlib.h>", public ] },
 
   # HACK: iwyu wrongly thinks that including <iosfwd> is sufficient.
   { symbol: [ "std::stringstream", private, "<sstream>", public ] },
   { symbol: [ "std::istringstream", private, "<sstream>", public ] },
   { symbol: [ "std::ostringstream", private, "<sstream>", public ] },
 
-  # HACK: iwyu suggests <ext/alloc_traits.h> and <memory> each time vector[] is used.
-  # https://github.com/include-what-you-use/include-what-you-use/issues/166
-  { include: [ "<ext/alloc_traits.h>", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmFileLock> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmFileLockPool::ScopePool> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmComputeComponentGraph::TarjanEntry> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmFortranFile> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmGraphEdgeList> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmOrderDirectories::ConflictList> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<cmStateSnapshot> >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<std::basic_string<char> > >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > > >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > >::value_type", private, "<vector>", public ] },
-  { symbol: [ "std::allocator_traits<std::allocator<uv_stdio_container_s> >::value_type", private, "<vector>", public ] },
-
-  # TODO: enable this block and remove some <utility> includes?
-  #{ symbol: [ "std::pair", private, "<utility>", public ] },
-  #{ symbol: [ "std::pair", private, "<map>", public ] },
-  #{ symbol: [ "std::pair", private, "<set>", public ] },
-
-  # HACK: iwyu wrongly thinks that <system_error> is needed for std::hash
-  { symbol: [ "std::hash", private, "<functional>", public ] },
-
-  # HACK: iwyu thinks we use a libstdc++ private type
-  { symbol: [ "__gnu_cxx::size_t", private, "<cstddef>", public ] },
-
-  # __decay_and_strip is used internally in the C++11 standard library.
-  # IWYU does not classify it as internal and suggests to add <type_traits>.
-  # To ignore it, we simply map it to a file that is included anyway.
-  # Use 'CMake_IWYU_VERBOSE' to see the fully qualified names that need this.
-  # TODO: Can this be simplified with an @-expression?
-  #{ symbol: [ "@std::__decay_and_strip<.*>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<int>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<bool>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<char const (&)[1]>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cmCommand *&>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cmGeneratorTarget *&>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cmFindCommon::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cmSearchPath>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cm::string_view>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<std::basic_string<char> &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<const std::basic_string<char> &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cmFindPackageCommand::PathLabel &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<cmGlobalNinjaGenerator::TargetAlias &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<__gnu_cxx::__normal_iterator<const cmCTestTestHandler::cmCTestTestProperties *, std::vector<cmCTestTestHandler::cmCTestTestProperties, std::allocator<cmCTestTestHandler::cmCTestTestProperties> > > &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__decay_and_strip<const __gnu_cxx::__normal_iterator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > *, std::vector<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> >, std::allocator<std::pair<cm::string_view, std::function<void (ArgumentParser::Instance &)> > > > > &>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__success_type<std::chrono::duration<double, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::__success_type<std::chrono::duration<long, std::ratio<1, 1000000000> > >::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<60, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1000> > >::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "__gnu_cxx::__enable_if<true, bool>::__type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::remove_reference<std::basic_string<char, std::char_traits<char>, std::allocator<char> > &>::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::remove_reference<cmCTestTestHandler::Signal &>::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::remove_reference<Defer &>::type", private, "\"cmConfigure.h\"", public ] },
-  { symbol: [ "std::remove_reference<dap::StoppedEvent &>::type", private, "\"cmConfigure.h\"", public ] },
+  # HACK: iwyu attributes vector's usage of std::max to its caller.
+  # https://github.com/include-what-you-use/include-what-you-use/issues/908
+  { symbol: ["std::max", "private", "<algorithm>", "public" ] },
+  { symbol: ["std::max", "private", "<vector>", "public" ] },
 
   # Wrappers for 3rd-party libraries
   { include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },
@@ -127,10 +75,14 @@
   { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] },
   { include: [ "<fstream>", public, "\"cmsys/FStream.hxx\"", public ] },
 
-  { include: [ "<filesystem>", private, "<cm/filesystem>", public ] },
-  { include: [ "<optional>", private, "<cm/optional>", public ] },
-  { include: [ "<shared_mutex>", private, "<cm/shared_mutex>", public ] },
-  { include: [ "<string_view>", private, "<cm/string_view>", public ] },
+  { symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] },
+  { symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] },
+  { symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] },
+
+  { include: [ "<filesystem>", public, "<cm/filesystem>", public ] },
+  { include: [ "<optional>", public, "<cm/optional>", public ] },
+  { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] },
+  { include: [ "<string_view>", public, "<cm/string_view>", public ] },
 
   # major and minor are used as macro arguments. Those are false matches.
   { symbol: [ "major", private, "\"cmVersion.h\"", public ] },
diff --git a/Utilities/Release/linux/x86_64/Dockerfile b/Utilities/Release/linux/x86_64/Dockerfile
index 736ee26..71c8776 100644
--- a/Utilities/Release/linux/x86_64/Dockerfile
+++ b/Utilities/Release/linux/x86_64/Dockerfile
@@ -6,8 +6,8 @@
 # The resulting image will have an '/out' directory containing the package.
 
 # Keep this in sync with the `.gitlab/os-linux.yml` `.linux_release_x86_64` image.
-ARG FROM_IMAGE_NAME=kitware/cmake:build-linux-x86_64-deps-2020-04-02
-ARG FROM_IMAGE_DIGEST=@sha256:77e9ab183f34680990db9da5945473e288f0d6556bce79ecc1589670d656e157
+ARG FROM_IMAGE_NAME=kitware/cmake:build-linux-x86_64-deps-2023-08-16
+ARG FROM_IMAGE_DIGEST=@sha256:aa0ebdbd90a51cc83d31f393c5c48ec4599a28f7ccdc288558522c6265b24fae
 ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
 FROM $FROM_IMAGE
 
@@ -19,8 +19,7 @@
  && mkdir -p /opt/cmake/src/cmake-build \
  && cd /opt/cmake/src/cmake-build \
  && cp ../cmake/Utilities/Release/linux/x86_64/cache.txt CMakeCache.txt \
- && source /opt/rh/devtoolset-6/enable \
- && source /opt/rh/rh-python36/enable \
+ && source /opt/rh/devtoolset-7/enable \
  && set -x \
  && ../cmake/bootstrap --parallel=$(nproc) --docdir=doc/cmake \
  && nice make -j $(nproc) \
diff --git a/Utilities/Release/linux/x86_64/base/Dockerfile b/Utilities/Release/linux/x86_64/base/Dockerfile
index dfc7df8..5df138e 100644
--- a/Utilities/Release/linux/x86_64/base/Dockerfile
+++ b/Utilities/Release/linux/x86_64/base/Dockerfile
@@ -4,8 +4,8 @@
 # Produce a base image with a build environment for portable CMake binaries.
 # Build using the directory containing this file as its own build context.
 
-ARG FROM_IMAGE_NAME=centos:6
-ARG FROM_IMAGE_DIGEST=@sha256:dec8f471302de43f4cfcf82f56d99a5227b5ea1aa6d02fa56344986e1f4610e7
+ARG FROM_IMAGE_NAME=centos:7
+ARG FROM_IMAGE_DIGEST=@sha256:be65f488b7764ad3638f236b7b515b3678369a5124c47b8d32916d6487418ea4
 ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
 FROM $FROM_IMAGE
 
@@ -14,8 +14,8 @@
  && yum install -y \
         ca-certificates \
         curl \
-        devtoolset-6-gcc \
-        devtoolset-6-gcc-c++ \
+        devtoolset-7-gcc \
+        devtoolset-7-gcc-c++ \
         fontconfig-devel \
         freetype-devel \
         git \
@@ -24,7 +24,8 @@
         make \
         patch \
         perl \
-        rh-python36-python-pip \
+        python3-pip \
         xz \
+        which \
  && yum clean all \
  && :
diff --git a/Utilities/Release/linux/x86_64/deps/Dockerfile b/Utilities/Release/linux/x86_64/deps/Dockerfile
index 7864aac..b8bd164 100644
--- a/Utilities/Release/linux/x86_64/deps/Dockerfile
+++ b/Utilities/Release/linux/x86_64/deps/Dockerfile
@@ -4,17 +4,11 @@
 # Produce an image with custom-built dependencies for portable CMake binaries.
 # Build using the directory containing this file as its own build context.
 
-ARG FROM_IMAGE_NAME=kitware/cmake:build-linux-x86_64-base-2019-08-09
-ARG FROM_IMAGE_DIGEST=@sha256:d2c13617f01181a3143a069e4496d6b78eafffa19d181c42be196d5dfd588151
+ARG FROM_IMAGE_NAME=kitware/cmake:build-linux-x86_64-base-2023-08-16
+ARG FROM_IMAGE_DIGEST=@sha256:95c4d937a635067096dc8fa21b20cce6108ced091d3c42d56cf0261f40406a10
 ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
 FROM $FROM_IMAGE
 
-# Sphinx
-RUN : \
- && source /opt/rh/rh-python36/enable \
- && pip install sphinx==2.1.2 \
- && :
-
 # Qt
 # Version 5.12.0 was the last to bundle xkbcommon.
 COPY qt-install.patch /opt/qt/src/
@@ -25,7 +19,7 @@
  && sha512sum qt-everywhere-src-5.12.0.tar.xz | grep -q 0dd03d2645fb6dac5b58c8caf92b4a0a6900131f1ccfb02443a0df4702b5da0458f4c45e758d1b929ec709b0f4b36900df2fd60a058af9cc8c1a0748b6d57aae \
  && tar xJf qt-everywhere-src-5.12.0.tar.xz \
  && cd qt-build \
- && source /opt/rh/devtoolset-6/enable \
+ && source /opt/rh/devtoolset-7/enable \
  && ../qt-everywhere-src-5.12.0/configure \
       -prefix /opt/qt \
       -static \
@@ -97,7 +91,7 @@
       -skip qtxmlpatterns \
       -nomake examples \
       -nomake tests \
- && make install -j $(nproc) \
+ && make install \
  && cd /opt/qt \
  && patch -p1 -i src/qt-install.patch \
  && cd /opt \
@@ -112,7 +106,7 @@
  && sha512sum ncurses-6.1.tar.gz | grep -q e308af43f8b7e01e98a55f4f6c4ee4d1c39ce09d95399fa555b3f0cdf5fd0db0f4c4d820b4af78a63f6cf6d8627587114a40af48cfc066134b600520808a77ee \
  && tar xzf ncurses-6.1.tar.gz \
  && cd ncurses-build \
- && source /opt/rh/devtoolset-6/enable \
+ && source /opt/rh/devtoolset-7/enable \
  && ../ncurses-6.1/configure \
       --prefix=/opt/ncurses \
       --with-terminfo-dirs=/etc/terminfo:/lib/terminfo:/usr/share/terminfo \
@@ -129,12 +123,12 @@
 RUN : \
  && mkdir -p /opt/openssl/src \
  && cd /opt/openssl/src \
- && curl -O https://www.openssl.org/source/openssl-1.1.1f.tar.gz \
- && sha512sum openssl-1.1.1f.tar.gz | grep -q b00bd9b5ad5298fbceeec6bb19c1ab0c106ca5cfb31178497c58bf7e0e0cf30fcc19c20f84e23af31cc126bf2447d3e4f8461db97bafa7bd78f69561932f000c \
- && tar xzf openssl-1.1.1f.tar.gz \
- && cd openssl-1.1.1f \
+ && curl -O https://www.openssl.org/source/openssl-1.1.1v.tar.gz \
+ && sha512sum openssl-1.1.1v.tar.gz | grep -q 1a67340d99026aa62bf50ff89165d9f77fe4a6690fe30d1751b5021dd3f238391afd581b41724687c322c4e3af1770c44a63766a06e9b8cab6425101153e0c7e \
+ && tar xzf openssl-1.1.1v.tar.gz \
+ && cd openssl-1.1.1v \
  && patch -p1 -i ../openssl-source.patch \
- && source /opt/rh/devtoolset-6/enable \
+ && source /opt/rh/devtoolset-7/enable \
  && ./Configure --prefix=/opt/openssl linux-elf no-asm no-shared -D_POSIX_C_SOURCE=199506L -D_POSIX_SOURCE=1 -D_SVID_SOURCE=1 -D_BSD_SOURCE=1 \
  && make install_dev -j $(nproc) \
  && cd /opt \
diff --git a/Utilities/Release/linux/x86_64/test/Dockerfile b/Utilities/Release/linux/x86_64/test/Dockerfile
index 6629156..e6c195e 100644
--- a/Utilities/Release/linux/x86_64/test/Dockerfile
+++ b/Utilities/Release/linux/x86_64/test/Dockerfile
@@ -4,8 +4,8 @@
 # Produce a base image with a test environment for packaged CMake binaries.
 # Build using the directory containing this file as its own build context.
 
-ARG FROM_IMAGE_NAME=debian:9
-ARG FROM_IMAGE_DIGEST=@sha256:397b2157a9ea8d7f16c613aded70284292106e8b813fb1ed5de8a8785310a26a
+ARG FROM_IMAGE_NAME=debian:10
+ARG FROM_IMAGE_DIGEST=@sha256:b86bfc1dd2fb1820fea6c1e0b6e23d155198b518b3a506f6edad71bf6e9a8cef
 ARG FROM_IMAGE=$FROM_IMAGE_NAME$FROM_IMAGE_DIGEST
 FROM $FROM_IMAGE
 
diff --git a/Utilities/Release/win/qt-5.12.1-win-x86-msvc-install.patch b/Utilities/Release/win/qt-5.12.1-win-x86-msvc-install.patch
deleted file mode 100644
index 39a649e..0000000
--- a/Utilities/Release/win/qt-5.12.1-win-x86-msvc-install.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-diff --git a/lib/cmake/Qt5Core/Qt5CoreConfig.cmake b/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
-index 04ec302..75d5596 100644
---- a/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
-+++ b/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
-@@ -118,7 +118,7 @@ if (NOT TARGET Qt5::Core)
-     list(REMOVE_DUPLICATES Qt5Core_COMPILE_DEFINITIONS)
-     list(REMOVE_DUPLICATES Qt5Core_EXECUTABLE_COMPILE_FLAGS)
- 
--    set(_Qt5Core_LIB_DEPENDENCIES "")
-+    set(_Qt5Core_LIB_DEPENDENCIES "${_qt5Core_install_prefix}/lib/qtpcre2.lib;netapi32.lib;version.lib")
- 
- 
-     add_library(Qt5::Core STATIC IMPORTED)
-diff --git a/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake b/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
-index a07b953..2e07371 100644
---- a/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
-+++ b/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
-@@ -118,7 +118,7 @@ if (NOT TARGET Qt5::Widgets)
-     list(REMOVE_DUPLICATES Qt5Widgets_COMPILE_DEFINITIONS)
-     list(REMOVE_DUPLICATES Qt5Widgets_EXECUTABLE_COMPILE_FLAGS)
- 
--    set(_Qt5Widgets_LIB_DEPENDENCIES "Qt5::Gui;Qt5::Core")
-+    set(_Qt5Widgets_LIB_DEPENDENCIES "Qt5::Gui;Qt5::Core;dwmapi.lib;uxtheme.lib")
- 
- 
-     add_library(Qt5::Widgets STATIC IMPORTED)
diff --git a/Utilities/Release/win/qt-5.12.1-win-x86-msvc.ps1 b/Utilities/Release/win/qt-5.12.1-win-x86-msvc.ps1
deleted file mode 100755
index d9e9617..0000000
--- a/Utilities/Release/win/qt-5.12.1-win-x86-msvc.ps1
+++ /dev/null
@@ -1,118 +0,0 @@
-# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-# file Copyright.txt or https://cmake.org/licensing for details.
-
-# Run this script on a Windows host to generate Qt binaries.
-# Set the PATH environment variable to contain the locations of cmake and git.
-
-param (
-  [string]$cmake = 'cmake',
-  [string]$git = 'git',
-  [switch]$trace
-)
-
-if ($trace -eq $true) {
-  Set-PSDebug -Trace 1
-}
-
-$ErrorActionPreference = 'Stop'
-$ProgressPreference = 'SilentlyContinue'
-
-if ($env:VSCMD_ARG_TGT_ARCH -eq "x64") {
-  $arch = "x86_64";
-} elseif ($env:VSCMD_ARG_TGT_ARCH -eq "x86") {
-  $arch = "i386";
-} else {
-  Write-Host "VSCMD_ARG_TGT_ARCH env var not recognized.  Run this from a Visual Studio Command Prompt."
-  exit 1
-}
-
-if ($env:VCToolsVersion -match '^(?<version>[0-9][0-9]\.[0-9])') {
-  $toolset = "msvc_v" + $Matches.version -replace '\.', ''
-} else {
-  Write-Host "VCToolsVersion env var not set.  Run this from a Visual Studio Command Prompt."
-}
-
-$srcname = "qt-everywhere-src-5.12.1"
-$pkgname = "qt-5.12.1-win-$arch-$toolset-1"
-$topdir = $pwd.Path
-$srcdir = Join-Path $topdir $srcname
-$blddir = Join-Path $topdir "$pkgname-build"
-$prefix = Join-Path $topdir $pkgname
-
-# JOM
-if ( -not (Test-Path -Path "jom")) {
-  Invoke-WebRequest -Uri "http://download.qt-project.org/official_releases/jom/unstable-jom.zip" -OutFile jom.zip
-  if ($(Get-FileHash "jom.zip").Hash -ne '128fdd846fe24f8594eed37d1d8929a0ea78df563537c0c1b1861a635013fff8') {
-      exit 1
-  }
-  Expand-Archive -Path jom.zip -DestinationPath jom
-  Remove-Item jom.zip
-}
-$jom = "$topdir\jom\jom.exe"
-
-# Qt Source
-if ( -not (Test-Path -Path $srcdir)) {
-  Invoke-WebRequest -Uri "https://download.qt.io/official_releases/qt/5.12/5.12.1/single/qt-everywhere-src-5.12.1.tar.xz" -OutFile qt.tar.xz
-  if ($(Get-FileHash "qt.tar.xz").Hash -ne 'caffbd625c7bc10ff8c5c7a27dbc7d84fa4de146975c0e1ffe904b514ccd6da4') {
-      exit 1
-  }
-  & $cmake -E tar xvf qt.tar.xz
-  Remove-Item qt.tar.xz
-}
-
-# Build Qt
-if ( -not (Test-Path -Path $blddir)) {
-  New-Item -ItemType Directory -Path $blddir
-  Set-Location -Path "$blddir"
-  & ..\$srcname\configure.bat `
-    -prefix $prefix `
-    -static `
-    -static-runtime `
-    -release `
-    -opensource -confirm-license `
-    -platform win32-msvc `
-    -mp `
-    -gui `
-    -widgets `
-    -qt-pcre `
-    -qt-zlib `
-    -qt-libpng `
-    -qt-libjpeg `
-    -no-gif `
-    -no-icu `
-    -no-pch `
-    -no-angle `
-    -no-opengl `
-    -no-dbus `
-    -no-harfbuzz `
-    -no-accessibility `
-    -skip declarative `
-    -skip multimedia `
-    -skip qtcanvas3d `
-    -skip qtconnectivity `
-    -skip qtdeclarative `
-    -skip qtlocation `
-    -skip qtmultimedia `
-    -skip qtsensors `
-    -skip qtserialport `
-    -skip qtsvg `
-    -skip qtwayland `
-    -skip qtwebchannel `
-    -skip qtwebengine `
-    -skip qtwebsockets `
-    -skip qtxmlpatterns `
-    -nomake examples -nomake tests
-  & $jom -J $env:NUMBER_OF_PROCESSORS
-}
-
-# Install Qt
-if ( -not (Test-Path -Path $prefix)) {
-  & $jom install
-  # Patch the installation.
-  Set-Location -Path $prefix
-  & $git apply -v (Join-Path $PSScriptRoot qt-5.12.1-win-x86-msvc-install.patch)
-}
-
-# Package Qt
-Set-Location -Path $topdir
-& $cmake -E tar cf "$pkgname.zip" "--format=zip" "$pkgname"
diff --git a/Utilities/Release/win/qt-5.15.10-win-x86-msvc-install.patch b/Utilities/Release/win/qt-5.15.10-win-x86-msvc-install.patch
new file mode 100644
index 0000000..de31d52
--- /dev/null
+++ b/Utilities/Release/win/qt-5.15.10-win-x86-msvc-install.patch
@@ -0,0 +1,26 @@
+diff --git a/lib/cmake/Qt5Core/Qt5CoreConfig.cmake b/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
+index 5bad1af..25bf3e3 100644
+--- a/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
++++ b/lib/cmake/Qt5Core/Qt5CoreConfig.cmake
+@@ -264,7 +264,7 @@ if (NOT TARGET Qt5::Core)
+         return()
+     endif()
+ 
+-    set(_Qt5Core_LIB_DEPENDENCIES "")
++    set(_Qt5Core_LIB_DEPENDENCIES "${_qt5Core_install_prefix}/lib/qtpcre2.lib;netapi32.lib;version.lib")
+ 
+ 
+     if(NOT Qt5_EXCLUDE_STATIC_DEPENDENCIES)
+diff --git a/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake b/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
+index d9966ad..308e4cb 100644
+--- a/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
++++ b/lib/cmake/Qt5Widgets/Qt5WidgetsConfig.cmake
+@@ -264,7 +264,7 @@ if (NOT TARGET Qt5::Widgets)
+         return()
+     endif()
+ 
+-    set(_Qt5Widgets_LIB_DEPENDENCIES "Qt5::Gui;Qt5::Core")
++    set(_Qt5Widgets_LIB_DEPENDENCIES "Qt5::Gui;Qt5::Core;dwmapi.lib;uxtheme.lib")
+ 
+ 
+     if(NOT Qt5_EXCLUDE_STATIC_DEPENDENCIES)
diff --git a/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1 b/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1
new file mode 100755
index 0000000..e1d9ad1
--- /dev/null
+++ b/Utilities/Release/win/qt-5.15.10-win-x86-msvc.ps1
@@ -0,0 +1,121 @@
+# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+# file Copyright.txt or https://cmake.org/licensing for details.
+
+# Run this script on a Windows host to generate Qt binaries.
+# Set the PATH environment variable to contain the locations of cmake and git.
+
+param (
+  [string]$cmake = 'cmake',
+  [string]$git = 'git',
+  [switch]$trace
+)
+
+if ($trace -eq $true) {
+  Set-PSDebug -Trace 1
+}
+
+$ErrorActionPreference = 'Stop'
+$ProgressPreference = 'SilentlyContinue'
+
+if ($env:VSCMD_ARG_TGT_ARCH -eq "x64") {
+  $arch = "x86_64";
+} elseif ($env:VSCMD_ARG_TGT_ARCH -eq "x86") {
+  $arch = "i386";
+} else {
+  Write-Host "VSCMD_ARG_TGT_ARCH env var not recognized.  Run this from a Visual Studio Command Prompt."
+  exit 1
+}
+
+if ($env:VCToolsVersion -match '^(?<version>[0-9][0-9]\.[0-9])') {
+  $toolset = "msvc_v" + $Matches.version -replace '\.', ''
+} else {
+  Write-Host "VCToolsVersion env var not set.  Run this from a Visual Studio Command Prompt."
+}
+
+$srcname = "qt-everywhere-src-5.15.10"
+$pkgname = "qt-5.15.10-win-$arch-$toolset-1"
+$topdir = $pwd.Path
+$srcdir = Join-Path $topdir $srcname
+$blddir = Join-Path $topdir "$pkgname-build"
+$prefix = Join-Path $topdir $pkgname
+
+# JOM
+if ( -not (Test-Path -Path "jom")) {
+  Invoke-WebRequest -Uri "http://download.qt-project.org/official_releases/jom/jom_1_1_4.zip" -OutFile jom.zip
+  if ($(Get-FileHash "jom.zip").Hash -ne 'd533c1ef49214229681e90196ed2094691e8c4a0a0bef0b2c901debcb562682b') {
+      Write-Host "jom hash does not match"
+      exit 1
+  }
+  Expand-Archive -Path jom.zip -DestinationPath jom
+  Remove-Item jom.zip
+}
+$jom = "$topdir\jom\jom.exe"
+
+# Qt Source
+if ( -not (Test-Path -Path $srcdir)) {
+  Invoke-WebRequest -Uri "https://download.qt.io/archive/qt/5.15/5.15.10/single/qt-everywhere-opensource-src-5.15.10.tar.xz" -OutFile qt.tar.xz
+  if ($(Get-FileHash "qt.tar.xz").Hash -ne 'b545cb83c60934adc9a6bbd27e2af79e5013de77d46f5b9f5bb2a3c762bf55ca') {
+      Write-Host "qt hash does not match"
+      exit 1
+  }
+  & $cmake -E tar xvf qt.tar.xz
+  Remove-Item qt.tar.xz
+}
+
+# Build Qt
+if ( -not (Test-Path -Path $blddir)) {
+  New-Item -ItemType Directory -Path $blddir
+  Set-Location -Path "$blddir"
+  & ..\$srcname\configure.bat `
+    -prefix $prefix `
+    -static `
+    -static-runtime `
+    -release `
+    -opensource -confirm-license `
+    -platform win32-msvc `
+    -mp `
+    -gui `
+    -widgets `
+    -qt-pcre `
+    -qt-zlib `
+    -qt-libpng `
+    -qt-libjpeg `
+    -no-gif `
+    -no-icu `
+    -no-pch `
+    -no-angle `
+    -no-opengl `
+    -no-dbus `
+    -no-harfbuzz `
+    -no-accessibility `
+    -skip declarative `
+    -skip multimedia `
+    -skip qtcanvas3d `
+    -skip qtconnectivity `
+    -skip qtdeclarative `
+    -skip qtlocation `
+    -skip qtmultimedia `
+    -skip qtsensors `
+    -skip qtserialbus `
+    -skip qtserialport `
+    -skip qtsvg `
+    -skip qtwayland `
+    -skip qtwebchannel `
+    -skip qtwebengine `
+    -skip qtwebsockets `
+    -skip qtxmlpatterns `
+    -nomake examples -nomake tests
+  & $jom -J $env:NUMBER_OF_PROCESSORS
+}
+
+# Install Qt
+if ( -not (Test-Path -Path $prefix)) {
+  & $jom install
+  # Patch the installation.
+  Set-Location -Path $prefix
+  & $git apply -v (Join-Path $PSScriptRoot qt-5.15.10-win-x86-msvc-install.patch)
+}
+
+# Package Qt
+Set-Location -Path $topdir
+& $cmake -E tar cf "$pkgname.zip" "--format=zip" "$pkgname"
diff --git a/Utilities/Scripts/update-curl.bash b/Utilities/Scripts/update-curl.bash
index 4cd75c5..a944daf 100755
--- a/Utilities/Scripts/update-curl.bash
+++ b/Utilities/Scripts/update-curl.bash
@@ -8,7 +8,7 @@
 readonly ownership="Curl Upstream <curl-library@lists.haxx.se>"
 readonly subtree="Utilities/cmcurl"
 readonly repo="https://github.com/curl/curl.git"
-readonly tag="curl-8_1_2"
+readonly tag="curl-8_4_0"
 readonly shortlog=false
 readonly paths="
   CMake/*
diff --git a/Utilities/Scripts/update-libuv.bash b/Utilities/Scripts/update-libuv.bash
index 280c684..1027436 100755
--- a/Utilities/Scripts/update-libuv.bash
+++ b/Utilities/Scripts/update-libuv.bash
@@ -8,6 +8,10 @@
 readonly ownership="libuv upstream <libuv@googlegroups.com>"
 readonly subtree="Utilities/cmlibuv"
 readonly repo="https://github.com/libuv/libuv.git"
+# We cannot import libuv 1.45 or higher because it has higher
+# minimum system requirements than we do:
+# - It requires C11 atomics from GCC 4.9+.  We support GCC 4.8.
+# - It requires Windows 8, we support Windows 7.
 readonly tag="v1.44.2"
 readonly shortlog=false
 readonly paths="
diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt
index bde6c6b..694ba3c 100644
--- a/Utilities/Sphinx/CMakeLists.txt
+++ b/Utilities/Sphinx/CMakeLists.txt
@@ -3,7 +3,7 @@
 
 if(NOT CMake_SOURCE_DIR)
   set(CMakeHelp_STANDALONE 1)
-  cmake_minimum_required(VERSION 3.13...3.25 FATAL_ERROR)
+  cmake_minimum_required(VERSION 3.13...3.26 FATAL_ERROR)
   get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH)
   get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH)
   include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake)
diff --git a/Utilities/Sphinx/conf.py.in b/Utilities/Sphinx/conf.py.in
index d4e4059..b8d8474 100644
--- a/Utilities/Sphinx/conf.py.in
+++ b/Utilities/Sphinx/conf.py.in
@@ -92,6 +92,7 @@
 
 linkcheck_allowed_redirects = {
     r'https://cdash\.org': r'https://www\.cdash\.org/',
+    r'https://cmake.org/get-involved/': r'https://cmake.org/documentation/',
     r'https://docs\.nvidia\.com/cuda/': r'https://docs\.nvidia\.com/cuda/index\.html',
     r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments': r'https://learn\.microsoft\.com/en-us/cpp/c-language/parsing-c-command-line-arguments\?.*',
     r'https://openjdk\.java\.net/jeps/313': r'https://openjdk\.org:443/jeps/313',
diff --git a/Utilities/cmThirdPartyChecks.cmake b/Utilities/cmThirdPartyChecks.cmake
index 5605667..8f68777 100644
--- a/Utilities/cmThirdPartyChecks.cmake
+++ b/Utilities/cmThirdPartyChecks.cmake
@@ -173,6 +173,7 @@
   set(HAVE_SIGNAL_H 1)
   set(HAVE_SIZEOF_ADDRESS_FAMILY 0)
   set(HAVE_SIZEOF_SA_FAMILY_T 0)
+  set(HAVE_SIZEOF_SUSECONDS_T 0)
   set(HAVE_SOCKETPAIR 0)
   set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 0)
   set(HAVE_SPAWN_H 0)
@@ -265,7 +266,6 @@
   set(HAVE_WINDOWS_H 1)
   set(HAVE_WINIOCTL_H 1)
   set(HAVE_WINSOCK2_H 1)
-  set(HAVE_WINSOCK_H 1)
   set(HAVE_WS2TCPIP_H 1)
   set(USE_WINCRYPT 1) # We do not need to build as a Windows App.
 
diff --git a/Utilities/cmbzip2/CMakeLists.txt b/Utilities/cmbzip2/CMakeLists.txt
index 1d7b265..b52358e 100644
--- a/Utilities/cmbzip2/CMakeLists.txt
+++ b/Utilities/cmbzip2/CMakeLists.txt
@@ -19,3 +19,7 @@
 add_definitions(-D_FILE_OFFSET_BITS=64)
 add_library(cmbzip2
   blocksort.c huffman.c crctable.c randtable.c compress.c decompress.c bzlib.c)
+
+if(WIN32 AND CMake_BUILD_PCH)
+  target_precompile_headers(cmbzip2 PRIVATE "bzlib.h")
+endif()
diff --git a/Utilities/cmcppdap/CMakeLists.txt b/Utilities/cmcppdap/CMakeLists.txt
index b6841f1..fe48132 100644
--- a/Utilities/cmcppdap/CMakeLists.txt
+++ b/Utilities/cmcppdap/CMakeLists.txt
@@ -37,4 +37,8 @@
   target_link_libraries(cmcppdap PRIVATE atomic)
 endif()
 
+if(CMake_BUILD_PCH)
+  target_precompile_headers(cmcppdap PRIVATE "include/dap/protocol.h")
+endif()
+
 install(FILES NOTICE DESTINATION ${CMAKE_DOC_DIR}/cmcppdap)
diff --git a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
index 142e919..8289b49 100644
--- a/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
+++ b/Utilities/cmcurl/CMake/CurlSymbolHiding.cmake
@@ -26,6 +26,12 @@
 option(CURL_HIDDEN_SYMBOLS "Set to ON to hide libcurl internal symbols (=hide all symbols that aren't officially external)." ON)
 mark_as_advanced(CURL_HIDDEN_SYMBOLS)
 
+if(WIN32 AND ENABLE_CURLDEBUG)
+  # We need to export internal debug functions (e.g. curl_dbg_*), so disable
+  # symbol hiding for debug builds.
+  set(CURL_HIDDEN_SYMBOLS OFF)
+endif()
+
 if(CURL_HIDDEN_SYMBOLS)
   set(SUPPORTS_SYMBOL_HIDING FALSE)
 
diff --git a/Utilities/cmcurl/CMake/CurlTests.c b/Utilities/cmcurl/CMake/CurlTests.c
index 3dbba3c..ea80ec8 100644
--- a/Utilities/cmcurl/CMake/CurlTests.c
+++ b/Utilities/cmcurl/CMake/CurlTests.c
@@ -21,23 +21,6 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#ifdef TIME_WITH_SYS_TIME
-/* Time with sys/time test */
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <time.h>
-
-int
-main ()
-{
-if ((struct tm *) 0)
-return 0;
-  ;
-  return 0;
-}
-
-#endif
 
 #ifdef HAVE_FCNTL_O_NONBLOCK
 
@@ -510,7 +493,7 @@
 int
 main() {
   _Atomic int i = 1;
-  i = 0;  // Force an atomic-write operation.
+  i = 0;  /* Force an atomic-write operation. */
   return i;
 }
 #endif
diff --git a/Utilities/cmcurl/CMake/FindNGTCP2.cmake b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
index ff0d49e..7ea4665 100644
--- a/Utilities/cmcurl/CMake/FindNGTCP2.cmake
+++ b/Utilities/cmcurl/CMake/FindNGTCP2.cmake
@@ -31,8 +31,10 @@
 This module accepts optional COMPONENTS to control the crypto library (these are
 mutually exclusive)::
 
-  OpenSSL:  Use libngtcp2_crypto_openssl
-  GnuTLS:   Use libngtcp2_crypto_gnutls
+  quictls, LibreSSL:  Use libngtcp2_crypto_quictls
+  BoringSSL, AWS-LC:  Use libngtcp2_crypto_boringssl
+  wolfSSL:            Use libngtcp2_crypto_wolfssl
+  GnuTLS:             Use libngtcp2_crypto_gnutls
 
 Result Variables
 ^^^^^^^^^^^^^^^^
@@ -71,7 +73,7 @@
 if(NGTCP2_FIND_COMPONENTS)
   set(NGTCP2_CRYPTO_BACKEND "")
   foreach(component IN LISTS NGTCP2_FIND_COMPONENTS)
-    if(component MATCHES "^(BoringSSL|OpenSSL|wolfSSL|GnuTLS)")
+    if(component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS)")
       if(NGTCP2_CRYPTO_BACKEND)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()
diff --git a/Utilities/cmcurl/CMake/FindNSS.cmake b/Utilities/cmcurl/CMake/FindNSS.cmake
deleted file mode 100644
index ccddf42..0000000
--- a/Utilities/cmcurl/CMake/FindNSS.cmake
+++ /dev/null
@@ -1,40 +0,0 @@
-#***************************************************************************
-#                                  _   _ ____  _
-#  Project                     ___| | | |  _ \| |
-#                             / __| | | | |_) | |
-#                            | (__| |_| |  _ <| |___
-#                             \___|\___/|_| \_\_____|
-#
-# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
-#
-# This software is licensed as described in the file COPYING, which
-# you should have received as part of this distribution. The terms
-# are also available at https://curl.se/docs/copyright.html.
-#
-# You may opt to use, copy, modify, merge, publish, distribute and/or sell
-# copies of the Software, and permit persons to whom the Software is
-# furnished to do so, under the terms of the COPYING file.
-#
-# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
-# KIND, either express or implied.
-#
-# SPDX-License-Identifier: curl
-#
-###########################################################################
-if(UNIX)
-  find_package(PkgConfig QUIET)
-  pkg_search_module(PC_NSS nss)
-endif()
-if(NOT PC_NSS_FOUND)
-  return()
-endif()
-
-set(NSS_LIBRARIES ${PC_NSS_LINK_LIBRARIES})
-set(NSS_INCLUDE_DIRS ${PC_NSS_INCLUDE_DIRS})
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(NSS
-    REQUIRED_VARS NSS_LIBRARIES NSS_INCLUDE_DIRS
-    VERSION_VAR PC_NSS_VERSION)
-
-mark_as_advanced(NSS_INCLUDE_DIRS NSS_LIBRARIES)
diff --git a/Utilities/cmcurl/CMake/OtherTests.cmake b/Utilities/cmcurl/CMake/OtherTests.cmake
index fa1e458..d67a905 100644
--- a/Utilities/cmcurl/CMake/OtherTests.cmake
+++ b/Utilities/cmcurl/CMake/OtherTests.cmake
@@ -22,6 +22,8 @@
 #
 ###########################################################################
 include(CheckCSourceCompiles)
+include(CheckCSourceRuns)
+
 # The begin of the sources (macros and includes)
 set(_source_epilogue "#undef inline")
 
@@ -38,7 +40,7 @@
   set(_source_epilogue
       "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif")
   set(signature_call_conv "PASCAL")
-  if(HAVE_LIBWS2_32)
+  if(WIN32)
     set(CMAKE_REQUIRED_LIBRARIES ws2_32)
   endif()
 else()
@@ -57,10 +59,9 @@
 
 if(NOT HAVE_WINDOWS_H)
   add_header_include(HAVE_SYS_TIME_H "sys/time.h")
-  add_header_include(TIME_WITH_SYS_TIME "time.h")
-  add_header_include(HAVE_TIME_H "time.h")
 endif()
 check_c_source_compiles("${_source_epilogue}
+#include <time.h>
 int main(void) {
   struct timeval ts;
   ts.tv_sec  = 0;
@@ -90,7 +91,6 @@
     # only try this on non-apple platforms
 
     # if not cross-compilation...
-    include(CheckCSourceRuns)
     set(CMAKE_REQUIRED_FLAGS "")
     if(HAVE_SYS_POLL_H)
       set(CMAKE_REQUIRED_FLAGS "-DHAVE_SYS_POLL_H")
@@ -134,3 +134,88 @@
   endif()
 endif()
 
+# Detect HAVE_GETADDRINFO_THREADSAFE
+
+if(WIN32)
+  set(HAVE_GETADDRINFO_THREADSAFE ${HAVE_GETADDRINFO})
+elseif(NOT HAVE_GETADDRINFO)
+  set(HAVE_GETADDRINFO_THREADSAFE FALSE)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
+       CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "HP-UX" OR
+       CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "SunOS")
+  set(HAVE_GETADDRINFO_THREADSAFE TRUE)
+elseif(CMAKE_SYSTEM_NAME MATCHES "BSD")
+  set(HAVE_GETADDRINFO_THREADSAFE FALSE)
+endif()
+
+if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
+
+  set(_save_epilogue "${_source_epilogue}")
+  set(_source_epilogue "#undef inline")
+
+  add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
+  add_header_include(HAVE_SYS_TIME_H "sys/time.h")
+  add_header_include(HAVE_NETDB_H "netdb.h")
+
+  check_c_source_compiles("${_source_epilogue}
+    int main(void)
+    {
+    #ifdef h_errno
+      return 0;
+    #else
+      force compilation error
+    #endif
+    }" HAVE_H_ERRNO)
+
+  if(NOT HAVE_H_ERRNO)
+    check_c_source_runs("${_source_epilogue}
+      int main(void)
+      {
+        h_errno = 2;
+        return h_errno != 0 ? 1 : 0;
+      }" HAVE_H_ERRNO_ASSIGNABLE)
+
+    if(NOT HAVE_H_ERRNO_ASSIGNABLE)
+      check_c_source_compiles("${_source_epilogue}
+        int main(void)
+        {
+        #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
+          return 0;
+        #elif defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 700)
+          return 0;
+        #else
+          force compilation error
+        #endif
+        }" HAVE_H_ERRNO_SBS_ISSUE_7)
+    endif()
+  endif()
+
+  if(HAVE_H_ERRNO OR HAVE_H_ERRNO_ASSIGNABLE OR HAVE_H_ERRNO_SBS_ISSUE_7)
+    set(HAVE_GETADDRINFO_THREADSAFE TRUE)
+  endif()
+
+  set(_source_epilogue "${_save_epilogue}")
+endif()
+
+if(NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
+  set(_save_epilogue "${_source_epilogue}")
+  set(_source_epilogue "#undef inline")
+
+  add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
+  add_header_include(HAVE_SYS_TIME_H "sys/time.h")
+
+  check_c_source_compiles("${_source_epilogue}
+    #include <time.h>
+    int main(void)
+    {
+      struct timespec ts;
+      (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+      return 0;
+    }" HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
+
+  set(_source_epilogue "${_save_epilogue}")
+endif()
diff --git a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
index 3771237..5daec0e 100644
--- a/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
+++ b/Utilities/cmcurl/CMake/Platforms/WindowsCache.cmake
@@ -23,41 +23,80 @@
 ###########################################################################
 if(NOT UNIX)
   if(WIN32)
+
+    set(HAVE_WINDOWS_H 1)
+    set(HAVE_WS2TCPIP_H 1)
+    set(HAVE_WINSOCK2_H 1)
+
+    if(MINGW)
+      set(HAVE_SNPRINTF 1)
+      set(HAVE_UNISTD_H 1)
+      set(HAVE_INTTYPES_H 1)
+      set(HAVE_STRTOLL 1)
+      set(HAVE_BASENAME 1)
+    elseif(MSVC)
+      if(NOT MSVC_VERSION LESS 1800)
+        set(HAVE_INTTYPES_H 1)
+        set(HAVE_STRTOLL 1)
+      else()
+        set(HAVE_INTTYPES_H 0)
+        set(HAVE_STRTOLL 0)
+      endif()
+      if(NOT MSVC_VERSION LESS 1900)
+        set(HAVE_SNPRINTF 1)
+      else()
+        set(HAVE_SNPRINTF 0)
+      endif()
+      set(HAVE_BASENAME 0)
+    endif()
+
     set(HAVE_LIBSOCKET 0)
     set(HAVE_GETHOSTNAME 1)
     set(HAVE_LIBZ 0)
 
+    set(HAVE_ARC4RANDOM 0)
+    set(HAVE_FNMATCH 0)
+    set(HAVE_SCHED_YIELD 0)
     set(HAVE_ARPA_INET_H 0)
     set(HAVE_FCNTL_H 1)
+    set(HAVE_IFADDRS_H 0)
     set(HAVE_IO_H 1)
     set(HAVE_NETDB_H 0)
     set(HAVE_NETINET_IN_H 0)
+    set(HAVE_NETINET_TCP_H 0)
+    set(HAVE_NETINET_UDP_H 0)
     set(HAVE_NET_IF_H 0)
+    set(HAVE_IOCTL_SIOCGIFADDR 0)
+    set(HAVE_POLL_H 0)
+    set(HAVE_POLL_FINE 0)
     set(HAVE_PWD_H 0)
-    set(HAVE_SETJMP_H 1)
-    set(HAVE_SIGNAL_H 1)
-    set(HAVE_STDLIB_H 1)
     set(HAVE_STRINGS_H 0)
-    set(HAVE_STRING_H 1)
+    set(HAVE_SYS_FILIO_H 0)
+    set(HAVE_SYS_WAIT_H 0)
+    set(HAVE_SYS_IOCTL_H 0)
     set(HAVE_SYS_PARAM_H 0)
     set(HAVE_SYS_POLL_H 0)
+    set(HAVE_SYS_RESOURCE_H 0)
     set(HAVE_SYS_SELECT_H 0)
     set(HAVE_SYS_SOCKET_H 0)
     set(HAVE_SYS_SOCKIO_H 0)
     set(HAVE_SYS_STAT_H 1)
     set(HAVE_SYS_TIME_H 0)
     set(HAVE_SYS_TYPES_H 1)
+    set(HAVE_SYS_UN_H 0)
     set(HAVE_SYS_UTIME_H 1)
     set(HAVE_TERMIOS_H 0)
     set(HAVE_TERMIO_H 0)
-    set(HAVE_TIME_H 1)
     set(HAVE_UTIME_H 0)
 
+    set(HAVE_FSEEKO 0)
+    set(HAVE__FSEEKI64 1)
     set(HAVE_SOCKET 1)
     set(HAVE_SELECT 1)
     set(HAVE_STRDUP 1)
     set(HAVE_STRICMP 1)
     set(HAVE_STRCMPI 1)
+    set(HAVE_MEMRCHR 0)
     set(HAVE_GETTIMEOFDAY 0)
     set(HAVE_CLOSESOCKET 1)
     set(HAVE_SIGSETJMP 0)
@@ -66,10 +105,14 @@
     set(HAVE_GETPWUID 0)
     set(HAVE_GETEUID 0)
     set(HAVE_UTIME 1)
-    set(HAVE_RAND_EGD 0)
     set(HAVE_GMTIME_R 0)
+    set(HAVE_CLOCK_GETTIME_MONOTONIC_RAW 0)
     set(HAVE_GETHOSTBYNAME_R 0)
     set(HAVE_SIGNAL 1)
+    set(HAVE_LINUX_TCP_H 0)
+    set(HAVE_GLIBC_STRERROR_R 0)
+    set(HAVE_MACH_ABSOLUTE_TIME 0)
+    set(HAVE_GETIFADDRS 0)
 
     set(HAVE_GETHOSTBYNAME_R_3 0)
     set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
@@ -78,7 +121,6 @@
     set(HAVE_GETHOSTBYNAME_R_6 0)
     set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
 
-    set(TIME_WITH_SYS_TIME 0)
     set(HAVE_O_NONBLOCK 0)
     set(HAVE_IN_ADDR_T 0)
     set(STDC_HEADERS 1)
diff --git a/Utilities/cmcurl/CMake/curl-config.cmake.in b/Utilities/cmcurl/CMake/curl-config.cmake.in
index dbe4ed2..056907c 100644
--- a/Utilities/cmcurl/CMake/curl-config.cmake.in
+++ b/Utilities/cmcurl/CMake/curl-config.cmake.in
@@ -33,3 +33,6 @@
 
 include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
 check_required_components("@PROJECT_NAME@")
+
+# Alias for either shared or static library
+add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
diff --git a/Utilities/cmcurl/CMakeLists.txt b/Utilities/cmcurl/CMakeLists.txt
index 82f9b0b..9387247 100644
--- a/Utilities/cmcurl/CMakeLists.txt
+++ b/Utilities/cmcurl/CMakeLists.txt
@@ -2,7 +2,9 @@
 set(BUILD_CURL_EXE OFF CACHE INTERNAL "No curl exe")
 set(BUILD_DASHBOARD_REPORTS OFF CACHE INTERNAL "No curl dashboard reports")
 set(BUILD_RELEASE_DEBUG_DIRS OFF CACHE INTERNAL "No curl release/debug dirs")
-set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Build shared libraries")
+set(BUILD_SHARED_LIBS OFF)
+set(BUILD_STATIC_LIBS ON)
+set(BUILD_STATIC_CURL OFF)
 set(CURL_USE_BEARSSL OFF)
 set(CURL_USE_GSSAPI OFF)
 set(CURL_USE_LIBPSL OFF)
@@ -17,23 +19,29 @@
 set(CURL_USE_WOLFSSL OFF)
 set(CURL_BROTLI OFF)
 set(CURL_DISABLE_ALTSVC ON)
+set(CURL_DISABLE_AWS OFF)
+set(CURL_DISABLE_BASIC_AUTH OFF)
+set(CURL_DISABLE_BEARER_AUTH OFF)
 set(CURL_DISABLE_COOKIES OFF CACHE INTERNAL "Do not disable curl cookie support")
-set(CURL_DISABLE_CRYPTO_AUTH OFF CACHE INTERNAL "Do not disable curl crypto auth")
 set(CURL_DISABLE_DICT ON CACHE INTERNAL "Disable curl dict protocol?")
+set(CURL_DISABLE_DIGEST_AUTH OFF)
 set(CURL_DISABLE_DOH OFF)
 set(CURL_DISABLE_FILE OFF CACHE INTERNAL "Disable curl file protocol?")
+set(CURL_DISABLE_FORM_API OFF)
 set(CURL_DISABLE_FTP OFF CACHE INTERNAL "Disable curl ftp protocol?")
 set(CURL_DISABLE_GETOPTIONS OFF)
 set(CURL_DISABLE_GOPHER ON CACHE INTERNAL "Disable curl gopher protocol?")
-set(CURL_DISABLE_HSTS ON)
+set(CURL_DISABLE_HSTS OFF)
 set(CURL_DISABLE_HTTP_AUTH OFF)
 set(CURL_DISABLE_HTTP OFF CACHE INTERNAL "Disable curl http protocol?")
 set(CURL_DISABLE_IMAP ON CACHE INTERNAL "Disable curl imap protocol?")
+set(CURL_DISABLE_KERBEROS_AUTH OFF)
 set(CURL_DISABLE_LDAP ON CACHE INTERNAL "Disable curl ldap protocol?")
 set(CURL_DISABLE_LDAPS ON CACHE INTERNAL "Disable curl ldaps protocol?")
 set(CURL_DISABLE_LIBCURL_OPTION OFF)
 set(CURL_DISABLE_MIME OFF)
 set(CURL_DISABLE_MQTT ON)
+set(CURL_DISABLE_NEGOTIATE_AUTH OFF)
 set(CURL_DISABLE_NETRC OFF)
 set(CURL_DISABLE_NTLM OFF)
 set(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG OFF)
@@ -46,6 +54,7 @@
 set(CURL_DISABLE_SMB OFF)
 set(CURL_DISABLE_SMTP ON CACHE INTERNAL "Disable curl smtp protocol?")
 set(CURL_DISABLE_SOCKETPAIR OFF)
+set(CURL_DISABLE_SRP OFF)
 set(CURL_DISABLE_TELNET ON CACHE INTERNAL "Disable curl telnet protocol?")
 set(CURL_DISABLE_TFTP ON CACHE INTERNAL "Disable curl tftp protocol?")
 set(CURL_DISABLE_VERBOSE_STRINGS OFF CACHE INTERNAL "Do not disable curl verbosity")
@@ -77,6 +86,7 @@
 set(HAVE_WIN32_WINNT 0) # we do not need this info
 set(HTTP_ONLY OFF CACHE INTERNAL "Curl is not http-only")
 set(PICKY_COMPILER OFF CACHE INTERNAL "Enable picky compiler options")
+set(SHARE_LIB_OBJECT OFF)
 set(USE_LIBIDN2 ON)
 set(USE_NGHTTP2 ON)
 set(USE_NGTCP2 OFF)
@@ -194,10 +204,9 @@
 # https://cmake.org/cmake/help/latest/module/FetchContent.html#integrating-with-find-package
 #
 # The following variables are available:
-#   HAVE_RAND_EGD: `RAND_egd` present in OpenSSL
-#   HAVE_AWSLC: OpenSSL is AWS-LC
-#   HAVE_BORINGSSL: OpenSSL is BoringSSL
-#   HAVE_PK11_CREATEMANAGEDGENERICOBJECTL: `PK11_CreateManagedGenericObject` present in NSS
+#   HAVE_SSL_SET0_WBIO: `SSL_set0_wbio` present in OpenSSL/wolfSSL
+#   HAVE_OPENSSL_SRP: `SSL_CTX_set_srp_username` present in OpenSSL/wolfSSL
+#   HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS
 #   HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL
 #   HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
 #   HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd
@@ -242,10 +251,14 @@
 
 include_directories(${CURL_SOURCE_DIR}/include)
 
+set(CMAKE_UNITY_BUILD_BATCH_SIZE 0)
+
 option(CURL_WERROR "Turn compiler warnings into errors" OFF)
 option(PICKY_COMPILER "Enable picky compiler options" ON)
 option(BUILD_CURL_EXE "Set to ON to build curl executable." ON)
 option(BUILD_SHARED_LIBS "Build shared libraries" ON)
+option(BUILD_STATIC_LIBS "Build static libraries" OFF)
+option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF)
 option(ENABLE_ARES "Set to ON to enable c-ares support" OFF)
 if(WIN32)
   option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF)
@@ -295,6 +308,32 @@
 endif()
 endif()
 
+set(LIB_STATIC "libcurl_static")
+set(LIB_SHARED "libcurl_shared")
+
+if(NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
+  set(BUILD_STATIC_LIBS ON)
+endif()
+if(NOT BUILD_STATIC_CURL AND NOT BUILD_SHARED_LIBS)
+  set(BUILD_STATIC_CURL ON)
+elseif(BUILD_STATIC_CURL AND NOT BUILD_STATIC_LIBS)
+  set(BUILD_STATIC_CURL OFF)
+endif()
+
+# lib flavour selected for curl tool
+if(BUILD_STATIC_CURL)
+  set(LIB_SELECTED_FOR_EXE ${LIB_STATIC})
+else()
+  set(LIB_SELECTED_FOR_EXE ${LIB_SHARED})
+endif()
+
+# lib flavour selected for example and test programs.
+if(BUILD_SHARED_LIBS)
+  set(LIB_SELECTED ${LIB_SHARED})
+else()
+  set(LIB_SELECTED ${LIB_STATIC})
+endif()
+
 # initialize CURL_LIBS
 set(CURL_LIBS "")
 
@@ -313,16 +352,33 @@
 
 option(CURL_DISABLE_ALTSVC "disables alt-svc support" OFF)
 mark_as_advanced(CURL_DISABLE_ALTSVC)
+option(CURL_DISABLE_SRP "disables TLS-SRP support" OFF)
+mark_as_advanced(CURL_DISABLE_SRP)
 option(CURL_DISABLE_COOKIES "disables cookies support" OFF)
 mark_as_advanced(CURL_DISABLE_COOKIES)
-option(CURL_DISABLE_CRYPTO_AUTH "disables cryptographic authentication" OFF)
-mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH)
+option(CURL_DISABLE_BASIC_AUTH "disables Basic authentication" OFF)
+mark_as_advanced(CURL_DISABLE_BASIC_AUTH)
+option(CURL_DISABLE_BEARER_AUTH "disables Bearer authentication" OFF)
+mark_as_advanced(CURL_DISABLE_BEARER_AUTH)
+option(CURL_DISABLE_DIGEST_AUTH "disables Digest authentication" OFF)
+mark_as_advanced(CURL_DISABLE_DIGEST_AUTH)
+option(CURL_DISABLE_KERBEROS_AUTH "disables Kerberos authentication" OFF)
+mark_as_advanced(CURL_DISABLE_KERBEROS_AUTH)
+option(CURL_DISABLE_NEGOTIATE_AUTH "disables negotiate authentication" OFF)
+mark_as_advanced(CURL_DISABLE_NEGOTIATE_AUTH)
+option(CURL_DISABLE_AWS "disables AWS-SIG4" OFF)
+mark_as_advanced(CURL_DISABLE_AWS)
 option(CURL_DISABLE_DICT "disables DICT" OFF)
 mark_as_advanced(CURL_DISABLE_DICT)
 option(CURL_DISABLE_DOH "disables DNS-over-HTTPS" OFF)
 mark_as_advanced(CURL_DISABLE_DOH)
 option(CURL_DISABLE_FILE "disables FILE" OFF)
 mark_as_advanced(CURL_DISABLE_FILE)
+if(0) # This code not needed for building within CMake.
+cmake_dependent_option(CURL_DISABLE_FORM_API "disables form api" OFF
+                       "NOT CURL_DISABLE_MIME" ON)
+mark_as_advanced(CURL_DISABLE_FORM_API)
+endif()
 option(CURL_DISABLE_FTP "disables FTP" OFF)
 mark_as_advanced(CURL_DISABLE_FTP)
 option(CURL_DISABLE_GETOPTIONS "disables curl_easy_options API for existing options to curl_easy_setopt" OFF)
@@ -413,7 +469,7 @@
   endif()
 
   if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT ENABLE_ARES)
-    set(use_core_foundation ON)
+    set(use_core_foundation_and_core_services ON)
 
     find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
     if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
@@ -494,43 +550,35 @@
 check_function_exists(gethostname HAVE_GETHOSTNAME)
 
 if(WIN32)
-  check_library_exists_concat("ws2_32" getch        HAVE_LIBWS2_32)
-  check_library_exists_concat("winmm"  getch        HAVE_LIBWINMM)
-
-  # Matching logic used for Curl_win32_random()
-  if(MINGW)
-    check_c_source_compiles("
-      #include <_mingw.h>
-      #if defined(__MINGW64_VERSION_MAJOR)
-      #error
-      #endif
-      int main(void) {
-        return 0;
-      }"
-      HAVE_MINGW_ORIGINAL)
+  list(APPEND CURL_LIBS "ws2_32")
+  if(USE_LIBRTMP)
+    list(APPEND CURL_LIBS "winmm")
   endif()
 endif()
 
 if(0) # This code not needed for building within CMake.
 # check SSL libraries
-# TODO support GnuTLS
 option(CURL_ENABLE_SSL "Enable SSL support" ON)
 
+if(CURL_DEFAULT_SSL_BACKEND)
+  set(valid_default_ssl_backend FALSE)
+endif()
+
 if(APPLE)
-  cmake_dependent_option(CURL_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+  cmake_dependent_option(CURL_USE_SECTRANSP "Enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 endif()
 if(WIN32)
-  cmake_dependent_option(CURL_USE_SCHANNEL "enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+  cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
   cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without OpenSSL" ON
     CURL_USE_SCHANNEL OFF)
 endif()
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
-cmake_dependent_option(CURL_USE_NSS "Enable NSS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
-cmake_dependent_option(CURL_USE_WOLFSSL "enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 
 set(openssl_default ON)
-if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_NSS OR CURL_USE_WOLFSSL)
+if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL)
   set(openssl_default OFF)
 endif()
 cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF)
@@ -543,7 +591,6 @@
   CURL_USE_OPENSSL
   CURL_USE_MBEDTLS
   CURL_USE_BEARSSL
-  CURL_USE_NSS
   CURL_USE_WOLFSSL
 )
 if(enabled_ssl_options_count GREATER "1")
@@ -554,13 +601,17 @@
   set(SSL_ENABLED ON)
   set(USE_SCHANNEL ON) # Windows native SSL/TLS support
   set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "schannel")
+    set(valid_default_ssl_backend TRUE)
+  endif()
 endif()
 if(CURL_WINDOWS_SSPI)
   set(USE_WINDOWS_SSPI ON)
 endif()
 
 if(CURL_USE_SECTRANSP)
-  set(use_core_foundation ON)
+  set(use_core_foundation_and_core_services ON)
 
   find_library(SECURITY_FRAMEWORK "Security")
   if(NOT SECURITY_FRAMEWORK)
@@ -570,15 +621,24 @@
   set(SSL_ENABLED ON)
   set(USE_SECTRANSP ON)
   list(APPEND CURL_LIBS "-framework Security")
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "secure-transport")
+    set(valid_default_ssl_backend TRUE)
+  endif()
 endif()
 
-if(use_core_foundation)
+if(use_core_foundation_and_core_services)
   find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation")
+  find_library(CORESERVICES_FRAMEWORK "CoreServices")
+
   if(NOT COREFOUNDATION_FRAMEWORK)
       message(FATAL_ERROR "CoreFoundation framework not found")
   endif()
+  if(NOT CORESERVICES_FRAMEWORK)
+      message(FATAL_ERROR "CoreServices framework not found")
+  endif()
 
-  list(APPEND CURL_LIBS "-framework CoreFoundation")
+  list(APPEND CURL_LIBS "-framework CoreFoundation -framework CoreServices")
 endif()
 
 if(CURL_USE_OPENSSL)
@@ -595,15 +655,14 @@
 
   if(WIN32)
     list(APPEND CURL_LIBS "ws2_32")
-    if(NOT HAVE_MINGW_ORIGINAL)
-      list(APPEND CURL_LIBS "bcrypt")  # for OpenSSL/LibreSSL
-    endif()
+    list(APPEND CURL_LIBS "bcrypt")  # for OpenSSL/LibreSSL
+  endif()
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl")
+    set(valid_default_ssl_backend TRUE)
   endif()
 
   set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
-  if(NOT DEFINED HAVE_RAND_EGD)
-    check_symbol_exists(RAND_egd "${CURL_INCLUDES}" HAVE_RAND_EGD)
-  endif()
   if(NOT DEFINED HAVE_BORINGSSL)
     check_symbol_exists(OPENSSL_IS_BORINGSSL "openssl/base.h" HAVE_BORINGSSL)
   endif()
@@ -627,6 +686,10 @@
   set(USE_MBEDTLS ON)
   list(APPEND CURL_LIBS ${MBEDTLS_LIBRARIES})
   include_directories(${MBEDTLS_INCLUDE_DIRS})
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "mbedtls")
+    set(valid_default_ssl_backend TRUE)
+  endif()
 endif()
 
 if(CURL_USE_BEARSSL)
@@ -635,6 +698,10 @@
   set(USE_BEARSSL ON)
   list(APPEND CURL_LIBS ${BEARSSL_LIBRARY})
   include_directories(${BEARSSL_INCLUDE_DIRS})
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "bearssl")
+    set(valid_default_ssl_backend TRUE)
+  endif()
 endif()
 
 if(CURL_USE_WOLFSSL)
@@ -643,25 +710,38 @@
   set(USE_WOLFSSL ON)
   list(APPEND CURL_LIBS ${WolfSSL_LIBRARIES})
   include_directories(${WolfSSL_INCLUDE_DIRS})
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "wolfssl")
+    set(valid_default_ssl_backend TRUE)
+  endif()
 endif()
 
-if(CURL_USE_NSS)
-  find_package(NSS REQUIRED)
-  include_directories(${NSS_INCLUDE_DIRS})
-  list(APPEND CURL_LIBS ${NSS_LIBRARIES})
+if(CURL_USE_GNUTLS)
+  find_package(GnuTLS REQUIRED)
   set(SSL_ENABLED ON)
-  set(USE_NSS ON)
-  if(NOT DEFINED HAVE_PK11_CREATEMANAGEDGENERICOBJECT)
+  set(USE_GNUTLS ON)
+  list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} "nettle")
+  include_directories(${GNUTLS_INCLUDE_DIRS})
+
+  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "gnutls")
+    set(valid_default_ssl_backend TRUE)
+  endif()
+
+  if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP)
     cmake_push_check_state()
-    set(CMAKE_REQUIRED_INCLUDES ${NSS_INCLUDE_DIRS})
-    set(CMAKE_REQUIRED_LIBRARIES ${NSS_LIBRARIES})
-    check_symbol_exists(PK11_CreateManagedGenericObject "pk11pub.h" HAVE_PK11_CREATEMANAGEDGENERICOBJECT)
+    set(CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS})
+    set(CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES})
+    check_symbol_exists(gnutls_srp_verifier "gnutls/gnutls.h" HAVE_GNUTLS_SRP)
     cmake_pop_check_state()
   endif()
 endif()
 
+if(CURL_DEFAULT_SSL_BACKEND AND NOT valid_default_ssl_backend)
+  message(FATAL_ERROR "CURL_DEFAULT_SSL_BACKEND '${CURL_DEFAULT_SSL_BACKEND}' not enabled.")
+endif()
+
 # Keep ZLIB detection after TLS detection,
-# and before calling CheckQuicSupportInOpenSSL.
+# and before calling openssl_check_symbol_exists().
 
 set(HAVE_LIBZ OFF)
 set(USE_ZLIB OFF)
@@ -689,9 +769,10 @@
 option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF)
 set(HAVE_BROTLI OFF)
 if(CURL_BROTLI)
-  find_package(Brotli QUIET)
+  find_package(Brotli REQUIRED)
   if(BROTLI_FOUND)
     set(HAVE_BROTLI ON)
+    set(CURL_LIBS "${BROTLI_LIBRARIES};${CURL_LIBS}")  # For 'ld' linker. Emulate `list(PREPEND ...)` to stay compatible with <v3.15 CMake.
     list(APPEND CURL_LIBS ${BROTLI_LIBRARIES})
     include_directories(${BROTLI_INCLUDE_DIRS})
     list(APPEND CMAKE_REQUIRED_INCLUDES ${BROTLI_INCLUDE_DIRS})
@@ -702,7 +783,7 @@
 set(HAVE_ZSTD OFF)
 if(CURL_ZSTD)
   find_package(Zstd REQUIRED)
-  if (NOT DEFINED HAVE_ZSTD_CREATEDSTREAM)
+  if(NOT DEFINED HAVE_ZSTD_CREATEDSTREAM)
     cmake_push_check_state()
     set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS})
     set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES})
@@ -716,63 +797,74 @@
   endif()
 endif()
 
-option(USE_NGHTTP2 "Use Nghttp2 library" OFF)
+# Check symbol in OpenSSL-like TLS backends.
+macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE)
+  cmake_push_check_state()
+  if(USE_OPENSSL)
+    set(CMAKE_REQUIRED_INCLUDES   "${OPENSSL_INCLUDE_DIR}")
+    set(CMAKE_REQUIRED_LIBRARIES  "${OPENSSL_LIBRARIES}")
+    if(HAVE_LIBZ)
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
+    endif()
+    if(WIN32)
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt")  # for OpenSSL/LibreSSL
+    endif()
+  elseif(USE_WOLFSSL)
+    set(CMAKE_REQUIRED_INCLUDES   "${WolfSSL_INCLUDE_DIRS}")
+    set(CMAKE_REQUIRED_LIBRARIES  "${WolfSSL_LIBRARIES}")
+    if(HAVE_LIBZ)
+      list(APPEND CMAKE_REQUIRED_INCLUDES  "${ZLIB_INCLUDE_DIRS}")  # Public wolfSSL headers require zlib headers
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
+    endif()
+    if(WIN32)
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32")
+    endif()
+    list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T)  # to pull in stdint.h (as of wolfSSL v5.5.4)
+  endif()
+  check_symbol_exists("${SYMBOL}" "${FILES}" "${VARIABLE}")
+  cmake_pop_check_state()
+endmacro()
+
+if(USE_OPENSSL OR USE_WOLFSSL)
+  if(NOT DEFINED HAVE_SSL_SET0_WBIO)
+    openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO)
+  endif()
+  if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP)
+    openssl_check_symbol_exists(SSL_CTX_set_srp_username "openssl/ssl.h" HAVE_OPENSSL_SRP)
+  endif()
+endif()
+
+option(USE_NGHTTP2 "Use nghttp2 library" OFF)
 if(USE_NGHTTP2)
   find_package(NGHTTP2 REQUIRED)
   include_directories(${NGHTTP2_INCLUDE_DIRS})
   list(APPEND CURL_LIBS ${NGHTTP2_LIBRARIES})
 endif()
 
-function(CheckQuicSupportInOpenSSL)
-  # Be sure that the OpenSSL/wolfSSL library actually supports QUIC.
-  if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
-    cmake_push_check_state()
-    if(USE_WOLFSSL)
-      set(CMAKE_REQUIRED_INCLUDES   "${WolfSSL_INCLUDE_DIRS}")
-      set(CMAKE_REQUIRED_LIBRARIES  "${WolfSSL_LIBRARIES}")
-      if(HAVE_LIBZ)
-        list(APPEND CMAKE_REQUIRED_INCLUDES  "${ZLIB_INCLUDE_DIRS}")  # Public wolfSSL headers require zlib headers
-        list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
-      endif()
-      if(WIN32)
-        list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32")
-      endif()
-      list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T)  # to pull in stdint.h (as of wolfSSL v5.5.4)
-      check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
-    else()
-      set(CMAKE_REQUIRED_INCLUDES   "${OPENSSL_INCLUDE_DIR}")
-      set(CMAKE_REQUIRED_LIBRARIES  "${OPENSSL_LIBRARIES}")
-      if(HAVE_LIBZ)
-        list(APPEND CMAKE_REQUIRED_LIBRARIES "${ZLIB_LIBRARIES}")
-      endif()
-      if(WIN32)
-        list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
-        if(NOT HAVE_MINGW_ORIGINAL)
-          list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt")  # for OpenSSL/LibreSSL
-        endif()
-      endif()
-      check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
-    endif()
-    cmake_pop_check_state()
-  endif()
-  if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
-    message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR")
-  endif()
-endfunction()
-
 option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF)
 if(USE_NGTCP2)
   if(USE_OPENSSL OR USE_WOLFSSL)
     if(USE_WOLFSSL)
       find_package(NGTCP2 REQUIRED wolfSSL)
-    elseif(HAVE_BORINGSSL)
+    elseif(HAVE_BORINGSSL OR HAVE_AWSLC)
       find_package(NGTCP2 REQUIRED BoringSSL)
     else()
-      find_package(NGTCP2 REQUIRED OpenSSL)
+      find_package(NGTCP2 REQUIRED quictls)
     endif()
-    CheckQuicSupportInOpenSSL()
+
+    # Be sure that the OpenSSL/wolfSSL library actually supports QUIC.
+    if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
+      if(USE_OPENSSL)
+        openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+      elseif(USE_WOLFSSL)
+        openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+      endif()
+    endif()
+    if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
+      message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR")
+    endif()
   elseif(USE_GNUTLS)
-    # TODO add GnuTLS support as vtls library.
     find_package(NGTCP2 REQUIRED GnuTLS)
   else()
     message(FATAL_ERROR "ngtcp2 requires OpenSSL, wolfSSL or GnuTLS")
@@ -816,32 +908,26 @@
   list(APPEND CURL_LIBS ${MSH3_LIBRARIES})
 endif()
 
+if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP))
+  set(USE_TLS_SRP 1)
+endif()
+
 if(NOT CURL_DISABLE_LDAP)
   if(WIN32)
     option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON)
     if(USE_WIN32_LDAP)
-      check_library_exists_concat("wldap32" cldap_open HAVE_WLDAP32)
-      if(NOT HAVE_WLDAP32)
-        set(USE_WIN32_LDAP OFF)
-      elseif(NOT CURL_DISABLE_LDAPS)
+      list(APPEND CURL_LIBS "wldap32")
+      if(NOT CURL_DISABLE_LDAPS)
         set(HAVE_LDAP_SSL ON)
       endif()
     endif()
   endif()
 
-  option(CURL_USE_OPENLDAP "Use OpenLDAP code." OFF)
-  mark_as_advanced(CURL_USE_OPENLDAP)
   set(CMAKE_LDAP_LIB "ldap" CACHE STRING "Name or full path to ldap library")
   set(CMAKE_LBER_LIB "lber" CACHE STRING "Name or full path to lber library")
 
-  if(CURL_USE_OPENLDAP AND USE_WIN32_LDAP)
-    message(FATAL_ERROR "Cannot use USE_WIN32_LDAP and CURL_USE_OPENLDAP at the same time")
-  endif()
-
   # Now that we know, we're not using windows LDAP...
-  if(USE_WIN32_LDAP)
-    check_include_file_concat("winldap.h" HAVE_WINLDAP_H)
-  else()
+  if(NOT USE_WIN32_LDAP)
     # Check for LDAP
     set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
     check_library_exists_concat(${CMAKE_LDAP_LIB} ldap_init HAVE_LIBLDAP)
@@ -864,9 +950,6 @@
       set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
       set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) #LDAP includes won't be used
     else()
-      if(CURL_USE_OPENLDAP)
-        set(USE_OPENLDAP ON)
-      endif()
       if(CMAKE_LDAP_INCLUDE_DIR)
         include_directories(${CMAKE_LDAP_INCLUDE_DIR})
       endif()
@@ -880,13 +963,18 @@
       endif()
       list(APPEND _HEADER_LIST "ldap.h")
 
-      set(_SRC_STRING "")
+      set(_INCLUDE_STRING "")
       foreach(_HEADER ${_HEADER_LIST})
         set(_INCLUDE_STRING "${_INCLUDE_STRING}#include <${_HEADER}>\n")
       endforeach()
 
-      set(_SRC_STRING
-        "
+      list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1)
+      list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB})
+      if(HAVE_LIBLBER)
+        list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
+      endif()
+
+      check_c_source_compiles("
         ${_INCLUDE_STRING}
         int main(int argc, char ** argv)
         {
@@ -894,21 +982,27 @@
           BerElement *bep = ber_init(bvp);
           ber_free(bep, 1);
           return 0;
-        }"
-      )
-      list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1)
-      list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB})
-      if(HAVE_LIBLBER)
-        list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
-      endif()
-      check_c_source_compiles("${_SRC_STRING}" NOT_NEED_LBER_H)
-      unset(CMAKE_REQUIRED_LIBRARIES)
-
+        }" NOT_NEED_LBER_H)
       if(NOT_NEED_LBER_H)
         set(NEED_LBER_H OFF)
       else()
         set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -DNEED_LBER_H")
       endif()
+
+      check_function_exists(ldap_url_parse HAVE_LDAP_URL_PARSE)
+      check_function_exists(ldap_init_fd HAVE_LDAP_INIT_FD)
+
+      unset(CMAKE_REQUIRED_LIBRARIES)
+
+      check_include_file("ldap_ssl.h" HAVE_LDAP_SSL_H)
+
+      if(HAVE_LDAP_INIT_FD)
+        set(USE_OPENLDAP ON)
+        add_definitions("-DLDAP_DEPRECATED=1")
+      endif()
+      if(NOT CURL_DISABLE_LDAPS)
+        set(HAVE_LDAP_SSL ON)
+      endif()
     endif()
   endif()
 endif()
@@ -921,10 +1015,6 @@
   endif()
 endif()
 
-if(NOT CURL_DISABLE_LDAPS)
-  check_include_file_concat("ldap_ssl.h" HAVE_LDAP_SSL_H)
-endif()
-
 # Check for idn2
 option(USE_LIBIDN2 "Use libidn2 for IDN support" ON)
 if(USE_LIBIDN2)
@@ -1089,7 +1179,7 @@
   unset(CURL_CA_PATH CACHE)
 elseif("${CURL_CA_PATH}" STREQUAL "auto")
   unset(CURL_CA_PATH CACHE)
-  if(NOT CMAKE_CROSSCOMPILING AND NOT USE_NSS)
+  if(NOT CMAKE_CROSSCOMPILING)
     set(CURL_CA_PATH_AUTODETECT TRUE)
   endif()
 else()
@@ -1131,9 +1221,13 @@
   endif()
 endif()
 
-if(CURL_CA_PATH_SET AND NOT USE_OPENSSL AND NOT USE_MBEDTLS)
+if(CURL_CA_PATH_SET AND
+   NOT USE_OPENSSL AND
+   NOT USE_WOLFSSL AND
+   NOT USE_GNUTLS AND
+   NOT USE_MBEDTLS)
   message(STATUS
-          "CA path only supported by OpenSSL, GnuTLS or mbed TLS. "
+          "CA path only supported by OpenSSL, wolfSSL, GnuTLS or mbedTLS. "
           "Set CURL_CA_PATH=none or enable one of those TLS backends.")
 endif()
 endif()
@@ -1143,16 +1237,15 @@
   check_include_file_concat("windows.h"      HAVE_WINDOWS_H)
   check_include_file_concat("ws2tcpip.h"     HAVE_WS2TCPIP_H)
   check_include_file_concat("winsock2.h"     HAVE_WINSOCK2_H)
-  check_include_file_concat("wincrypt.h"     HAVE_WINCRYPT_H)
 else()
   set(HAVE_WINDOWS_H 0)
-  set(HAVE_WINSOCK_H 0)
   set(HAVE_WS2TCPIP_H 0)
   set(HAVE_WINSOCK2_H 0)
 endif()
 
 check_include_file_concat("inttypes.h"       HAVE_INTTYPES_H)
 check_include_file_concat("sys/filio.h"      HAVE_SYS_FILIO_H)
+check_include_file_concat("sys/wait.h"       HAVE_SYS_WAIT_H)
 check_include_file_concat("sys/ioctl.h"      HAVE_SYS_IOCTL_H)
 check_include_file_concat("sys/param.h"      HAVE_SYS_PARAM_H)
 check_include_file_concat("sys/poll.h"       HAVE_SYS_POLL_H)
@@ -1167,7 +1260,6 @@
 check_include_file_concat("sys/utime.h"      HAVE_SYS_UTIME_H)
 check_include_file_concat("sys/xattr.h"      HAVE_SYS_XATTR_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
-check_include_file_concat("arpa/tftp.h"      HAVE_ARPA_TFTP_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("idn2.h"           HAVE_IDN2_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
@@ -1178,32 +1270,29 @@
 check_include_file_concat("netdb.h"          HAVE_NETDB_H)
 check_include_file_concat("netinet/in.h"     HAVE_NETINET_IN_H)
 check_include_file_concat("netinet/tcp.h"    HAVE_NETINET_TCP_H)
+check_include_file_concat("netinet/udp.h"    HAVE_NETINET_UDP_H)
 check_include_file("linux/tcp.h"      HAVE_LINUX_TCP_H)
 
 check_include_file_concat("poll.h"           HAVE_POLL_H)
 check_include_file_concat("pwd.h"            HAVE_PWD_H)
-check_include_file_concat("setjmp.h"         HAVE_SETJMP_H)
-check_include_file_concat("signal.h"         HAVE_SIGNAL_H)
-check_include_file_concat("ssl.h"            HAVE_SSL_H)
 check_include_file_concat("stdatomic.h"      HAVE_STDATOMIC_H)
 check_include_file_concat("stdbool.h"        HAVE_STDBOOL_H)
 check_include_file_concat("stdint.h"         HAVE_STDINT_H)
-check_include_file_concat("stdlib.h"         HAVE_STDLIB_H)
-check_include_file_concat("string.h"         HAVE_STRING_H)
 check_include_file_concat("strings.h"        HAVE_STRINGS_H)
 check_include_file_concat("stropts.h"        HAVE_STROPTS_H)
 check_include_file_concat("termio.h"         HAVE_TERMIO_H)
 check_include_file_concat("termios.h"        HAVE_TERMIOS_H)
-check_include_file_concat("time.h"           HAVE_TIME_H)
 check_include_file_concat("unistd.h"         HAVE_UNISTD_H)
 check_include_file_concat("utime.h"          HAVE_UTIME_H)
 
-check_include_file_concat("stddef.h"         HAVE_STDDEF_H)
-check_include_file_concat("sys/utsname.h"   HAVE_SYS_UTSNAME_H)
-
 check_type_size(size_t  SIZEOF_SIZE_T)
 check_type_size(ssize_t  SIZEOF_SSIZE_T)
 check_type_size("time_t"  SIZEOF_TIME_T)
+check_type_size("suseconds_t"  SIZEOF_SUSECONDS_T)
+
+if(SIZEOF_SUSECONDS_T)
+  set(HAVE_SUSECONDS_T 1)
+endif()
 
 if(NOT CMAKE_CROSSCOMPILING)
   find_file(RANDOM_FILE urandom /dev)
@@ -1211,7 +1300,7 @@
 endif()
 
 # Check for some functions that are used
-if(HAVE_LIBWS2_32)
+if(WIN32)
   set(CMAKE_REQUIRED_LIBRARIES ws2_32)
 elseif(HAVE_LIBSOCKET)
   set(CMAKE_REQUIRED_LIBRARIES socket)
@@ -1220,45 +1309,50 @@
 endif()
 
 check_symbol_exists(fchmod        "${CURL_INCLUDES}" HAVE_FCHMOD)
-check_symbol_exists(basename      "${CURL_INCLUDES}" HAVE_BASENAME)
+check_symbol_exists(fnmatch       "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH)
+check_symbol_exists(basename      "${CURL_INCLUDES};string.h" HAVE_BASENAME)
 check_symbol_exists(socket        "${CURL_INCLUDES}" HAVE_SOCKET)
+check_symbol_exists(sched_yield   "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD)
 check_symbol_exists(socketpair    "${CURL_INCLUDES}" HAVE_SOCKETPAIR)
 check_symbol_exists(recv          "${CURL_INCLUDES}" HAVE_RECV)
 check_symbol_exists(send          "${CURL_INCLUDES}" HAVE_SEND)
 check_symbol_exists(sendmsg       "${CURL_INCLUDES}" HAVE_SENDMSG)
 check_symbol_exists(select        "${CURL_INCLUDES}" HAVE_SELECT)
-check_symbol_exists(strdup        "${CURL_INCLUDES}" HAVE_STRDUP)
-check_symbol_exists(strtok_r      "${CURL_INCLUDES}" HAVE_STRTOK_R)
-check_symbol_exists(strcasecmp    "${CURL_INCLUDES}" HAVE_STRCASECMP)
-check_symbol_exists(stricmp       "${CURL_INCLUDES}" HAVE_STRICMP)
-check_symbol_exists(strcmpi       "${CURL_INCLUDES}" HAVE_STRCMPI)
+check_symbol_exists(strdup        "${CURL_INCLUDES};string.h" HAVE_STRDUP)
+check_symbol_exists(strtok_r      "${CURL_INCLUDES};string.h" HAVE_STRTOK_R)
+check_symbol_exists(strcasecmp    "${CURL_INCLUDES};string.h" HAVE_STRCASECMP)
+check_symbol_exists(stricmp       "${CURL_INCLUDES};string.h" HAVE_STRICMP)
+check_symbol_exists(strcmpi       "${CURL_INCLUDES};string.h" HAVE_STRCMPI)
+check_symbol_exists(memrchr       "${CURL_INCLUDES};string.h" HAVE_MEMRCHR)
 check_symbol_exists(alarm         "${CURL_INCLUDES}" HAVE_ALARM)
+check_symbol_exists(arc4random    "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM)
+check_symbol_exists(fcntl         "${CURL_INCLUDES}" HAVE_FCNTL)
 check_symbol_exists(getppid       "${CURL_INCLUDES}" HAVE_GETPPID)
 check_symbol_exists(utimes        "${CURL_INCLUDES}" HAVE_UTIMES)
 
 check_symbol_exists(gettimeofday  "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY)
 check_symbol_exists(closesocket   "${CURL_INCLUDES}" HAVE_CLOSESOCKET)
-check_symbol_exists(sigsetjmp     "${CURL_INCLUDES}" HAVE_SIGSETJMP)
+check_symbol_exists(sigsetjmp     "${CURL_INCLUDES};setjmp.h" HAVE_SIGSETJMP)
 check_symbol_exists(getpass_r     "${CURL_INCLUDES}" HAVE_GETPASS_R)
 check_symbol_exists(getpwuid      "${CURL_INCLUDES}" HAVE_GETPWUID)
 check_symbol_exists(getpwuid_r    "${CURL_INCLUDES}" HAVE_GETPWUID_R)
 check_symbol_exists(geteuid       "${CURL_INCLUDES}" HAVE_GETEUID)
 check_symbol_exists(utime         "${CURL_INCLUDES}" HAVE_UTIME)
-check_symbol_exists(gmtime_r      "${CURL_INCLUDES}" HAVE_GMTIME_R)
+check_symbol_exists(gmtime_r      "${CURL_INCLUDES};stdlib.h;time.h" HAVE_GMTIME_R)
 
 check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R)
 
-check_symbol_exists(signal         "${CURL_INCLUDES}" HAVE_SIGNAL)
-check_symbol_exists(strtoll        "${CURL_INCLUDES}" HAVE_STRTOLL)
-check_symbol_exists(strerror_r     "${CURL_INCLUDES}" HAVE_STRERROR_R)
-check_symbol_exists(siginterrupt   "${CURL_INCLUDES}" HAVE_SIGINTERRUPT)
-check_symbol_exists(getaddrinfo    "${CURL_INCLUDES}" HAVE_GETADDRINFO)
-if(WIN32)
-  set(HAVE_GETADDRINFO_THREADSAFE ${HAVE_GETADDRINFO})
-endif()
+check_symbol_exists(signal         "${CURL_INCLUDES};signal.h" HAVE_SIGNAL)
+check_symbol_exists(strtoll        "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL)
+check_symbol_exists(strerror_r     "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R)
+check_symbol_exists(siginterrupt   "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT)
+check_symbol_exists(getaddrinfo    "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO)
+check_symbol_exists(getifaddrs     "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS)
 check_symbol_exists(freeaddrinfo   "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
 check_symbol_exists(pipe           "${CURL_INCLUDES}" HAVE_PIPE)
 check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
+check_symbol_exists(fseeko         "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO)
+check_symbol_exists(_fseeki64      "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64)
 check_symbol_exists(getpeername    "${CURL_INCLUDES}" HAVE_GETPEERNAME)
 check_symbol_exists(getsockname    "${CURL_INCLUDES}" HAVE_GETSOCKNAME)
 check_symbol_exists(if_nametoindex "${CURL_INCLUDES}" HAVE_IF_NAMETOINDEX)
@@ -1272,11 +1366,11 @@
   check_symbol_exists(snprintf       "stdio.h" HAVE_SNPRINTF)
 endif()
 check_function_exists(mach_absolute_time HAVE_MACH_ABSOLUTE_TIME)
-check_symbol_exists(inet_ntop      "${CURL_INCLUDES}" HAVE_INET_NTOP)
+check_symbol_exists(inet_ntop      "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP)
 if(MSVC AND (MSVC_VERSION LESS_EQUAL 1600))
   set(HAVE_INET_NTOP OFF)
 endif()
-check_symbol_exists(inet_pton      "${CURL_INCLUDES}" HAVE_INET_PTON)
+check_symbol_exists(inet_pton      "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON)
 
 check_symbol_exists(fsetxattr "${CURL_INCLUDES}" HAVE_FSETXATTR)
 if(HAVE_FSETXATTR)
@@ -1297,24 +1391,15 @@
 
 # sigaction and sigsetjmp are special. Use special mechanism for
 # detecting those, but only if previous attempt failed.
-if(HAVE_SIGNAL_H)
-  check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
-endif()
+check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
 
 if(NOT HAVE_SIGSETJMP)
-  if(HAVE_SETJMP_H)
-    check_symbol_exists(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP)
-    if(HAVE_MACRO_SIGSETJMP)
-      set(HAVE_SIGSETJMP 1)
-    endif()
+  check_symbol_exists(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP)
+  if(HAVE_MACRO_SIGSETJMP)
+    set(HAVE_SIGSETJMP 1)
   endif()
 endif()
 
-# If there is no stricmp(), do not allow LDAP to parse URLs
-if(NOT HAVE_STRICMP)
-  set(HAVE_LDAP_URL_PARSE 1)
-endif()
-
 # Do curl specific tests
 foreach(CURL_TEST
     HAVE_FCNTL_O_NONBLOCK
@@ -1325,7 +1410,6 @@
     HAVE_IOCTL_FIONBIO
     HAVE_IOCTL_SIOCGIFADDR
     HAVE_SETSOCKOPT_SO_NONBLOCK
-    TIME_WITH_SYS_TIME
     HAVE_O_NONBLOCK
     HAVE_GETHOSTBYNAME_R_3
     HAVE_GETHOSTBYNAME_R_5
@@ -1370,6 +1454,20 @@
   unset(HAVE_WIN32_WINNT CACHE)
 endif()
 
+if(0) # This code not needed for building within CMake.
+if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
+  # on not-Windows and not-crosscompiling, check for writable argv[]
+  include(CheckCSourceRuns)
+  check_c_source_runs("
+    int main(int argc, char **argv)
+    {
+      (void)argc;
+      argv[0][0] = ' ';
+      return (argv[0][0] == ' ')?0:1;
+    }" HAVE_WRITABLE_ARGV)
+endif()
+endif()
+
 set(CMAKE_REQUIRED_FLAGS)
 
 option(ENABLE_WEBSOCKETS "Set to ON to enable EXPERIMENTAL websockets" OFF)
@@ -1470,11 +1568,8 @@
   # Use the manifest embedded in the Windows Resource
   set(CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS} -DCURL_EMBED_MANIFEST")
 
-  # Check if crypto functions in wincrypt.h are actually available
-  if(HAVE_WINCRYPT_H)
-    check_symbol_exists(CryptAcquireContext "windows.h;wincrypt.h" USE_WINCRYPT)
-  endif()
-  if(USE_WINCRYPT)
+  # We use crypto functions that are not available for UWP apps
+  if(NOT WINDOWS_STORE)
     set(USE_WIN32_CRYPTO ON)
   endif()
 
@@ -1483,11 +1578,7 @@
     list(APPEND CURL_LIBS "advapi32" "crypt32")
   endif()
 
-  if(NOT HAVE_MINGW_ORIGINAL)
-    list(APPEND CURL_LIBS "bcrypt")
-  else()
-    set(HAVE_FTRUNCATE OFF)
-  endif()
+  list(APPEND CURL_LIBS "bcrypt")
 endif()
 
 if(MSVC)
@@ -1551,7 +1642,22 @@
   set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${INPUT_FILE}")
 endfunction()
 
-if(0) # This code not needed for building within CMake.
+#-----------------------------------------------------------------------------
+# CMake-specific curl code.
+add_subdirectory(lib)
+
+add_executable(curltest curltest.c)
+target_link_libraries(curltest cmcurl)
+
+if(BUILD_TESTING AND CMAKE_CURL_TEST_URL)
+  add_test(curl curltest ${CMAKE_CURL_TEST_URL})
+endif()
+
+install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl)
+
+return() # The rest of this file is not needed for building within CMake.
+#-----------------------------------------------------------------------------
+
 include(GNUInstallDirs)
 
 set(CURL_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
@@ -1559,7 +1665,6 @@
 set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
 set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
 set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
-endif()
 
 if(USE_MANUAL)
   add_subdirectory(docs)
@@ -1571,19 +1676,6 @@
   add_subdirectory(src)
 endif()
 
-#-----------------------------------------------------------------------------
-# CMake-specific curl code.
-add_executable(curltest curltest.c)
-target_link_libraries(curltest cmcurl)
-
-if(BUILD_TESTING AND CMAKE_CURL_TEST_URL)
-  add_test(curl curltest ${CMAKE_CURL_TEST_URL})
-endif()
-
-install(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmcurl)
-#-----------------------------------------------------------------------------
-
-if(0) # This code not needed for building within CMake.
 cmake_dependent_option(BUILD_TESTING "Build tests"
   ON "PERL_FOUND;NOT CURL_DISABLE_TESTS"
   OFF)
@@ -1601,9 +1693,9 @@
 endmacro()
 
 # NTLM support requires crypto function adaptions from various SSL libs
-# TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-if(NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
-    (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO))
+# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
+if(NOT (CURL_DISABLE_NTLM) AND
+    (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS))
   set(use_curl_ntlm_core ON)
 endif()
 
@@ -1625,26 +1717,25 @@
 _add_if("alt-svc"       NOT CURL_DISABLE_ALTSVC)
 _add_if("HSTS"          NOT CURL_DISABLE_HSTS)
 # TODO SSP1 missing for SPNEGO
-_add_if("SPNEGO"        NOT CURL_DISABLE_CRYPTO_AUTH AND
+_add_if("SPNEGO"        NOT CURL_DISABLE_NEGOTIATE_AUTH AND
                         (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-_add_if("Kerberos"      NOT CURL_DISABLE_CRYPTO_AUTH AND
+_add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
                         (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
 # NTLM support requires crypto function adaptions from various SSL libs
-# TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-_add_if("NTLM"          NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
+# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
+_add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
                         (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 # TODO missing option (autoconf: --enable-ntlm-wb)
-_add_if("NTLM_WB"       NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
+_add_if("NTLM_WB"       NOT (CURL_DISABLE_NTLM) AND
                         (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND
                         NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
-# TODO missing option (--enable-tls-srp), depends on GNUTLS_SRP/OPENSSL_SRP
 _add_if("TLS-SRP"       USE_TLS_SRP)
 # TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header
 _add_if("HTTP2"         USE_NGHTTP2)
 _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
 _add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 # TODO wolfSSL only support this from v5.0.0 onwards
-_add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS OR USE_NSS
+_add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
                         OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
                         USE_MBEDTLS OR USE_SECTRANSP))
 _add_if("unicode"       ENABLE_UNICODE)
@@ -1664,7 +1755,6 @@
 _add_if("TELNET"        NOT CURL_DISABLE_TELNET)
 _add_if("LDAP"          NOT CURL_DISABLE_LDAP)
 # CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS
-# TODO check HAVE_LDAP_SSL (in autoconf this is enabled with --enable-ldaps)
 _add_if("LDAPS"         NOT CURL_DISABLE_LDAPS AND
                         ((USE_OPENLDAP AND SSL_ENABLED) OR
                         (NOT USE_OPENLDAP AND HAVE_LDAP_SSL)))
@@ -1702,20 +1792,22 @@
 _add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP)
 _add_if("mbedTLS"          SSL_ENABLED AND USE_MBEDTLS)
 _add_if("BearSSL"          SSL_ENABLED AND USE_BEARSSL)
-_add_if("NSS"              SSL_ENABLED AND USE_NSS)
 _add_if("wolfSSL"          SSL_ENABLED AND USE_WOLFSSL)
+_add_if("GnuTLS"           SSL_ENABLED AND USE_GNUTLS)
+
 if(_items)
   list(SORT _items)
 endif()
 string(REPLACE ";" " " SSL_BACKENDS "${_items}")
 message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
+if(CURL_DEFAULT_SSL_BACKEND)
+  message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}")
+endif()
 
 # curl-config needs the following options to be set.
 set(CC                      "${CMAKE_C_COMPILER}")
 # TODO probably put a -D... options here?
 set(CONFIGURE_OPTIONS       "")
-# TODO when to set "-DCURL_STATICLIB" for CPPFLAG_CURL_STATICLIB?
-set(CPPFLAG_CURL_STATICLIB  "")
 set(CURLVERSION             "${CURL_VERSION}")
 set(exec_prefix             "\${prefix}")
 set(includedir              "\${prefix}/include")
@@ -1745,12 +1837,17 @@
 endforeach()
 if(BUILD_SHARED_LIBS)
   set(ENABLE_SHARED         "yes")
-  set(ENABLE_STATIC         "no")
   set(LIBCURL_NO_SHARED     "")
+  set(CPPFLAG_CURL_STATICLIB "")
 else()
   set(ENABLE_SHARED         "no")
-  set(ENABLE_STATIC         "yes")
   set(LIBCURL_NO_SHARED     "${LIBCURL_LIBS}")
+  set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB")
+endif()
+if(BUILD_STATIC_LIBS)
+  set(ENABLE_STATIC         "yes")
+else()
+  set(ENABLE_STATIC         "no")
 endif()
 # "a" (Linux) or "lib" (Windows)
 string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}")
@@ -1841,4 +1938,3 @@
       COMMAND ${CMAKE_COMMAND} -P
       ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake)
 endif()
-endif()
diff --git a/Utilities/cmcurl/include/curl/curl.h b/Utilities/cmcurl/include/curl/curl.h
index cae9b1c..0496570 100644
--- a/Utilities/cmcurl/include/curl/curl.h
+++ b/Utilities/cmcurl/include/curl/curl.h
@@ -93,7 +93,7 @@
     defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
    (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
    (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \
-    defined(__sun__) || defined(__serenity__)
+    defined(__sun__) || defined(__serenity__) || defined(__vxworks__)
 #include <sys/select.h>
 #endif
 
@@ -159,9 +159,9 @@
   CURLSSLBACKEND_NONE = 0,
   CURLSSLBACKEND_OPENSSL = 1,
   CURLSSLBACKEND_GNUTLS = 2,
-  CURLSSLBACKEND_NSS = 3,
+  CURLSSLBACKEND_NSS                    CURL_DEPRECATED(8.3.0, "") = 3,
   CURLSSLBACKEND_OBSOLETE4 = 4,  /* Was QSOSSL. */
-  CURLSSLBACKEND_GSKIT = 5,
+  CURLSSLBACKEND_GSKIT                  CURL_DEPRECATED(8.3.0, "") = 5,
   CURLSSLBACKEND_POLARSSL               CURL_DEPRECATED(7.69.0, "") = 6,
   CURLSSLBACKEND_WOLFSSL = 7,
   CURLSSLBACKEND_SCHANNEL = 8,
@@ -646,10 +646,10 @@
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
                           the obsolete stuff removed! */
 
-/* Previously obsolete error code re-used in 7.38.0 */
+/* Previously obsolete error code reused in 7.38.0 */
 #define CURLE_OBSOLETE16 CURLE_HTTP2
 
-/* Previously obsolete error codes re-used in 7.24.0 */
+/* Previously obsolete error codes reused in 7.24.0 */
 #define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED
 #define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT
 
@@ -781,7 +781,7 @@
   CURLPROXY_HTTP_1_0 = 1,   /* added in 7.19.4, force to use CONNECT
                                HTTP/1.0  */
   CURLPROXY_HTTPS = 2,  /* HTTPS but stick to HTTP/1 added in 7.52.0 */
-  CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.1.0 */
+  CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */
   CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
                            in 7.10 */
   CURLPROXY_SOCKS5 = 5, /* added in 7.10 */
@@ -1358,7 +1358,7 @@
      operation slower and is less friendly for the network. */
   CURLOPT(CURLOPT_FRESH_CONNECT, CURLOPTTYPE_LONG, 74),
 
-  /* Set to explicitly forbid the upcoming transfer's connection to be re-used
+  /* Set to explicitly forbid the upcoming transfer's connection to be reused
      when done. Do not use this unless you're absolutely sure of this, as it
      makes the operation slower and is less friendly for the network. */
   CURLOPT(CURLOPT_FORBID_REUSE, CURLOPTTYPE_LONG, 75),
@@ -1652,7 +1652,7 @@
   CURLOPT(CURLOPT_SOCKOPTFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 148),
   CURLOPT(CURLOPT_SOCKOPTDATA, CURLOPTTYPE_CBPOINT, 149),
 
-  /* set to 0 to disable session ID re-use for this transfer, default is
+  /* set to 0 to disable session ID reuse for this transfer, default is
      enabled (== 1) */
   CURLOPT(CURLOPT_SSL_SESSIONID_CACHE, CURLOPTTYPE_LONG, 150),
 
@@ -2113,7 +2113,7 @@
   CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289),
 
   /* allow RCPT TO command to fail for some recipients */
-  CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290),
+  CURLOPT(CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOPTTYPE_LONG, 290),
 
   /* the private SSL-certificate as a "blob" */
   CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291),
@@ -2207,6 +2207,9 @@
   /* Can leak things, gonna exit() soon */
   CURLOPT(CURLOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 322),
 
+  /* set a specific client IP for HAProxy PROXY protocol header? */
+  CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -2235,6 +2238,9 @@
 /* */
 #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT
 
+/* Added in 8.2.0 */
+#define CURLOPT_MAIL_RCPT_ALLLOWFAILS CURLOPT_MAIL_RCPT_ALLOWFAILS
+
 #else
 /* This is set if CURL_NO_OLDIES is defined at compile-time */
 #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */
@@ -2725,6 +2731,20 @@
  */
 CURL_EXTERN void curl_global_cleanup(void);
 
+/*
+ * NAME curl_global_trace()
+ *
+ * DESCRIPTION
+ *
+ * curl_global_trace() can be invoked at application start to
+ * configure which components in curl should participate in tracing.
+
+ * This function is thread-safe if CURL_VERSION_THREADSAFE is set in the
+ * curl_version_info_data.features flag (fetch by curl_version_info()).
+
+ */
+CURL_EXTERN CURLcode curl_global_trace(const char *config);
+
 /* linked-list structure for the CURLOPT_QUOTE option (and other) */
 struct curl_slist {
   char *data;
@@ -2804,13 +2824,14 @@
  */
 CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused);
 
-/* info about the certificate chain, only for OpenSSL, GnuTLS, Schannel, NSS
-   and GSKit builds. Asked for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */
+/* info about the certificate chain, for SSL backends that support it. Asked
+   for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */
 struct curl_certinfo {
   int num_of_certs;             /* number of certificates with information */
   struct curl_slist **certinfo; /* for each index in this array, there's a
-                                   linked list with textual information in the
-                                   format "name: value" */
+                                   linked list with textual information for a
+                                   certificate in the format "name:content".
+                                   eg "Subject:foo", "Issuer:bar", etc. */
 };
 
 /* Information about the SSL library used and the respective internal SSL
@@ -2918,7 +2939,9 @@
   CURLINFO_REFERER          = CURLINFO_STRING + 60,
   CURLINFO_CAINFO           = CURLINFO_STRING + 61,
   CURLINFO_CAPATH           = CURLINFO_STRING + 62,
-  CURLINFO_LASTONE          = 62
+  CURLINFO_XFER_ID          = CURLINFO_OFF_T + 63,
+  CURLINFO_CONN_ID          = CURLINFO_OFF_T + 64,
+  CURLINFO_LASTONE          = 64
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
diff --git a/Utilities/cmcurl/include/curl/curlver.h b/Utilities/cmcurl/include/curl/curlver.h
index 5588fa5..3c3f992 100644
--- a/Utilities/cmcurl/include/curl/curlver.h
+++ b/Utilities/cmcurl/include/curl/curlver.h
@@ -32,13 +32,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.1.2"
+#define LIBCURL_VERSION "8.4.0"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 1
-#define LIBCURL_VERSION_PATCH 2
+#define LIBCURL_VERSION_MINOR 4
+#define LIBCURL_VERSION_PATCH 0
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080102
+#define LIBCURL_VERSION_NUM 0x080400
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/Utilities/cmcurl/include/curl/mprintf.h b/Utilities/cmcurl/include/curl/mprintf.h
index e652a65..dc5664b 100644
--- a/Utilities/cmcurl/include/curl/mprintf.h
+++ b/Utilities/cmcurl/include/curl/mprintf.h
@@ -32,18 +32,36 @@
 extern "C" {
 #endif
 
-CURL_EXTERN int curl_mprintf(const char *format, ...);
-CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...);
-CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...);
+#if (defined(__GNUC__) || defined(__clang__)) &&                        \
+  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
+  !defined(__MINGW32__) && !defined(CURL_NO_FMT_CHECKS)
+#define CURL_TEMP_PRINTF(a,b) __attribute__ ((format(printf, a, b)))
+#else
+#define CURL_TEMP_PRINTF(a,b)
+#endif
+
+CURL_EXTERN int curl_mprintf(const char *format, ...) CURL_TEMP_PRINTF(1, 2);
+CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...)
+  CURL_TEMP_PRINTF(2, 3);
+CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...)
+  CURL_TEMP_PRINTF(2, 3);
 CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength,
-                               const char *format, ...);
-CURL_EXTERN int curl_mvprintf(const char *format, va_list args);
-CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args);
-CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args);
+                               const char *format, ...) CURL_TEMP_PRINTF(3, 4);
+CURL_EXTERN int curl_mvprintf(const char *format, va_list args)
+  CURL_TEMP_PRINTF(1, 0);
+CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args)
+  CURL_TEMP_PRINTF(2, 0);
+CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args)
+  CURL_TEMP_PRINTF(2, 0);
 CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength,
-                                const char *format, va_list args);
-CURL_EXTERN char *curl_maprintf(const char *format, ...);
-CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args);
+                                const char *format, va_list args)
+  CURL_TEMP_PRINTF(3, 0);
+CURL_EXTERN char *curl_maprintf(const char *format, ...)
+  CURL_TEMP_PRINTF(1, 2);
+CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args)
+  CURL_TEMP_PRINTF(1, 0);
+
+#undef CURL_TEMP_PRINTF
 
 #ifdef  __cplusplus
 } /* end of extern "C" */
diff --git a/Utilities/cmcurl/include/curl/multi.h b/Utilities/cmcurl/include/curl/multi.h
index 30a3d93..e79b48f 100644
--- a/Utilities/cmcurl/include/curl/multi.h
+++ b/Utilities/cmcurl/include/curl/multi.h
@@ -118,7 +118,7 @@
 struct curl_waitfd {
   curl_socket_t fd;
   short events;
-  short revents; /* not supported yet */
+  short revents;
 };
 
 /*
@@ -426,6 +426,17 @@
 CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
                                         curl_socket_t sockfd, void *sockp);
 
+/*
+ * Name:    curl_multi_get_handles()
+ *
+ * Desc:    Returns an allocated array holding all handles currently added to
+ *          the multi handle. Marks the final entry with a NULL pointer. If
+ *          there is no easy handle added to the multi handle, this function
+ *          returns an array with the first entry as a NULL pointer.
+ *
+ * Returns: NULL on failure, otherwise a CURL **array pointer
+ */
+CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle);
 
 /*
  * Name: curl_push_callback
diff --git a/Utilities/cmcurl/include/curl/system.h b/Utilities/cmcurl/include/curl/system.h
index def7739..97e0d03 100644
--- a/Utilities/cmcurl/include/curl/system.h
+++ b/Utilities/cmcurl/include/curl/system.h
@@ -237,33 +237,28 @@
 #  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__MVS__)
-#  if defined(__IBMC__) || defined(__IBMCPP__)
-#    if defined(_ILP32)
-#    elif defined(_LP64)
-#    endif
-#    if defined(_LONG_LONG)
-#      define CURL_TYPEOF_CURL_OFF_T     long long
-#      define CURL_FORMAT_CURL_OFF_T     "lld"
-#      define CURL_FORMAT_CURL_OFF_TU    "llu"
-#      define CURL_SUFFIX_CURL_OFF_T     LL
-#      define CURL_SUFFIX_CURL_OFF_TU    ULL
-#    elif defined(_LP64)
-#      define CURL_TYPEOF_CURL_OFF_T     long
-#      define CURL_FORMAT_CURL_OFF_T     "ld"
-#      define CURL_FORMAT_CURL_OFF_TU    "lu"
-#      define CURL_SUFFIX_CURL_OFF_T     L
-#      define CURL_SUFFIX_CURL_OFF_TU    UL
-#    else
-#      define CURL_TYPEOF_CURL_OFF_T     long
-#      define CURL_FORMAT_CURL_OFF_T     "ld"
-#      define CURL_FORMAT_CURL_OFF_TU    "lu"
-#      define CURL_SUFFIX_CURL_OFF_T     L
-#      define CURL_SUFFIX_CURL_OFF_TU    UL
-#    endif
-#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
-#    define CURL_PULL_SYS_TYPES_H      1
-#    define CURL_PULL_SYS_SOCKET_H     1
+#  if defined(_LONG_LONG)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  elif defined(_LP64)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
 #  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__370__)
 #  if defined(__IBMC__) || defined(__IBMCPP__)
@@ -352,6 +347,24 @@
 #  define CURL_PULL_SYS_TYPES_H      1
 #  define CURL_PULL_SYS_SOCKET_H     1
 
+#elif defined(__hpux) /* HP aCC compiler */
+#  if !defined(_LP64)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
+
 /* ===================================== */
 /*    KEEP MSVC THE PENULTIMATE ENTRY    */
 /* ===================================== */
diff --git a/Utilities/cmcurl/include/curl/typecheck-gcc.h b/Utilities/cmcurl/include/curl/typecheck-gcc.h
index bc8d7a7..b880f3d 100644
--- a/Utilities/cmcurl/include/curl/typecheck-gcc.h
+++ b/Utilities/cmcurl/include/curl/typecheck-gcc.h
@@ -280,6 +280,7 @@
    (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
    (option) == CURLOPT_FTPPORT ||                                             \
    (option) == CURLOPT_HSTS ||                                                \
+   (option) == CURLOPT_HAPROXY_CLIENT_IP ||                                   \
    (option) == CURLOPT_INTERFACE ||                                           \
    (option) == CURLOPT_ISSUERCERT ||                                          \
    (option) == CURLOPT_KEYPASSWD ||                                           \
diff --git a/Utilities/cmcurl/include/curl/urlapi.h b/Utilities/cmcurl/include/curl/urlapi.h
index b3504b6..88cdeb3 100644
--- a/Utilities/cmcurl/include/curl/urlapi.h
+++ b/Utilities/cmcurl/include/curl/urlapi.h
@@ -96,7 +96,8 @@
 #define CURLU_NO_AUTHORITY (1<<10)      /* Allow empty authority when the
                                            scheme is unknown. */
 #define CURLU_ALLOW_SPACE (1<<11)       /* Allow spaces in the URL */
-#define CURLU_PUNYCODE (1<<12)          /* get the host name in pynycode */
+#define CURLU_PUNYCODE (1<<12)          /* get the host name in punycode */
+#define CURLU_PUNY2IDN (1<<13)          /* punycode => IDN conversion */
 
 typedef struct Curl_URL CURLU;
 
diff --git a/Utilities/cmcurl/include/curl/websockets.h b/Utilities/cmcurl/include/curl/websockets.h
index fd6a916..6ef6a2b 100644
--- a/Utilities/cmcurl/include/curl/websockets.h
+++ b/Utilities/cmcurl/include/curl/websockets.h
@@ -54,13 +54,13 @@
  */
 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
                                   size_t *recv,
-                                  struct curl_ws_frame **metap);
+                                  const struct curl_ws_frame **metap);
 
-/* sendflags for curl_ws_send() */
+/* flags for curl_ws_send() */
 #define CURLWS_PONG       (1<<6)
 
 /*
- * NAME curl_easy_send()
+ * NAME curl_ws_send()
  *
  * DESCRIPTION
  *
@@ -69,13 +69,13 @@
  */
 CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
                                   size_t buflen, size_t *sent,
-                                  curl_off_t framesize,
-                                  unsigned int sendflags);
+                                  curl_off_t fragsize,
+                                  unsigned int flags);
 
 /* bits for the CURLOPT_WS_OPTIONS bitmask: */
 #define CURLWS_RAW_MODE (1<<0)
 
-CURL_EXTERN struct curl_ws_frame *curl_ws_meta(CURL *curl);
+CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl);
 
 #ifdef  __cplusplus
 }
diff --git a/Utilities/cmcurl/lib/CMakeLists.txt b/Utilities/cmcurl/lib/CMakeLists.txt
index f1d0f76..9899b9d 100644
--- a/Utilities/cmcurl/lib/CMakeLists.txt
+++ b/Utilities/cmcurl/lib/CMakeLists.txt
@@ -21,18 +21,10 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
-set(LIB_NAME cmcurl)
-set(LIBCURL_OUTPUT_NAME cmcurl)
+set(LIB_NAME libcurl)
+set(LIBCURL_OUTPUT_NAME libcurl CACHE STRING "Basename of the curl library")
 add_definitions(-DBUILDING_LIBCURL)
 
-if(BUILD_SHARED_LIBS)
-  set(CURL_STATICLIB NO)
-else()
-  set(CURL_STATICLIB YES)
-endif()
-
-# Use:
-# * CURL_STATICLIB
 configure_file(curl_config.h.cmake
   ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)
 
@@ -43,10 +35,6 @@
   ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h
   )
 
-if(WIN32 AND NOT CURL_STATICLIB)
-  list(APPEND CSOURCES libcurl.rc)
-endif()
-
 # The rest of the build
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR}/../include)
@@ -59,6 +47,17 @@
   include_directories(${CARES_INCLUDE_DIR})
 endif()
 
+#-----------------------------------------------------------------------------
+# CMake-specific curl code.
+unset(LIBCURL_OUTPUT_NAME CACHE)
+
+add_library(cmcurl ${HHEADERS} ${CSOURCES})
+target_compile_definitions(cmcurl INTERFACE CURL_STATICLIB)
+target_link_libraries(cmcurl PRIVATE ${CURL_LIBS})
+if(WIN32 AND CMake_BUILD_PCH)
+  target_precompile_headers(cmcurl PRIVATE "curl_setup.h" "curl_sspi.h" "${CURL_SOURCE_DIR}/include/curl/curl.h")
+endif()
+
 # For windows we want to install OPENSSL_LIBRARIES dlls
 # and also copy them into the build tree so that testing
 # can find them.
@@ -83,34 +82,27 @@
   endif()
 endif()
 
+return() # The rest of this file is not needed for building within CMake.
+#-----------------------------------------------------------------------------
+
 add_library(
-  ${LIB_NAME}
+  curlu # special libcurlu library just for unittests
+  STATIC
+  EXCLUDE_FROM_ALL
   ${HHEADERS} ${CSOURCES}
-  ${CMAKE_CURL_SSL_DLLS}
-  )
+)
+target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
 
-add_library(
-  ${PROJECT_NAME}::${LIB_NAME}
-  ALIAS ${LIB_NAME}
-  )
-
-if(NOT BUILD_SHARED_LIBS)
-    set_target_properties(${LIB_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB)
+if(ENABLE_CURLDEBUG)
+  # We must compile these sources separately to avoid memdebug.h redefinitions
+  # applying to them.
+  set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
 endif()
+target_link_libraries(curlu PRIVATE ${CURL_LIBS})
 
-target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS})
-
-if(0) # This code not needed for building within CMake.
 transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
 include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake)
-endif()
 
-set_target_properties(${LIB_NAME} PROPERTIES
-  COMPILE_DEFINITIONS BUILDING_LIBCURL
-  OUTPUT_NAME ${LIBCURL_OUTPUT_NAME}
-  )
-
-if(0) # This code not needed for building within CMake.
 if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
   CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
   CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR
@@ -127,67 +119,155 @@
 
   math(EXPR CMAKESONAME "${VERSIONCHANGE} - ${VERSIONDEL}")
   set(CMAKEVERSION "${CMAKESONAME}.${VERSIONDEL}.${VERSIONADD}")
-
-  set_target_properties(${LIB_NAME} PROPERTIES
-    VERSION ${CMAKEVERSION}
-    SOVERSION ${CMAKESONAME}
-  )
-
+else()
+  unset(CMAKESONAME)
 endif()
 
+## Library definition
 
-if(HIDES_CURL_PRIVATE_SYMBOLS)
-  set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
-  set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_FLAGS ${CURL_CFLAG_SYMBOLS_HIDE})
+# Add "_imp" as a suffix before the extension to avoid conflicting with
+# the statically linked "libcurl.lib" (typically with MSVC)
+if(WIN32 AND
+   NOT IMPORT_LIB_SUFFIX AND
+   CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL CMAKE_IMPORT_LIBRARY_SUFFIX)
+  set(IMPORT_LIB_SUFFIX "_imp")
 endif()
 
-# Remove the "lib" prefix since the library is already named "libcurl".
-set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
-set_target_properties(${LIB_NAME} PROPERTIES IMPORT_PREFIX "")
-
-if(CURL_HAS_LTO)
-  set_target_properties(${LIB_NAME} PROPERTIES
-    INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
-    INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+# Whether to do a single compilation pass for libcurl sources and reuse these
+# objects to generate both static and shared target.
+if(NOT DEFINED SHARE_LIB_OBJECT)
+  # Enable it by default on platforms where PIC is the default for both shared
+  # and static and there is a way to tell the linker which libcurl symbols it
+  # should export (vs. marking these symbols exportable at compile-time).
+  if(WIN32)
+    set(SHARE_LIB_OBJECT ON)
+  else()
+    # On other platforms, make it an option disabled by default
+    set(SHARE_LIB_OBJECT OFF)
+  endif()
 endif()
 
 if(WIN32)
-  if(BUILD_SHARED_LIBS)
-    if(MSVC)
-      # Add "_imp" as a suffix before the extension to avoid conflicting with
-      # the statically linked "libcurl.lib"
-      set_target_properties(${LIB_NAME} PROPERTIES IMPORT_SUFFIX "_imp.lib")
-    endif()
-  endif()
-elseif(NOT CMAKE_CROSSCOMPILING)
-  # on not-Windows and not-crosscompiling, check for writable argv[]
-    include(CheckCSourceRuns)
-    check_c_source_runs("
-int main(int argc, char **argv)
-{
-  (void)argc;
-  argv[0][0] = ' ';
-  return (argv[0][0] == ' ')?0:1;
-}"
-      HAVE_WRITABLE_ARGV)
+  # Define CURL_STATICLIB always, to disable __declspec(dllexport) for exported
+  # libcurl symbols. We handle exports via libcurl.def instead. Except with
+  # symbol hiding disabled or debug mode enabled, when we export _all_ symbols
+  # from libcurl DLL, without using libcurl.def.
+  add_definitions("-DCURL_STATICLIB")
 endif()
 
-target_include_directories(${LIB_NAME} INTERFACE
-  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
-  $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+if(SHARE_LIB_OBJECT)
+  set(LIB_OBJECT "libcurl_object")
+  add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
+  target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
+  set_target_properties(${LIB_OBJECT} PROPERTIES
+    POSITION_INDEPENDENT_CODE ON)
+  if(HIDES_CURL_PRIVATE_SYMBOLS)
+    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
+    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
+  endif()
+  if(CURL_HAS_LTO)
+    set_target_properties(${LIB_OBJECT} PROPERTIES
+      INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+      INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+  endif()
+
+  target_include_directories(${LIB_OBJECT} INTERFACE
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+    $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+
+  set(LIB_SOURCE $<TARGET_OBJECTS:${LIB_OBJECT}>)
+else()
+  set(LIB_SOURCE ${HHEADERS} ${CSOURCES})
+endif()
+
+# we want it to be called libcurl on all platforms
+if(BUILD_STATIC_LIBS)
+  list(APPEND libcurl_export ${LIB_STATIC})
+  add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
+  add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
+  target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
+  # Remove the "lib" prefix since the library is already named "libcurl".
+  set_target_properties(${LIB_STATIC} PROPERTIES
+    PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
+    SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
+    INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB")
+  if(HIDES_CURL_PRIVATE_SYMBOLS)
+    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
+    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
+  endif()
+  if(CURL_HAS_LTO)
+    set_target_properties(${LIB_STATIC} PROPERTIES
+      INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+      INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+  endif()
+  if(CMAKEVERSION AND CMAKESONAME)
+    set_target_properties(${LIB_STATIC} PROPERTIES
+      VERSION ${CMAKEVERSION} SOVERSION ${CMAKESONAME})
+  endif()
+
+  target_include_directories(${LIB_STATIC} INTERFACE
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+    $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+endif()
+
+if(BUILD_SHARED_LIBS)
+  list(APPEND libcurl_export ${LIB_SHARED})
+  add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
+  add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
+  if(WIN32)
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc)
+    if(HIDES_CURL_PRIVATE_SYMBOLS)
+      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${CURL_SOURCE_DIR}/libcurl.def")
+    endif()
+  endif()
+  target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS})
+  # Remove the "lib" prefix since the library is already named "libcurl".
+  set_target_properties(${LIB_SHARED} PROPERTIES
+    PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
+    IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
+    POSITION_INDEPENDENT_CODE ON)
+  if(HIDES_CURL_PRIVATE_SYMBOLS)
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
+  endif()
+  if(CURL_HAS_LTO)
+    set_target_properties(${LIB_SHARED} PROPERTIES
+      INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+      INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+  endif()
+  if(CMAKEVERSION AND CMAKESONAME)
+    set_target_properties(${LIB_SHARED} PROPERTIES
+      VERSION ${CMAKEVERSION} SOVERSION ${CMAKESONAME})
+  endif()
+
+  target_include_directories(${LIB_SHARED} INTERFACE
+    $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
+    $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
+endif()
+
+add_library(${LIB_NAME} ALIAS ${LIB_SELECTED})
+add_library(${PROJECT_NAME}::${LIB_NAME} ALIAS ${LIB_SELECTED})
 
 if(CURL_ENABLE_EXPORT_TARGET)
-  install(TARGETS ${LIB_NAME}
-    EXPORT ${TARGETS_EXPORT_NAME}
-    ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
-    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
-  )
+  if(BUILD_STATIC_LIBS)
+    install(TARGETS ${LIB_STATIC}
+      EXPORT ${TARGETS_EXPORT_NAME}
+      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+  endif()
+  if(BUILD_SHARED_LIBS)
+    install(TARGETS ${LIB_SHARED}
+      EXPORT ${TARGETS_EXPORT_NAME}
+      ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+    )
+  endif()
 
-  export(TARGETS ${LIB_NAME}
+  export(TARGETS ${libcurl_export}
          FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
          NAMESPACE ${PROJECT_NAME}::
   )
 endif()
-
-endif()
diff --git a/Utilities/cmcurl/lib/Makefile.inc b/Utilities/cmcurl/lib/Makefile.inc
index f815170..e568ef9 100644
--- a/Utilities/cmcurl/lib/Makefile.inc
+++ b/Utilities/cmcurl/lib/Makefile.inc
@@ -44,13 +44,11 @@
 
 LIB_VTLS_CFILES =           \
   vtls/bearssl.c            \
-  vtls/gskit.c              \
   vtls/gtls.c               \
   vtls/hostcheck.c          \
   vtls/keylog.c             \
   vtls/mbedtls.c            \
   vtls/mbedtls_threadlock.c \
-  vtls/nss.c                \
   vtls/openssl.c            \
   vtls/rustls.c             \
   vtls/schannel.c           \
@@ -62,16 +60,15 @@
 
 LIB_VTLS_HFILES =           \
   vtls/bearssl.h            \
-  vtls/gskit.h              \
   vtls/gtls.h               \
   vtls/hostcheck.h          \
   vtls/keylog.h             \
   vtls/mbedtls.h            \
   vtls/mbedtls_threadlock.h \
-  vtls/nssg.h               \
   vtls/openssl.h            \
   vtls/rustls.h             \
   vtls/schannel.h           \
+  vtls/schannel_int.h       \
   vtls/sectransp.h          \
   vtls/vtls.h               \
   vtls/vtls_int.h           \
@@ -125,7 +122,6 @@
   curl_get_line.c    \
   curl_gethostname.c \
   curl_gssapi.c      \
-  curl_log.c         \
   curl_memrchr.c     \
   curl_multibyte.c   \
   curl_ntlm_core.c   \
@@ -136,6 +132,7 @@
   curl_sasl.c        \
   curl_sspi.c        \
   curl_threads.c     \
+  curl_trc.c         \
   dict.c             \
   doh.c              \
   dynbuf.c           \
@@ -165,12 +162,12 @@
   http.c             \
   http1.c            \
   http2.c            \
+  http_aws_sigv4.c   \
   http_chunks.c      \
   http_digest.c      \
   http_negotiate.c   \
   http_ntlm.c        \
   http_proxy.c       \
-  http_aws_sigv4.c   \
   idn.c              \
   if2ip.c            \
   imap.c             \
@@ -179,6 +176,7 @@
   krb5.c             \
   ldap.c             \
   llist.c            \
+  macos.c            \
   md4.c              \
   md5.c              \
   memdebug.c         \
@@ -260,7 +258,6 @@
   curl_hmac.h        \
   curl_krb5.h        \
   curl_ldap.h        \
-  curl_log.h         \
   curl_md4.h         \
   curl_md5.h         \
   curl_memory.h      \
@@ -278,6 +275,7 @@
   curl_sha256.h      \
   curl_sspi.h        \
   curl_threads.h     \
+  curl_trc.h         \
   curlx.h            \
   dict.h             \
   doh.h              \
@@ -291,9 +289,9 @@
   fileinfo.h         \
   fopen.h            \
   formdata.h         \
-  functypes.h        \
   ftp.h              \
   ftplistparser.h    \
+  functypes.h        \
   getinfo.h          \
   gopher.h           \
   hash.h             \
@@ -303,18 +301,19 @@
   http.h             \
   http1.h            \
   http2.h            \
+  http_aws_sigv4.h   \
   http_chunks.h      \
   http_digest.h      \
   http_negotiate.h   \
   http_ntlm.h        \
   http_proxy.h       \
-  http_aws_sigv4.h   \
   idn.h              \
   if2ip.h            \
   imap.h             \
   inet_ntop.h        \
   inet_pton.h        \
   llist.h            \
+  macos.h            \
   memdebug.h         \
   mime.h             \
   mqtt.h             \
diff --git a/Utilities/cmcurl/lib/altsvc.c b/Utilities/cmcurl/lib/altsvc.c
index f812baf..22b0b69 100644
--- a/Utilities/cmcurl/lib/altsvc.c
+++ b/Utilities/cmcurl/lib/altsvc.c
@@ -38,6 +38,8 @@
 #include "warnless.h"
 #include "fopen.h"
 #include "rename.h"
+#include "strdup.h"
+#include "inet_pton.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -97,19 +99,39 @@
 {
   struct altsvc *as = calloc(sizeof(struct altsvc), 1);
   size_t hlen;
+  size_t dlen;
   if(!as)
     return NULL;
   hlen = strlen(srchost);
+  dlen = strlen(dsthost);
   DEBUGASSERT(hlen);
-  as->src.host = strdup(srchost);
+  DEBUGASSERT(dlen);
+  if(!hlen || !dlen)
+    /* bad input */
+    return NULL;
+  if((hlen > 2) && srchost[0] == '[') {
+    /* IPv6 address, strip off brackets */
+    srchost++;
+    hlen -= 2;
+  }
+  else if(srchost[hlen - 1] == '.')
+    /* strip off trailing dot */
+    hlen--;
+  if((dlen > 2) && dsthost[0] == '[') {
+    /* IPv6 address, strip off brackets */
+    dsthost++;
+    dlen -= 2;
+  }
+
+  as->src.host = Curl_memdup(srchost, hlen + 1);
   if(!as->src.host)
     goto error;
-  if(hlen && (srchost[hlen - 1] == '.'))
-    /* strip off trailing any dot */
-    as->src.host[--hlen] = 0;
-  as->dst.host = strdup(dsthost);
+  as->src.host[hlen] = 0;
+
+  as->dst.host = Curl_memdup(dsthost, dlen + 1);
   if(!as->dst.host)
     goto error;
+  as->dst.host[dlen] = 0;
 
   as->src.alpnid = srcalpnid;
   as->dst.alpnid = dstalpnid;
@@ -231,18 +253,40 @@
 static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
 {
   struct tm stamp;
+  const char *dst6_pre = "";
+  const char *dst6_post = "";
+  const char *src6_pre = "";
+  const char *src6_post = "";
   CURLcode result = Curl_gmtime(as->expires, &stamp);
   if(result)
     return result;
-
+#ifdef ENABLE_IPV6
+  else {
+    char ipv6_unused[16];
+    if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
+      dst6_pre = "[";
+      dst6_post = "]";
+    }
+    if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
+      src6_pre = "[";
+      src6_post = "]";
+    }
+  }
+#endif
   fprintf(fp,
-          "%s %s %u "
-          "%s %s %u "
+          "%s %s%s%s %u "
+          "%s %s%s%s %u "
           "\"%d%02d%02d "
           "%02d:%02d:%02d\" "
           "%u %d\n",
-          Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port,
-          Curl_alpnid2str(as->dst.alpnid), as->dst.host, as->dst.port,
+          Curl_alpnid2str(as->src.alpnid),
+          src6_pre, as->src.host, src6_post,
+          as->src.port,
+
+          Curl_alpnid2str(as->dst.alpnid),
+          dst6_pre, as->dst.host, dst6_post,
+          as->dst.port,
+
           stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
           stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
           as->persist, as->prio);
@@ -424,7 +468,7 @@
 #ifdef DEBUGBUILD
 /* to play well with debug builds, we can *set* a fixed time this will
    return */
-static time_t debugtime(void *unused)
+static time_t altsvc_debugtime(void *unused)
 {
   char *timestr = getenv("CURL_TIME");
   (void)unused;
@@ -434,7 +478,8 @@
   }
   return time(NULL);
 }
-#define time(x) debugtime(x)
+#undef time
+#define time(x) altsvc_debugtime(x)
 #endif
 
 #define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
@@ -499,9 +544,21 @@
         if(*p != ':') {
           /* host name starts here */
           const char *hostp = p;
-          while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
-            p++;
-          len = p - hostp;
+          if(*p == '[') {
+            /* pass all valid IPv6 letters - does not handle zone id */
+            len = strspn(++p, "0123456789abcdefABCDEF:.");
+            if(p[len] != ']')
+              /* invalid host syntax, bail out */
+              break;
+            /* we store the IPv6 numerical address *with* brackets */
+            len += 2;
+            p = &p[len-1];
+          }
+          else {
+            while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
+              p++;
+            len = p - hostp;
+          }
           if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
             infof(data, "Excessive alt-svc host name, ignoring.");
             valid = FALSE;
diff --git a/Utilities/cmcurl/lib/amigaos.c b/Utilities/cmcurl/lib/amigaos.c
index b0a9500..139309b 100644
--- a/Utilities/cmcurl/lib/amigaos.c
+++ b/Utilities/cmcurl/lib/amigaos.c
@@ -178,6 +178,7 @@
 #endif /* CURLRES_AMIGA */
 
 #ifdef USE_AMISSL
+#include <signal.h>
 int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
                       fd_set *errorfds, struct timeval *timeout)
 {
diff --git a/Utilities/cmcurl/lib/amigaos.h b/Utilities/cmcurl/lib/amigaos.h
index 7997ede..c99d963 100644
--- a/Utilities/cmcurl/lib/amigaos.h
+++ b/Utilities/cmcurl/lib/amigaos.h
@@ -39,4 +39,3 @@
 #endif
 
 #endif /* HEADER_CURL_AMIGAOS_H */
-
diff --git a/Utilities/cmcurl/lib/asyn-ares.c b/Utilities/cmcurl/lib/asyn-ares.c
index 19fe853..e73e41d 100644
--- a/Utilities/cmcurl/lib/asyn-ares.c
+++ b/Utilities/cmcurl/lib/asyn-ares.c
@@ -110,7 +110,7 @@
 };
 
 /* How long we are willing to wait for additional parallel responses after
-   obtaining a "definitive" one.
+   obtaining a "definitive" one. For old c-ares without getaddrinfo.
 
    This is intended to equal the c-ares default timeout.  cURL always uses that
    default value.  Unfortunately, c-ares doesn't expose its default timeout in
@@ -120,6 +120,8 @@
  */
 #define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
 
+#define CARES_TIMEOUT_PER_ATTEMPT 2000
+
 /*
  * Curl_resolver_global_init() - the generic low-level asynchronous name
  * resolve API.  Called from curl_global_init() to initialize global resolver
@@ -173,6 +175,9 @@
   int optmask = ARES_OPT_SOCK_STATE_CB;
   options.sock_state_cb = sock_state_cb;
   options.sock_state_cb_data = easy;
+  options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
+  optmask |= ARES_OPT_TIMEOUTMS;
+
   status = ares_init_options((ares_channel*)resolver, &options, optmask);
   if(status != ARES_SUCCESS) {
     if(status == ARES_ENOMEM)
@@ -770,9 +775,14 @@
       int pf = PF_INET;
       memset(&hints, 0, sizeof(hints));
 #ifdef CURLRES_IPV6
-      if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+      if((data->conn->ip_version != CURL_IPRESOLVE_V4) &&
+         Curl_ipv6works(data)) {
         /* The stack seems to be IPv6-enabled */
-        pf = PF_UNSPEC;
+        if(data->conn->ip_version == CURL_IPRESOLVE_V6)
+          pf = PF_INET6;
+        else
+          pf = PF_UNSPEC;
+      }
 #endif /* CURLRES_IPV6 */
       hints.ai_family = pf;
       hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
diff --git a/Utilities/cmcurl/lib/asyn-thread.c b/Utilities/cmcurl/lib/asyn-thread.c
index 6f0a212..a2e294f 100644
--- a/Utilities/cmcurl/lib/asyn-thread.c
+++ b/Utilities/cmcurl/lib/asyn-thread.c
@@ -696,9 +696,13 @@
   *waitp = 0; /* default to synchronous response */
 
 #ifdef CURLRES_IPV6
-  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
     /* The stack seems to be IPv6-enabled */
-    pf = PF_UNSPEC;
+    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
+      pf = PF_INET6;
+    else
+      pf = PF_UNSPEC;
+  }
 #endif /* CURLRES_IPV6 */
 
   memset(&hints, 0, sizeof(hints));
diff --git a/Utilities/cmcurl/lib/base64.c b/Utilities/cmcurl/lib/base64.c
index 971300e..2a49b5a 100644
--- a/Utilities/cmcurl/lib/base64.c
+++ b/Utilities/cmcurl/lib/base64.c
@@ -31,19 +31,20 @@
   !defined(CURL_DISABLE_SMTP) || \
   !defined(CURL_DISABLE_POP3) || \
   !defined(CURL_DISABLE_IMAP) || \
-  !defined(CURL_DISABLE_DOH) || defined(USE_SSL)
-
-#include "urldata.h" /* for the Curl_easy definition */
+  !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
+#include "curl/curl.h"
 #include "warnless.h"
 #include "curl_base64.h"
 
 /* The last 2 #include files should be in this order */
+#ifdef BUILDING_LIBCURL
 #include "curl_memory.h"
+#endif
 #include "memdebug.h"
 
 /* ---- Base64 Encoding/Decoding Table --- */
 /* Padding character string starts at offset 64. */
-static const char base64[]=
+static const char base64encdec[]=
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
 /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
@@ -120,7 +121,7 @@
   /* replaces
   {
     unsigned char c;
-    const unsigned char *p = (const unsigned char *)base64;
+    const unsigned char *p = (const unsigned char *)base64encdec;
     for(c = 0; *p; c++, p++)
       lookup[*p] = c;
   }
@@ -264,7 +265,7 @@
 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
                             char **outptr, size_t *outlen)
 {
-  return base64_encode(base64, inputbuff, insize, outptr, outlen);
+  return base64_encode(base64encdec, inputbuff, insize, outptr, outlen);
 }
 
 /*
diff --git a/Utilities/cmcurl/lib/bufq.c b/Utilities/cmcurl/lib/bufq.c
index 30598cf..d03906d 100644
--- a/Utilities/cmcurl/lib/bufq.c
+++ b/Utilities/cmcurl/lib/bufq.c
@@ -144,21 +144,6 @@
   return n;
 }
 
-static void chunk_shift(struct buf_chunk *chunk)
-{
-  if(chunk->r_offset) {
-    if(!chunk_is_empty(chunk)) {
-      size_t n = chunk->w_offset - chunk->r_offset;
-      memmove(chunk->x.data, chunk->x.data + chunk->r_offset, n);
-      chunk->w_offset -= chunk->r_offset;
-      chunk->r_offset = 0;
-    }
-    else {
-      chunk->r_offset = chunk->w_offset = 0;
-    }
-  }
-}
-
 static void chunk_list_free(struct buf_chunk **anchor)
 {
   struct buf_chunk *chunk;
@@ -418,7 +403,8 @@
       break;
     }
     n = chunk_append(tail, buf, len);
-    DEBUGASSERT(n);
+    if(!n)
+      break;
     nwritten += n;
     buf += n;
     len -= n;
@@ -503,13 +489,6 @@
   }
 }
 
-void Curl_bufq_skip_and_shift(struct bufq *q, size_t amount)
-{
-  Curl_bufq_skip(q, amount);
-  if(q->tail)
-    chunk_shift(q->tail);
-}
-
 ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
                        void *writer_ctx, CURLcode *err)
 {
@@ -528,6 +507,14 @@
       }
       break;
     }
+    if(!chunk_written) {
+      if(!nwritten) {
+        /* treat as blocked */
+        *err = CURLE_AGAIN;
+        nwritten = -1;
+      }
+      break;
+    }
     Curl_bufq_skip(q, (size_t)chunk_written);
     nwritten += chunk_written;
   }
@@ -551,7 +538,8 @@
           /* real error, fail */
           return -1;
         }
-        /* would block */
+        /* would block, bufq is full, give up */
+        break;
       }
     }
 
@@ -562,16 +550,25 @@
         /* real error, fail */
         return -1;
       }
-      /* no room in bufq, bail out */
-      goto out;
+      /* no room in bufq */
+      break;
     }
+    /* edge case of writer returning 0 (and len is >0)
+     * break or we might enter an infinite loop here */
+    if(n == 0)
+      break;
+
     /* Maybe only part of `data` has been added, continue to loop */
     buf += (size_t)n;
     len -= (size_t)n;
     nwritten += (size_t)n;
   }
 
-out:
+  if(!nwritten && len) {
+    *err = CURLE_AGAIN;
+    return -1;
+  }
+  *err = CURLE_OK;
   return nwritten;
 }
 
diff --git a/Utilities/cmcurl/lib/bufq.h b/Utilities/cmcurl/lib/bufq.h
index 89b5c84..089d61b 100644
--- a/Utilities/cmcurl/lib/bufq.h
+++ b/Utilities/cmcurl/lib/bufq.h
@@ -209,12 +209,6 @@
  */
 void Curl_bufq_skip(struct bufq *q, size_t amount);
 
-/**
- * Same as `skip` but shift tail data to the start afterwards,
- * so that further writes will find room in tail.
- */
-void Curl_bufq_skip_and_shift(struct bufq *q, size_t amount);
-
 typedef ssize_t Curl_bufq_writer(void *writer_ctx,
                                  const unsigned char *buf, size_t len,
                                  CURLcode *err);
diff --git a/Utilities/cmcurl/lib/c-hyper.c b/Utilities/cmcurl/lib/c-hyper.c
index 756aebe..5726ff1 100644
--- a/Utilities/cmcurl/lib/c-hyper.c
+++ b/Utilities/cmcurl/lib/c-hyper.c
@@ -61,6 +61,11 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+typedef enum {
+    USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
+    USERDATA_RESP_BODY
+} userdata_t;
+
 size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
                        uint8_t *buf, size_t buflen)
 {
@@ -71,9 +76,11 @@
   DEBUGASSERT(conn);
   (void)ctx;
 
+  DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen));
   result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread);
   if(result == CURLE_AGAIN) {
     /* would block, register interest */
+    DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen));
     if(data->hyp.read_waker)
       hyper_waker_free(data->hyp.read_waker);
     data->hyp.read_waker = hyper_context_waker(ctx);
@@ -87,6 +94,7 @@
     failf(data, "Curl_read failed");
     return HYPER_IO_ERROR;
   }
+  DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread));
   return (size_t)nread;
 }
 
@@ -98,8 +106,12 @@
   CURLcode result;
   ssize_t nwrote;
 
+  DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
   result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote);
+  if(!result && !nwrote)
+    result = CURLE_AGAIN;
   if(result == CURLE_AGAIN) {
+    DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
     /* would block, register interest */
     if(data->hyp.write_waker)
       hyper_waker_free(data->hyp.write_waker);
@@ -114,6 +126,7 @@
     failf(data, "Curl_write failed");
     return HYPER_IO_ERROR;
   }
+  DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote));
   return (size_t)nwrote;
 }
 
@@ -161,8 +174,6 @@
 
   if(!data->state.hconnect || !data->set.suppress_connect_headers) {
     writetype = CLIENTWRITE_HEADER;
-    if(data->set.include_header)
-      writetype |= CLIENTWRITE_BODY;
     if(data->state.hconnect)
       writetype |= CLIENTWRITE_CONNECT;
     if(data->req.httpcode/100 == 1)
@@ -174,8 +185,11 @@
     }
   }
 
-  data->info.header_size += (curl_off_t)len;
-  data->req.headerbytecount += (curl_off_t)len;
+  result = Curl_bump_headersize(data, len, FALSE);
+  if(result) {
+    data->state.hresult = result;
+    return HYPER_ITER_BREAK;
+  }
   return HYPER_ITER_CONTINUE;
 }
 
@@ -232,11 +246,7 @@
   if(0 == len)
     return HYPER_ITER_CONTINUE;
   Curl_debug(data, CURLINFO_DATA_IN, buf, len);
-  if(!data->set.http_ce_skip && k->writer_stack)
-    /* content-encoded data */
-    result = Curl_unencode_write(data, k->writer_stack, buf, len);
-  else
-    result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
+  result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
 
   if(result) {
     data->state.hresult = result;
@@ -244,7 +254,11 @@
   }
 
   data->req.bytecount += len;
-  Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+  result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+  if(result) {
+    data->state.hresult = result;
+    return HYPER_ITER_BREAK;
+  }
   return HYPER_ITER_CONTINUE;
 }
 
@@ -298,16 +312,13 @@
 
   if(!data->state.hconnect || !data->set.suppress_connect_headers) {
     writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
-    if(data->set.include_header)
-      writetype |= CLIENTWRITE_BODY;
     result = Curl_client_write(data, writetype,
                                Curl_dyn_ptr(&data->state.headerb), len);
     if(result)
       return result;
   }
-  data->info.header_size += (curl_off_t)len;
-  data->req.headerbytecount += (curl_off_t)len;
-  return CURLE_OK;
+  result = Curl_bump_headersize(data, len, FALSE);
+  return result;
 }
 
 /*
@@ -340,7 +351,6 @@
   struct hyptransfer *h = &data->hyp;
   hyper_task *task;
   hyper_task *foreach;
-  hyper_error *hypererr = NULL;
   const uint8_t *reasonp;
   size_t reason_len;
   CURLcode result = CURLE_OK;
@@ -383,19 +393,9 @@
       break;
     }
     t = hyper_task_type(task);
-    switch(t) {
-    case HYPER_TASK_ERROR:
-      hypererr = hyper_task_value(task);
-      break;
-    case HYPER_TASK_RESPONSE:
-      resp = hyper_task_value(task);
-      break;
-    default:
-      break;
-    }
-    hyper_task_free(task);
-
     if(t == HYPER_TASK_ERROR) {
+      hyper_error *hypererr = hyper_task_value(task);
+      hyper_task_free(task);
       if(data->state.hresult) {
         /* override Hyper's view, might not even be an error */
         result = data->state.hresult;
@@ -406,37 +406,55 @@
         size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
         hyper_code code = hyper_error_code(hypererr);
         failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
-        if(code == HYPERE_ABORTED_BY_CALLBACK)
+        switch(code) {
+        case HYPERE_ABORTED_BY_CALLBACK:
           result = CURLE_OK;
-        else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount)
-          result = CURLE_GOT_NOTHING;
-        else if(code == HYPERE_INVALID_PEER_MESSAGE)
+          break;
+        case HYPERE_UNEXPECTED_EOF:
+          if(!data->req.bytecount)
+            result = CURLE_GOT_NOTHING;
+          else
+            result = CURLE_RECV_ERROR;
+          break;
+        case HYPERE_INVALID_PEER_MESSAGE:
+          /* bump headerbytecount to avoid the count remaining at zero and
+             appearing to not having read anything from the peer at all */
+          data->req.headerbytecount++;
           result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
-        else
+          break;
+        default:
           result = CURLE_RECV_ERROR;
+          break;
+        }
       }
       *done = TRUE;
       hyper_error_free(hypererr);
       break;
     }
-    else if(h->endtask == task) {
-      /* end of transfer, forget the task handled, we might get a
-       * new one with the same address in the future. */
-      *done = TRUE;
-      h->endtask = NULL;
-      infof(data, "hyperstream is done");
-      if(!k->bodywrites) {
-        /* hyper doesn't always call the body write callback */
-        bool stilldone;
-        result = Curl_http_firstwrite(data, data->conn, &stilldone);
+    else if(t == HYPER_TASK_EMPTY) {
+      void *userdata = hyper_task_userdata(task);
+      hyper_task_free(task);
+      if((userdata_t)userdata == USERDATA_RESP_BODY) {
+        /* end of transfer */
+        *done = TRUE;
+        infof(data, "hyperstream is done");
+        if(!k->bodywrites) {
+          /* hyper doesn't always call the body write callback */
+          bool stilldone;
+          result = Curl_http_firstwrite(data, data->conn, &stilldone);
+        }
+        break;
       }
-      break;
+      else {
+        /* A background task for hyper; ignore */
+        continue;
+      }
     }
-    else if(t != HYPER_TASK_RESPONSE) {
-      *didwhat = KEEP_RECV;
-      break;
-    }
-    /* HYPER_TASK_RESPONSE */
+
+    DEBUGASSERT(HYPER_TASK_RESPONSE);
+
+    resp = hyper_task_value(task);
+    hyper_task_free(task);
 
     *didwhat = KEEP_RECV;
     if(!resp) {
@@ -516,13 +534,12 @@
       result = CURLE_OUT_OF_MEMORY;
       break;
     }
-    DEBUGASSERT(hyper_task_type(foreach) == HYPER_TASK_EMPTY);
+    hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY);
     if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
       failf(data, "Couldn't hyper_executor_push the body-foreach");
       result = CURLE_OUT_OF_MEMORY;
       break;
     }
-    h->endtask = foreach;
 
     hyper_response_free(resp);
     resp = NULL;
@@ -750,7 +767,7 @@
     /* increasing the writebytecount here is a little premature but we
        don't know exactly when the body is sent */
     data->req.writebytecount += fillcount;
-    Curl_pgrsSetUploadCounter(data, fillcount);
+    Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
   }
   return HYPER_POLL_READY;
 }
@@ -787,15 +804,16 @@
       hyper_body_set_data_func(body, uploadpostfields);
     else {
       result = Curl_get_upload_buffer(data);
-      if(result)
+      if(result) {
+        hyper_body_free(body);
         return result;
+      }
       /* init the "upload from here" pointer */
       data->req.upload_fromhere = data->state.ulbuf;
       hyper_body_set_data_func(body, uploadstreamed);
     }
     if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
       /* fail */
-      hyper_body_free(body);
       result = CURLE_OUT_OF_MEMORY;
     }
   }
@@ -1187,14 +1205,17 @@
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
+  req = NULL;
 
   if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
     failf(data, "Couldn't hyper_executor_push the send");
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
+  sendtask = NULL; /* ownership passed on */
 
   hyper_clientconn_free(client);
+  client = NULL;
 
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
     /* HTTP GET/HEAD download */
@@ -1207,8 +1228,8 @@
        the full request has been sent. */
     data->req.start100 = Curl_now();
 
-  /* clear userpwd and proxyuserpwd to avoid re-using old credentials
-   * from re-used connections */
+  /* clear userpwd and proxyuserpwd to avoid reusing old credentials
+   * from reused connections */
   Curl_safefree(data->state.aptr.userpwd);
   Curl_safefree(data->state.aptr.proxyuserpwd);
   return CURLE_OK;
@@ -1223,6 +1244,12 @@
   if(handshake)
     hyper_task_free(handshake);
 
+  if(client)
+    hyper_clientconn_free(client);
+
+  if(req)
+    hyper_request_free(req);
+
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/c-hyper.h b/Utilities/cmcurl/lib/c-hyper.h
index 4218cda..0c7de90 100644
--- a/Utilities/cmcurl/lib/c-hyper.h
+++ b/Utilities/cmcurl/lib/c-hyper.h
@@ -34,7 +34,6 @@
   hyper_waker *write_waker;
   hyper_waker *read_waker;
   const hyper_executor *exec;
-  hyper_task *endtask;
   hyper_waker *exp100_waker;
   hyper_waker *send_body_waker;
 };
diff --git a/Utilities/cmcurl/lib/cf-h1-proxy.c b/Utilities/cmcurl/lib/cf-h1-proxy.c
index b42c4e6..6748021 100644
--- a/Utilities/cmcurl/lib/cf-h1-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h1-proxy.c
@@ -34,6 +34,7 @@
 #include "dynbuf.h"
 #include "sendf.h"
 #include "http.h"
+#include "http1.h"
 #include "http_proxy.h"
 #include "url.h"
 #include "select.h"
@@ -41,7 +42,7 @@
 #include "cfilters.h"
 #include "cf-h1-proxy.h"
 #include "connect.h"
-#include "curl_log.h"
+#include "curl_trc.h"
 #include "curlx.h"
 #include "vtls/vtls.h"
 #include "transfer.h"
@@ -54,23 +55,20 @@
 
 
 typedef enum {
-    TUNNEL_INIT,     /* init/default/no tunnel state */
-    TUNNEL_CONNECT,  /* CONNECT request is being send */
-    TUNNEL_RECEIVE,  /* CONNECT answer is being received */
-    TUNNEL_RESPONSE, /* CONNECT response received completely */
-    TUNNEL_ESTABLISHED,
-    TUNNEL_FAILED
-} tunnel_state;
+    H1_TUNNEL_INIT,     /* init/default/no tunnel state */
+    H1_TUNNEL_CONNECT,  /* CONNECT request is being send */
+    H1_TUNNEL_RECEIVE,  /* CONNECT answer is being received */
+    H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
+    H1_TUNNEL_ESTABLISHED,
+    H1_TUNNEL_FAILED
+} h1_tunnel_state;
 
 /* struct for HTTP CONNECT tunneling */
-struct tunnel_state {
-  int sockindex;
-  const char *hostname;
-  int remote_port;
+struct h1_tunnel_state {
   struct HTTP CONNECT;
   struct dynbuf rcvbuf;
-  struct dynbuf req;
-  size_t nsend;
+  struct dynbuf request_data;
+  size_t nsent;
   size_t headerlines;
   enum keeponval {
     KEEPON_DONE,
@@ -78,62 +76,47 @@
     KEEPON_IGNORE
   } keepon;
   curl_off_t cl; /* size of content to read and ignore */
-  tunnel_state tunnel_state;
+  h1_tunnel_state tunnel_state;
   BIT(chunked_encoding);
   BIT(close_connection);
 };
 
 
-static bool tunnel_is_established(struct tunnel_state *ts)
+static bool tunnel_is_established(struct h1_tunnel_state *ts)
 {
-  return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED);
+  return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
 }
 
-static bool tunnel_is_failed(struct tunnel_state *ts)
+static bool tunnel_is_failed(struct h1_tunnel_state *ts)
 {
-  return ts && (ts->tunnel_state == TUNNEL_FAILED);
+  return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
 }
 
-static CURLcode tunnel_reinit(struct tunnel_state *ts,
-                              struct connectdata *conn,
-                              struct Curl_easy *data)
+static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              struct h1_tunnel_state *ts)
 {
   (void)data;
+  (void)cf;
   DEBUGASSERT(ts);
   Curl_dyn_reset(&ts->rcvbuf);
-  Curl_dyn_reset(&ts->req);
-  ts->tunnel_state = TUNNEL_INIT;
+  Curl_dyn_reset(&ts->request_data);
+  ts->tunnel_state = H1_TUNNEL_INIT;
   ts->keepon = KEEPON_CONNECT;
   ts->cl = 0;
   ts->close_connection = FALSE;
-
-  if(conn->bits.conn_to_host)
-    ts->hostname = conn->conn_to_host.name;
-  else if(ts->sockindex == SECONDARYSOCKET)
-    ts->hostname = conn->secondaryhostname;
-  else
-    ts->hostname = conn->host.name;
-
-  if(ts->sockindex == SECONDARYSOCKET)
-    ts->remote_port = conn->secondary_port;
-  else if(conn->bits.conn_to_port)
-    ts->remote_port = conn->conn_to_port;
-  else
-    ts->remote_port = conn->remote_port;
-
   return CURLE_OK;
 }
 
-static CURLcode tunnel_init(struct tunnel_state **pts,
+static CURLcode tunnel_init(struct Curl_cfilter *cf,
                             struct Curl_easy *data,
-                            struct connectdata *conn,
-                            int sockindex)
+                            struct h1_tunnel_state **pts)
 {
-  struct tunnel_state *ts;
+  struct h1_tunnel_state *ts;
   CURLcode result;
 
-  if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
-    failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
+  if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
+    failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
 
@@ -146,27 +129,26 @@
   if(!ts)
     return CURLE_OUT_OF_MEMORY;
 
-  ts->sockindex = sockindex;
   infof(data, "allocate connect buffer");
 
   Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
-  Curl_dyn_init(&ts->req, DYN_HTTP_REQUEST);
+  Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
 
   *pts =  ts;
-  connkeep(conn, "HTTP proxy CONNECT");
-  return tunnel_reinit(ts, conn, data);
+  connkeep(cf->conn, "HTTP proxy CONNECT");
+  return tunnel_reinit(cf, data, ts);
 }
 
-static void tunnel_go_state(struct Curl_cfilter *cf,
-                            struct tunnel_state *ts,
-                            tunnel_state new_state,
-                            struct Curl_easy *data)
+static void h1_tunnel_go_state(struct Curl_cfilter *cf,
+                               struct h1_tunnel_state *ts,
+                               h1_tunnel_state new_state,
+                               struct Curl_easy *data)
 {
   if(ts->tunnel_state == new_state)
     return;
   /* leaving this one */
   switch(ts->tunnel_state) {
-  case TUNNEL_CONNECT:
+  case H1_TUNNEL_CONNECT:
     data->req.ignorebody = FALSE;
     break;
   default:
@@ -174,40 +156,40 @@
   }
   /* entering this one */
   switch(new_state) {
-  case TUNNEL_INIT:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
-    tunnel_reinit(ts, cf->conn, data);
+  case H1_TUNNEL_INIT:
+    CURL_TRC_CF(data, cf, "new tunnel state 'init'");
+    tunnel_reinit(cf, data, ts);
     break;
 
-  case TUNNEL_CONNECT:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
-    ts->tunnel_state = TUNNEL_CONNECT;
+  case H1_TUNNEL_CONNECT:
+    CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
+    ts->tunnel_state = H1_TUNNEL_CONNECT;
     ts->keepon = KEEPON_CONNECT;
     Curl_dyn_reset(&ts->rcvbuf);
     break;
 
-  case TUNNEL_RECEIVE:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'"));
-    ts->tunnel_state = TUNNEL_RECEIVE;
+  case H1_TUNNEL_RECEIVE:
+    CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
+    ts->tunnel_state = H1_TUNNEL_RECEIVE;
     break;
 
-  case TUNNEL_RESPONSE:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
-    ts->tunnel_state = TUNNEL_RESPONSE;
+  case H1_TUNNEL_RESPONSE:
+    CURL_TRC_CF(data, cf, "new tunnel state 'response'");
+    ts->tunnel_state = H1_TUNNEL_RESPONSE;
     break;
 
-  case TUNNEL_ESTABLISHED:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
+  case H1_TUNNEL_ESTABLISHED:
+    CURL_TRC_CF(data, cf, "new tunnel state 'established'");
     infof(data, "CONNECT phase completed");
     data->state.authproxy.done = TRUE;
     data->state.authproxy.multipass = FALSE;
     /* FALLTHROUGH */
-  case TUNNEL_FAILED:
-    if(new_state == TUNNEL_FAILED)
-      DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
+  case H1_TUNNEL_FAILED:
+    if(new_state == H1_TUNNEL_FAILED)
+      CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
     ts->tunnel_state = new_state;
     Curl_dyn_reset(&ts->rcvbuf);
-    Curl_dyn_reset(&ts->req);
+    Curl_dyn_reset(&ts->request_data);
     /* restore the protocol pointer */
     data->info.httpcode = 0; /* clear it as it might've been used for the
                                 proxy */
@@ -225,181 +207,90 @@
 static void tunnel_free(struct Curl_cfilter *cf,
                         struct Curl_easy *data)
 {
-  struct tunnel_state *ts = cf->ctx;
+  struct h1_tunnel_state *ts = cf->ctx;
   if(ts) {
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
     Curl_dyn_free(&ts->rcvbuf);
-    Curl_dyn_free(&ts->req);
+    Curl_dyn_free(&ts->request_data);
     free(ts);
     cf->ctx = NULL;
   }
 }
 
-static CURLcode CONNECT_host(struct Curl_easy *data,
-                             struct connectdata *conn,
-                             const char *hostname,
-                             int remote_port,
-                             char **connecthostp,
-                             char **hostp)
-{
-  char *hostheader; /* for CONNECT */
-  char *host = NULL; /* Host: */
-  bool ipv6_ip = conn->bits.ipv6_ip;
-
-  /* the hostname may be different */
-  if(hostname != conn->host.name)
-    ipv6_ip = (strchr(hostname, ':') != NULL);
-  hostheader = /* host:port with IPv6 support */
-    aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
-            remote_port);
-  if(!hostheader)
-    return CURLE_OUT_OF_MEMORY;
-
-  if(!Curl_checkProxyheaders(data, conn, STRCONST("Host"))) {
-    host = aprintf("Host: %s\r\n", hostheader);
-    if(!host) {
-      free(hostheader);
-      return CURLE_OUT_OF_MEMORY;
-    }
-  }
-  *connecthostp = hostheader;
-  *hostp = host;
-  return CURLE_OK;
-}
-
 #ifndef USE_HYPER
 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              struct tunnel_state *ts)
+                              struct h1_tunnel_state *ts)
 {
-  struct connectdata *conn = cf->conn;
-  char *hostheader = NULL;
-  char *host = NULL;
-  const char *httpv;
+  struct httpreq *req = NULL;
+  int http_minor;
   CURLcode result;
 
-  infof(data, "Establish HTTP proxy tunnel to %s:%d",
-        ts->hostname, ts->remote_port);
-
     /* This only happens if we've looped here due to authentication
        reasons, and we don't really use the newly cloned URL here
        then. Just free() it. */
   Curl_safefree(data->req.newurl);
 
-  result = CONNECT_host(data, conn,
-                        ts->hostname, ts->remote_port,
-                        &hostheader, &host);
+  result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
   if(result)
     goto out;
 
-  /* Setup the proxy-authorization header, if any */
-  result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
-                                 hostheader, TRUE);
-  if(result)
-    goto out;
+  infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
 
-  httpv = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
-
-  result =
-      Curl_dyn_addf(&ts->req,
-                    "CONNECT %s HTTP/%s\r\n"
-                    "%s"  /* Host: */
-                    "%s", /* Proxy-Authorization */
-                    hostheader,
-                    httpv,
-                    host?host:"",
-                    data->state.aptr.proxyuserpwd?
-                    data->state.aptr.proxyuserpwd:"");
-  if(result)
-    goto out;
-
-  if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent"))
-     && data->set.str[STRING_USERAGENT])
-    result = Curl_dyn_addf(&ts->req, "User-Agent: %s\r\n",
-                           data->set.str[STRING_USERAGENT]);
-  if(result)
-    goto out;
-
-  if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection")))
-    result = Curl_dyn_addn(&ts->req,
-                           STRCONST("Proxy-Connection: Keep-Alive\r\n"));
-  if(result)
-    goto out;
-
-  result = Curl_add_custom_headers(data, TRUE, &ts->req);
-  if(result)
-    goto out;
-
-  /* CRLF terminate the request */
-  result = Curl_dyn_addn(&ts->req, STRCONST("\r\n"));
-  if(result)
-    goto out;
-
-  /* Send the connect request to the proxy */
-  result = Curl_buffer_send(&ts->req, data, &ts->CONNECT,
-                            &data->info.request_size, 0,
-                            ts->sockindex);
+  Curl_dyn_reset(&ts->request_data);
+  ts->nsent = 0;
   ts->headerlines = 0;
+  http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
+
+  result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
 
 out:
   if(result)
     failf(data, "Failed sending CONNECT to proxy");
-  free(host);
-  free(hostheader);
+  if(req)
+    Curl_http_req_free(req);
   return result;
 }
 
-static CURLcode send_CONNECT(struct Curl_easy *data,
-                             struct connectdata *conn,
-                             struct tunnel_state *ts,
+static CURLcode send_CONNECT(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             struct h1_tunnel_state *ts,
                              bool *done)
 {
-  struct SingleRequest *k = &data->req;
-  struct HTTP *http = &ts->CONNECT;
+  char *buf = Curl_dyn_ptr(&ts->request_data);
+  size_t request_len = Curl_dyn_len(&ts->request_data);
+  size_t blen = request_len;
   CURLcode result = CURLE_OK;
+  ssize_t nwritten;
 
-  if(http->sending != HTTPSEND_REQUEST)
+  if(blen <= ts->nsent)
+    goto out;  /* we are done */
+
+  blen -= ts->nsent;
+  buf += ts->nsent;
+
+  nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, &result);
+  if(nwritten < 0) {
+    if(result == CURLE_AGAIN) {
+      result = CURLE_OK;
+    }
     goto out;
-
-  if(!ts->nsend) {
-    size_t fillcount;
-    k->upload_fromhere = data->state.ulbuf;
-    result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
-                                 &fillcount);
-    if(result)
-      goto out;
-    ts->nsend = fillcount;
   }
-  if(ts->nsend) {
-    ssize_t bytes_written;
-    /* write to socket (send away data) */
-    result = Curl_write(data,
-                        conn->writesockfd,  /* socket to send to */
-                        k->upload_fromhere, /* buffer pointer */
-                        ts->nsend,          /* buffer size */
-                        &bytes_written);    /* actually sent */
-    if(result)
-      goto out;
-    /* send to debug callback! */
-    Curl_debug(data, CURLINFO_HEADER_OUT,
-               k->upload_fromhere, bytes_written);
 
-    ts->nsend -= bytes_written;
-    k->upload_fromhere += bytes_written;
-  }
-  if(!ts->nsend)
-    http->sending = HTTPSEND_NADA;
+  DEBUGASSERT(blen >= (size_t)nwritten);
+  ts->nsent += (size_t)nwritten;
+  Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
 
 out:
   if(result)
     failf(data, "Failed sending CONNECT to proxy");
-  *done = (http->sending != HTTPSEND_REQUEST);
+  *done = (!result && (ts->nsent >= request_len));
   return result;
 }
 
 static CURLcode on_resp_header(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
-                               struct tunnel_state *ts,
+                               struct h1_tunnel_state *ts,
                                const char *header)
 {
   CURLcode result = CURLE_OK;
@@ -416,7 +307,7 @@
     if(!auth)
       return CURLE_OUT_OF_MEMORY;
 
-    DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'", header));
+    CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
     result = Curl_http_input_auth(data, proxy, auth);
 
     free(auth);
@@ -475,7 +366,7 @@
 
 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  struct tunnel_state *ts,
+                                  struct h1_tunnel_state *ts,
                                   bool *done)
 {
   CURLcode result = CURLE_OK;
@@ -491,7 +382,7 @@
   error = SELECT_OK;
   *done = FALSE;
 
-  if(!Curl_conn_data_pending(data, ts->sockindex))
+  if(!Curl_conn_data_pending(data, cf->sockindex))
     return CURLE_OK;
 
   while(ts->keepon) {
@@ -579,7 +470,6 @@
     if(!data->set.suppress_connect_headers) {
       /* send the header to the callback */
       int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
-        (data->set.include_header ? CLIENTWRITE_BODY : 0) |
         (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
 
       result = Curl_client_write(data, writetype, linep, perline);
@@ -587,7 +477,9 @@
         return result;
     }
 
-    data->info.header_size += (long)perline;
+    result = Curl_bump_headersize(data, perline, TRUE);
+    if(result)
+      return result;
 
     /* Newlines are CRLF, so the CR is ignored as the line isn't
        really terminated until the LF comes. Treat a following CR
@@ -636,7 +528,7 @@
           /* without content-length or chunked encoding, we
              can't keep the connection alive since the close is
              the end signal so we bail out at once instead */
-          DEBUGF(LOG_CF(data, cf, "CONNECT: no content-length or chunked"));
+          CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
           ts->keepon = KEEPON_DONE;
         }
       }
@@ -668,10 +560,45 @@
 }
 
 #else /* USE_HYPER */
+
+static CURLcode CONNECT_host(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             char **pauthority,
+                             char **phost_header)
+{
+  const char *hostname;
+  int port;
+  bool ipv6_ip;
+  CURLcode result;
+  char *authority; /* for CONNECT, the destination host + port */
+  char *host_header = NULL; /* Host: authority */
+
+  result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
+  if(result)
+    return result;
+
+  authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
+                      port);
+  if(!authority)
+    return CURLE_OUT_OF_MEMORY;
+
+  /* If user is not overriding the Host header later */
+  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
+    host_header = aprintf("Host: %s\r\n", authority);
+    if(!host_header) {
+      free(authority);
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+  *pauthority = authority;
+  *phost_header = host_header;
+  return CURLE_OK;
+}
+
 /* The Hyper version of CONNECT */
 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              struct tunnel_state *ts)
+                              struct h1_tunnel_state *ts)
 {
   struct connectdata *conn = cf->conn;
   struct hyptransfer *h = &data->hyp;
@@ -684,9 +611,10 @@
   hyper_task *task = NULL; /* for the handshake */
   hyper_clientconn *client = NULL;
   hyper_task *sendtask = NULL; /* for the send */
-  char *hostheader = NULL; /* for CONNECT */
-  char *host = NULL; /* Host: */
+  char *authority = NULL; /* for CONNECT */
+  char *host_header = NULL; /* Host: */
   CURLcode result = CURLE_OUT_OF_MEMORY;
+  (void)ts;
 
   io = hyper_io_new();
   if(!io) {
@@ -713,14 +641,13 @@
   }
 
   options = hyper_clientconn_options_new();
-  hyper_clientconn_options_set_preserve_header_case(options, 1);
-  hyper_clientconn_options_set_preserve_header_order(options, 1);
-
   if(!options) {
     failf(data, "Couldn't create hyper client options");
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
+  hyper_clientconn_options_set_preserve_header_case(options, 1);
+  hyper_clientconn_options_set_preserve_header_order(options, 1);
 
   hyper_clientconn_options_exec(options, h->exec);
 
@@ -751,6 +678,7 @@
 
   client = hyper_task_value(task);
   hyper_task_free(task);
+
   req = hyper_request_new();
   if(!req) {
     failf(data, "Couldn't hyper_request_new");
@@ -764,27 +692,25 @@
     goto error;
   }
 
-  infof(data, "Establish HTTP proxy tunnel to %s:%d",
-        ts->hostname, ts->remote_port);
-
     /* This only happens if we've looped here due to authentication
        reasons, and we don't really use the newly cloned URL here
        then. Just free() it. */
   Curl_safefree(data->req.newurl);
 
-  result = CONNECT_host(data, conn, ts->hostname, ts->remote_port,
-                        &hostheader, &host);
+  result = CONNECT_host(cf, data, &authority, &host_header);
   if(result)
     goto error;
 
-  if(hyper_request_set_uri(req, (uint8_t *)hostheader,
-                           strlen(hostheader))) {
+  infof(data, "Establish HTTP proxy tunnel to %s", authority);
+
+  if(hyper_request_set_uri(req, (uint8_t *)authority,
+                           strlen(authority))) {
     failf(data, "error setting path");
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
   if(data->set.verbose) {
-    char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
+    char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority);
     if(!se) {
       result = CURLE_OUT_OF_MEMORY;
       goto error;
@@ -794,10 +720,10 @@
   }
   /* Setup the proxy-authorization header, if any */
   result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
-                                 hostheader, TRUE);
+                                 authority, TRUE);
   if(result)
     goto error;
-  Curl_safefree(hostheader);
+  Curl_safefree(authority);
 
   /* default is 1.1 */
   if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
@@ -814,11 +740,11 @@
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
-  if(host) {
-    result = Curl_hyper_header(data, headers, host);
+  if(host_header) {
+    result = Curl_hyper_header(data, headers, host_header);
     if(result)
       goto error;
-    Curl_safefree(host);
+    Curl_safefree(host_header);
   }
 
   if(data->state.aptr.proxyuserpwd) {
@@ -859,16 +785,21 @@
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
+  req = NULL;
 
   if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
     failf(data, "Couldn't hyper_executor_push the send");
     result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
+  sendtask = NULL; /* ownership passed on */
+
+  hyper_clientconn_free(client);
+  client = NULL;
 
 error:
-  free(host);
-  free(hostheader);
+  free(host_header);
+  free(authority);
   if(io)
     hyper_io_free(io);
   if(options)
@@ -877,15 +808,19 @@
     hyper_task_free(handshake);
   if(client)
     hyper_clientconn_free(client);
+  if(req)
+    hyper_request_free(req);
+
   return result;
 }
 
-static CURLcode send_CONNECT(struct Curl_easy *data,
-                             struct connectdata *conn,
-                             struct tunnel_state *ts,
+static CURLcode send_CONNECT(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             struct h1_tunnel_state *ts,
                              bool *done)
 {
   struct hyptransfer *h = &data->hyp;
+  struct connectdata *conn = cf->conn;
   hyper_task *task = NULL;
   hyper_error *hypererr = NULL;
   CURLcode result = CURLE_OK;
@@ -919,7 +854,7 @@
 
 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  struct tunnel_state *ts,
+                                  struct h1_tunnel_state *ts,
                                   bool *done)
 {
   struct hyptransfer *h = &data->hyp;
@@ -949,9 +884,9 @@
 
 #endif /* USE_HYPER */
 
-static CURLcode CONNECT(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,
-                        struct tunnel_state *ts)
+static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct h1_tunnel_state *ts)
 {
   struct connectdata *conn = cf->conn;
   CURLcode result;
@@ -973,27 +908,27 @@
     }
 
     switch(ts->tunnel_state) {
-    case TUNNEL_INIT:
+    case H1_TUNNEL_INIT:
       /* Prepare the CONNECT request and make a first attempt to send. */
-      DEBUGF(LOG_CF(data, cf, "CONNECT start"));
+      CURL_TRC_CF(data, cf, "CONNECT start");
       result = start_CONNECT(cf, data, ts);
       if(result)
         goto out;
-      tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_CONNECT:
+    case H1_TUNNEL_CONNECT:
       /* see that the request is completely sent */
-      DEBUGF(LOG_CF(data, cf, "CONNECT send"));
-      result = send_CONNECT(data, cf->conn, ts, &done);
+      CURL_TRC_CF(data, cf, "CONNECT send");
+      result = send_CONNECT(cf, data, ts, &done);
       if(result || !done)
         goto out;
-      tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data);
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_RECEIVE:
+    case H1_TUNNEL_RECEIVE:
       /* read what is there */
-      DEBUGF(LOG_CF(data, cf, "CONNECT receive"));
+      CURL_TRC_CF(data, cf, "CONNECT receive");
       result = recv_CONNECT_resp(cf, data, ts, &done);
       if(Curl_pgrsUpdate(data)) {
         result = CURLE_ABORTED_BY_CALLBACK;
@@ -1003,11 +938,11 @@
       if(result || !done)
         goto out;
       /* got it */
-      tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_RESPONSE:
-      DEBUGF(LOG_CF(data, cf, "CONNECT response"));
+    case H1_TUNNEL_RESPONSE:
+      CURL_TRC_CF(data, cf, "CONNECT response");
       if(data->req.newurl) {
         /* not the "final" response, we need to do a follow up request.
          * If the other side indicated a connection close, or if someone
@@ -1019,7 +954,7 @@
            * reset our tunnel state. To avoid recursion, we return
            * and expect to be called again.
            */
-          DEBUGF(LOG_CF(data, cf, "CONNECT need to close+open"));
+          CURL_TRC_CF(data, cf, "CONNECT need to close+open");
           infof(data, "Connect me again please");
           Curl_conn_cf_close(cf, data);
           connkeep(conn, "HTTP proxy CONNECT");
@@ -1028,7 +963,7 @@
         }
         else {
           /* staying on this connection, reset state */
-          tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+          h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
         }
       }
       break;
@@ -1039,25 +974,25 @@
 
   } while(data->req.newurl);
 
-  DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE);
+  DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
   if(data->info.httpproxycode/100 != 2) {
     /* a non-2xx response and we have no next url to try. */
     Curl_safefree(data->req.newurl);
-    /* failure, close this connection to avoid re-use */
+    /* failure, close this connection to avoid reuse */
     streamclose(conn, "proxy CONNECT failure");
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
     failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
     return CURLE_RECV_ERROR;
   }
   /* 2xx response, SUCCESS! */
-  tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
+  h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
   infof(data, "CONNECT tunnel established, response %d",
         data->info.httpproxycode);
   result = CURLE_OK;
 
 out:
   if(result)
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
   return result;
 }
 
@@ -1066,21 +1001,21 @@
                                     bool blocking, bool *done)
 {
   CURLcode result;
-  struct tunnel_state *ts = cf->ctx;
+  struct h1_tunnel_state *ts = cf->ctx;
 
   if(cf->connected) {
     *done = TRUE;
     return CURLE_OK;
   }
 
-  DEBUGF(LOG_CF(data, cf, "connect"));
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  CURL_TRC_CF(data, cf, "connect");
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
   *done = FALSE;
   if(!ts) {
-    result = tunnel_init(&ts, data, cf->conn, cf->sockindex);
+    result = tunnel_init(cf, data, &ts);
     if(result)
       return result;
     cf->ctx = ts;
@@ -1089,7 +1024,7 @@
   /* TODO: can we do blocking? */
   /* We want "seamless" operations through HTTP proxy tunnel */
 
-  result = CONNECT(cf, data, ts);
+  result = H1_CONNECT(cf, data, ts);
   if(result)
     goto out;
   Curl_safefree(data->state.aptr.proxyuserpwd);
@@ -1107,7 +1042,7 @@
                                         struct Curl_easy *data,
                                         curl_socket_t *socks)
 {
-  struct tunnel_state *ts = cf->ctx;
+  struct h1_tunnel_state *ts = cf->ctx;
   int fds;
 
   fds = cf->next->cft->get_select_socks(cf->next, data, socks);
@@ -1133,20 +1068,20 @@
 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
                                 struct Curl_easy *data)
 {
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   tunnel_free(cf, data);
 }
 
 static void cf_h1_proxy_close(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
 {
-  DEBUGF(LOG_CF(data, cf, "close"));
+  CURL_TRC_CF(data, cf, "close");
   cf->connected = FALSE;
   if(cf->ctx) {
-    tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data);
+    h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
   }
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 
 
diff --git a/Utilities/cmcurl/lib/cf-h2-proxy.c b/Utilities/cmcurl/lib/cf-h2-proxy.c
index 8e76ff8..dbc895d 100644
--- a/Utilities/cmcurl/lib/cf-h2-proxy.c
+++ b/Utilities/cmcurl/lib/cf-h2-proxy.c
@@ -30,11 +30,12 @@
 #include "urldata.h"
 #include "cfilters.h"
 #include "connect.h"
-#include "curl_log.h"
+#include "curl_trc.h"
 #include "bufq.h"
 #include "dynbuf.h"
 #include "dynhds.h"
 #include "http1.h"
+#include "http2.h"
 #include "http_proxy.h"
 #include "multiif.h"
 #include "cf-h2-proxy.h"
@@ -44,26 +45,25 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define H2_NW_CHUNK_SIZE  (128*1024)
-#define H2_NW_RECV_CHUNKS   1
-#define H2_NW_SEND_CHUNKS   1
+#define PROXY_H2_CHUNK_SIZE  (16*1024)
 
-#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
+#define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024)
+#define H2_TUNNEL_WINDOW_SIZE        (10 * 1024 * 1024)
 
-#define H2_TUNNEL_WINDOW_SIZE (1024 * 1024)
-#define H2_TUNNEL_CHUNK_SIZE   (32 * 1024)
-#define H2_TUNNEL_RECV_CHUNKS \
-          (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE)
-#define H2_TUNNEL_SEND_CHUNKS \
-          (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE)
+#define PROXY_H2_NW_RECV_CHUNKS  (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE)
+#define PROXY_H2_NW_SEND_CHUNKS   1
+
+#define H2_TUNNEL_RECV_CHUNKS   (H2_TUNNEL_WINDOW_SIZE / PROXY_H2_CHUNK_SIZE)
+#define H2_TUNNEL_SEND_CHUNKS   ((128 * 1024) / PROXY_H2_CHUNK_SIZE)
+
 
 typedef enum {
-    TUNNEL_INIT,     /* init/default/no tunnel state */
-    TUNNEL_CONNECT,  /* CONNECT request is being send */
-    TUNNEL_RESPONSE, /* CONNECT response received completely */
-    TUNNEL_ESTABLISHED,
-    TUNNEL_FAILED
-} tunnel_state;
+    H2_TUNNEL_INIT,     /* init/default/no tunnel state */
+    H2_TUNNEL_CONNECT,  /* CONNECT request is being send */
+    H2_TUNNEL_RESPONSE, /* CONNECT response received completely */
+    H2_TUNNEL_ESTABLISHED,
+    H2_TUNNEL_FAILED
+} h2_tunnel_state;
 
 struct tunnel_stream {
   struct http_resp *resp;
@@ -72,10 +72,11 @@
   char *authority;
   int32_t stream_id;
   uint32_t error;
-  tunnel_state state;
-  bool has_final_response;
-  bool closed;
-  bool reset;
+  size_t upload_blocked_len;
+  h2_tunnel_state state;
+  BIT(has_final_response);
+  BIT(closed);
+  BIT(reset);
 };
 
 static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
@@ -83,30 +84,18 @@
 {
   const char *hostname;
   int port;
-  bool ipv6_ip = cf->conn->bits.ipv6_ip;
+  bool ipv6_ip;
+  CURLcode result;
 
-  ts->state = TUNNEL_INIT;
+  ts->state = H2_TUNNEL_INIT;
   ts->stream_id = -1;
-  Curl_bufq_init2(&ts->recvbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS,
+  Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS,
                   BUFQ_OPT_SOFT_LIMIT);
-  Curl_bufq_init(&ts->sendbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
+  Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
 
-  if(cf->conn->bits.conn_to_host)
-    hostname = cf->conn->conn_to_host.name;
-  else if(cf->sockindex == SECONDARYSOCKET)
-    hostname = cf->conn->secondaryhostname;
-  else
-    hostname = cf->conn->host.name;
-
-  if(cf->sockindex == SECONDARYSOCKET)
-    port = cf->conn->secondary_port;
-  else if(cf->conn->bits.conn_to_port)
-    port = cf->conn->conn_to_port;
-  else
-    port = cf->conn->remote_port;
-
-  if(hostname != cf->conn->host.name)
-    ipv6_ip = (strchr(hostname, ':') != NULL);
+  result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
+  if(result)
+    return result;
 
   ts->authority = /* host:port with IPv6 support */
     aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"", port);
@@ -123,13 +112,13 @@
   Curl_bufq_free(&ts->sendbuf);
   Curl_safefree(ts->authority);
   memset(ts, 0, sizeof(*ts));
-  ts->state = TUNNEL_INIT;
+  ts->state = H2_TUNNEL_INIT;
 }
 
-static void tunnel_go_state(struct Curl_cfilter *cf,
-                            struct tunnel_stream *ts,
-                            tunnel_state new_state,
-                            struct Curl_easy *data)
+static void h2_tunnel_go_state(struct Curl_cfilter *cf,
+                               struct tunnel_stream *ts,
+                               h2_tunnel_state new_state,
+                               struct Curl_easy *data)
 {
   (void)cf;
 
@@ -137,7 +126,7 @@
     return;
   /* leaving this one */
   switch(ts->state) {
-  case TUNNEL_CONNECT:
+  case H2_TUNNEL_CONNECT:
     data->req.ignorebody = FALSE;
     break;
   default:
@@ -145,30 +134,31 @@
   }
   /* entering this one */
   switch(new_state) {
-  case TUNNEL_INIT:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
+  case H2_TUNNEL_INIT:
+    CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id);
     tunnel_stream_clear(ts);
     break;
 
-  case TUNNEL_CONNECT:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
-    ts->state = TUNNEL_CONNECT;
+  case H2_TUNNEL_CONNECT:
+    CURL_TRC_CF(data, cf, "[%d] new tunnel state 'connect'", ts->stream_id);
+    ts->state = H2_TUNNEL_CONNECT;
     break;
 
-  case TUNNEL_RESPONSE:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
-    ts->state = TUNNEL_RESPONSE;
+  case H2_TUNNEL_RESPONSE:
+    CURL_TRC_CF(data, cf, "[%d] new tunnel state 'response'", ts->stream_id);
+    ts->state = H2_TUNNEL_RESPONSE;
     break;
 
-  case TUNNEL_ESTABLISHED:
-    DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
+  case H2_TUNNEL_ESTABLISHED:
+    CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'",
+                ts->stream_id);
     infof(data, "CONNECT phase completed");
     data->state.authproxy.done = TRUE;
     data->state.authproxy.multipass = FALSE;
     /* FALLTHROUGH */
-  case TUNNEL_FAILED:
-    if(new_state == TUNNEL_FAILED)
-      DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
+  case H2_TUNNEL_FAILED:
+    if(new_state == H2_TUNNEL_FAILED)
+      CURL_TRC_CF(data, cf, "[%d] new tunnel state 'failed'", ts->stream_id);
     ts->state = new_state;
     /* If a proxy-authorization header was used for the proxy, then we should
        make sure that it isn't accidentally used for the document request
@@ -191,9 +181,11 @@
   int32_t last_stream_id;
   BIT(conn_closed);
   BIT(goaway);
+  BIT(nw_out_blocked);
 };
 
 /* How to access `call_data` from a cf_h2 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data
 
@@ -219,35 +211,65 @@
   }
 }
 
-static ssize_t nw_in_reader(void *reader_ctx,
-                              unsigned char *buf, size_t buflen,
-                              CURLcode *err)
+static void drain_tunnel(struct Curl_cfilter *cf,
+                         struct Curl_easy *data,
+                         struct tunnel_stream *tunnel)
+{
+  unsigned char bits;
+
+  (void)cf;
+  bits = CURL_CSELECT_IN;
+  if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len)
+    bits |= CURL_CSELECT_OUT;
+  if(data->state.dselect_bits != bits) {
+    CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x",
+                tunnel->stream_id, bits);
+    data->state.dselect_bits = bits;
+    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+  }
+}
+
+static ssize_t proxy_nw_in_reader(void *reader_ctx,
+                                  unsigned char *buf, size_t buflen,
+                                  CURLcode *err)
 {
   struct Curl_cfilter *cf = reader_ctx;
-  struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
 
-  nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
-  DEBUGF(LOG_CF(data, cf, "nw_in recv(len=%zu) -> %zd, %d",
-         buflen, nread, *err));
+  if(cf) {
+    struct Curl_easy *data = CF_DATA_CURRENT(cf);
+    nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
+    CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d",
+                buflen, nread, *err);
+  }
+  else {
+    nread = 0;
+  }
   return nread;
 }
 
-static ssize_t nw_out_writer(void *writer_ctx,
-                             const unsigned char *buf, size_t buflen,
-                             CURLcode *err)
+static ssize_t proxy_h2_nw_out_writer(void *writer_ctx,
+                                      const unsigned char *buf, size_t buflen,
+                                      CURLcode *err)
 {
   struct Curl_cfilter *cf = writer_ctx;
-  struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
 
-  nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
-  DEBUGF(LOG_CF(data, cf, "nw_out send(len=%zu) -> %zd", buflen, nwritten));
+  if(cf) {
+    struct Curl_easy *data = CF_DATA_CURRENT(cf);
+    nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen,
+                                 err);
+    CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d",
+                buflen, nwritten, *err);
+  }
+  else {
+    nwritten = 0;
+  }
   return nwritten;
 }
 
-static int h2_client_new(struct Curl_cfilter *cf,
-                         nghttp2_session_callbacks *cbs)
+static int proxy_h2_client_new(struct Curl_cfilter *cf,
+                               nghttp2_session_callbacks *cbs)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   nghttp2_option *o;
@@ -271,15 +293,23 @@
 static ssize_t on_session_send(nghttp2_session *h2,
                               const uint8_t *buf, size_t blen,
                               int flags, void *userp);
-static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
-                         void *userp);
-static int on_stream_close(nghttp2_session *session, int32_t stream_id,
-                           uint32_t error_code, void *userp);
-static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
-                     const uint8_t *name, size_t namelen,
-                     const uint8_t *value, size_t valuelen,
-                     uint8_t flags,
-                     void *userp);
+static int proxy_h2_on_frame_recv(nghttp2_session *session,
+                                  const nghttp2_frame *frame,
+                                  void *userp);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static int proxy_h2_on_frame_send(nghttp2_session *session,
+                                  const nghttp2_frame *frame,
+                                  void *userp);
+#endif
+static int proxy_h2_on_stream_close(nghttp2_session *session,
+                                    int32_t stream_id,
+                                    uint32_t error_code, void *userp);
+static int proxy_h2_on_header(nghttp2_session *session,
+                              const nghttp2_frame *frame,
+                              const uint8_t *name, size_t namelen,
+                              const uint8_t *value, size_t valuelen,
+                              uint8_t flags,
+                              void *userp);
 static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
                                 int32_t stream_id,
                                 const uint8_t *mem, size_t len, void *userp);
@@ -298,8 +328,8 @@
   DEBUGASSERT(!ctx->h2);
   memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
 
-  Curl_bufq_init(&ctx->inbufq, H2_NW_CHUNK_SIZE, H2_NW_RECV_CHUNKS);
-  Curl_bufq_init(&ctx->outbufq, H2_NW_CHUNK_SIZE, H2_NW_SEND_CHUNKS);
+  Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
+  Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
 
   if(tunnel_stream_init(cf, &ctx->tunnel))
     goto out;
@@ -311,14 +341,20 @@
   }
 
   nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
-  nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+  nghttp2_session_callbacks_set_on_frame_recv_callback(
+    cbs, proxy_h2_on_frame_recv);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  nghttp2_session_callbacks_set_on_frame_send_callback(cbs,
+                                                       proxy_h2_on_frame_send);
+#endif
   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
     cbs, tunnel_recv_callback);
-  nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
-  nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
+  nghttp2_session_callbacks_set_on_stream_close_callback(
+    cbs, proxy_h2_on_stream_close);
+  nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
 
   /* The nghttp2 session is not yet setup, do it */
-  rc = h2_client_new(cf, cbs);
+  rc = proxy_h2_client_new(cf, cbs);
   if(rc) {
     failf(data, "Couldn't initialize nghttp2");
     goto out;
@@ -343,7 +379,7 @@
   }
 
   rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
-                                             HTTP2_HUGE_WINDOW_SIZE);
+                                             PROXY_HTTP2_HUGE_WINDOW_SIZE);
   if(rc) {
     failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
           nghttp2_strerror(rc), rc);
@@ -358,31 +394,39 @@
 out:
   if(cbs)
     nghttp2_session_callbacks_del(cbs);
-  DEBUGF(LOG_CF(data, cf, "init proxy ctx -> %d", result));
+  CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
   return result;
 }
 
-static CURLcode nw_out_flush(struct Curl_cfilter *cf,
-                             struct Curl_easy *data)
+static int proxy_h2_should_close_session(struct cf_h2_proxy_ctx *ctx)
+{
+  return !nghttp2_session_want_read(ctx->h2) &&
+    !nghttp2_session_want_write(ctx->h2);
+}
+
+static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  size_t buflen = Curl_bufq_len(&ctx->outbufq);
   ssize_t nwritten;
   CURLcode result;
 
   (void)data;
-  if(!buflen)
+  if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
 
-  DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen));
-  nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
+  nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf,
+                            &result);
   if(nwritten < 0) {
+    if(result == CURLE_AGAIN) {
+      CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN",
+                  Curl_bufq_len(&ctx->outbufq));
+      ctx->nw_out_blocked = 1;
+    }
     return result;
   }
-  if((size_t)nwritten < buflen) {
-    return CURLE_AGAIN;
-  }
-  return CURLE_OK;
+  CURL_TRC_CF(data, cf, "[0] nw send buffer flushed");
+  return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
 }
 
 /*
@@ -390,9 +434,9 @@
  * This function returns 0 if it succeeds, or -1 and error code will
  * be assigned to *err.
  */
-static int h2_process_pending_input(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    CURLcode *err)
+static int proxy_h2_process_pending_input(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          CURLcode *err)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   const unsigned char *buf;
@@ -402,8 +446,7 @@
   while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) {
 
     rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
-    DEBUGF(LOG_CF(data, cf,
-                 "fed %zu bytes from nw to nghttp2 -> %zd", blen, rv));
+    CURL_TRC_CF(data, cf, "[0] %zu bytes to nghttp2 -> %zd", blen, rv);
     if(rv < 0) {
       failf(data,
             "process_pending_input: nghttp2_session_mem_recv() returned "
@@ -413,28 +456,20 @@
     }
     Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
     if(Curl_bufq_is_empty(&ctx->inbufq)) {
-      DEBUGF(LOG_CF(data, cf, "all data in connection buffer processed"));
+      CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed");
       break;
     }
     else {
-      DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left "
-                    "in connection buffer", Curl_bufq_len(&ctx->inbufq)));
+      CURL_TRC_CF(data, cf, "[0] process_pending_input: %zu bytes left "
+                  "in connection buffer", Curl_bufq_len(&ctx->inbufq));
     }
   }
 
-  if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
-    /* No more requests are allowed in the current session, so
-       the connection may not be reused. This is set when a
-       GOAWAY frame has been received or when the limit of stream
-       identifiers has been reached. */
-    connclose(cf->conn, "http/2: No new requests allowed");
-  }
-
   return 0;
 }
 
-static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data)
+static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -442,9 +477,9 @@
 
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
-    DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer",
-                  Curl_bufq_len(&ctx->inbufq)));
-    if(h2_process_pending_input(cf, data, &result) < 0)
+    CURL_TRC_CF(data, cf, "[0] process %zu bytes in connection buffer",
+                Curl_bufq_len(&ctx->inbufq));
+    if(proxy_h2_process_pending_input(cf, data, &result) < 0)
       return result;
   }
 
@@ -455,9 +490,9 @@
         Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */
         !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) {
 
-    nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
-    DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d",
-                  Curl_bufq_len(&ctx->inbufq), nread, result));
+    nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
+    CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d",
+                Curl_bufq_len(&ctx->inbufq), nread, result);
     if(nread < 0) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data");
@@ -470,7 +505,7 @@
       break;
     }
 
-    if(h2_process_pending_input(cf, data, &result))
+    if(proxy_h2_process_pending_input(cf, data, &result))
       return result;
   }
 
@@ -481,25 +516,22 @@
   return CURLE_OK;
 }
 
-/*
- * Check if there's been an update in the priority /
- * dependency settings and if so it submits a PRIORITY frame with the updated
- * info.
- * Flush any out data pending in the network buffer.
- */
-static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data)
+static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf,
+                                         struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   int rv = 0;
 
-  rv = nghttp2_session_send(ctx->h2);
+  ctx->nw_out_blocked = 0;
+  while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
+    rv = nghttp2_session_send(ctx->h2);
+
   if(nghttp2_is_fatal(rv)) {
-    DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d",
-                  nghttp2_strerror(rv), rv));
+    CURL_TRC_CF(data, cf, "[0] nghttp2_session_send error (%s)%d",
+                nghttp2_strerror(rv), rv);
     return CURLE_SEND_ERROR;
   }
-  return nw_out_flush(cf, data);
+  return proxy_h2_nw_out_flush(cf, data);
 }
 
 static ssize_t on_session_send(nghttp2_session *h2,
@@ -517,7 +549,7 @@
   DEBUGASSERT(data);
 
   nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
-                                  nw_out_writer, cf, &result);
+                                  proxy_h2_nw_out_writer, cf, &result);
   if(nwritten < 0) {
     if(result == CURLE_AGAIN) {
       return NGHTTP2_ERR_WOULDBLOCK;
@@ -532,8 +564,102 @@
   return nwritten;
 }
 
-static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
-                         void *userp)
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static int proxy_h2_fr_print(const nghttp2_frame *frame,
+                             char *buffer, size_t blen)
+{
+  switch(frame->hd.type) {
+    case NGHTTP2_DATA: {
+      return msnprintf(buffer, blen,
+                       "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
+                       (int)frame->hd.length,
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
+                       (int)frame->data.padlen);
+    }
+    case NGHTTP2_HEADERS: {
+      return msnprintf(buffer, blen,
+                       "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
+                       (int)frame->hd.length,
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+    }
+    case NGHTTP2_PRIORITY: {
+      return msnprintf(buffer, blen,
+                       "FRAME[PRIORITY, len=%d, flags=%d]",
+                       (int)frame->hd.length, frame->hd.flags);
+    }
+    case NGHTTP2_RST_STREAM: {
+      return msnprintf(buffer, blen,
+                       "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
+                       (int)frame->hd.length, frame->hd.flags,
+                       frame->rst_stream.error_code);
+    }
+    case NGHTTP2_SETTINGS: {
+      if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
+        return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
+      }
+      return msnprintf(buffer, blen,
+                       "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
+    }
+    case NGHTTP2_PUSH_PROMISE: {
+      return msnprintf(buffer, blen,
+                       "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
+                       (int)frame->hd.length,
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
+    }
+    case NGHTTP2_PING: {
+      return msnprintf(buffer, blen,
+                       "FRAME[PING, len=%d, ack=%d]",
+                       (int)frame->hd.length,
+                       frame->hd.flags&NGHTTP2_FLAG_ACK);
+    }
+    case NGHTTP2_GOAWAY: {
+      char scratch[128];
+      size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+        size_t len = (frame->goaway.opaque_data_len < s_len)?
+                      frame->goaway.opaque_data_len : s_len-1;
+        if(len)
+          memcpy(scratch, frame->goaway.opaque_data, len);
+        scratch[len] = '\0';
+        return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
+                         "last_stream=%d]", frame->goaway.error_code,
+                         scratch, frame->goaway.last_stream_id);
+    }
+    case NGHTTP2_WINDOW_UPDATE: {
+      return msnprintf(buffer, blen,
+                       "FRAME[WINDOW_UPDATE, incr=%d]",
+                       frame->window_update.window_size_increment);
+    }
+    default:
+      return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
+                       frame->hd.type, (int)frame->hd.length,
+                       frame->hd.flags);
+  }
+}
+
+static int proxy_h2_on_frame_send(nghttp2_session *session,
+                                  const nghttp2_frame *frame,
+                                  void *userp)
+{
+  struct Curl_cfilter *cf = userp;
+  struct Curl_easy *data = CF_DATA_CURRENT(cf);
+
+  (void)session;
+  DEBUGASSERT(data);
+  if(data && Curl_trc_cf_is_verbose(cf, data)) {
+    char buffer[256];
+    int len;
+    len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1);
+    buffer[len] = 0;
+    CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
+  }
+  return 0;
+}
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+static int proxy_h2_on_frame_recv(nghttp2_session *session,
+                                  const nghttp2_frame *frame,
+                                  void *userp)
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
@@ -542,85 +668,85 @@
 
   (void)session;
   DEBUGASSERT(data);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  if(Curl_trc_cf_is_verbose(cf, data)) {
+    char buffer[256];
+    int len;
+    len = proxy_h2_fr_print(frame, buffer, sizeof(buffer)-1);
+    buffer[len] = 0;
+    CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer);
+  }
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
   if(!stream_id) {
     /* stream ID zero is for connection-oriented stuff */
     DEBUGASSERT(data);
     switch(frame->hd.type) {
     case NGHTTP2_SETTINGS:
-      /* we do not do anything with this for now */
+      /* Since the initial stream window is 64K, a request might be on HOLD,
+       * due to exhaustion. The (initial) SETTINGS may announce a much larger
+       * window and *assume* that we treat this like a WINDOW_UPDATE. Some
+       * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
+       * To be safe, we UNHOLD a stream in order not to stall. */
+      if((data->req.keepon & KEEP_SEND_HOLD) &&
+         (data->req.keepon & KEEP_SEND)) {
+        data->req.keepon &= ~KEEP_SEND_HOLD;
+        drain_tunnel(cf, data, &ctx->tunnel);
+        CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
+                    stream_id);
+      }
       break;
     case NGHTTP2_GOAWAY:
-      infof(data, "recveived GOAWAY, error=%d, last_stream=%u",
-                  frame->goaway.error_code, frame->goaway.last_stream_id);
       ctx->goaway = TRUE;
       break;
-    case NGHTTP2_WINDOW_UPDATE:
-      DEBUGF(LOG_CF(data, cf, "recv frame WINDOW_UPDATE"));
-      break;
     default:
-      DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type));
+      break;
     }
     return 0;
   }
 
   if(stream_id != ctx->tunnel.stream_id) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] rcvd FRAME not for tunnel",
-                  stream_id));
+    CURL_TRC_CF(data, cf, "[%d] rcvd FRAME not for tunnel", stream_id);
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
   switch(frame->hd.type) {
-  case NGHTTP2_DATA:
-    /* If body started on this stream, then receiving DATA is illegal. */
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame DATA", stream_id));
-    break;
   case NGHTTP2_HEADERS:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame HEADERS", stream_id));
-
     /* nghttp2 guarantees that :status is received, and we store it to
        stream->status_code. Fuzzing has proven this can still be reached
        without status code having been set. */
     if(!ctx->tunnel.resp)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     /* Only final status code signals the end of header */
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] got http status: %d",
-           stream_id, ctx->tunnel.resp->status));
+    CURL_TRC_CF(data, cf, "[%d] got http status: %d",
+                stream_id, ctx->tunnel.resp->status);
     if(!ctx->tunnel.has_final_response) {
       if(ctx->tunnel.resp->status / 100 != 1) {
         ctx->tunnel.has_final_response = TRUE;
       }
     }
     break;
-  case NGHTTP2_PUSH_PROMISE:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv PUSH_PROMISE", stream_id));
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  case NGHTTP2_RST_STREAM:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv RST", stream_id));
-    ctx->tunnel.reset = TRUE;
-    break;
   case NGHTTP2_WINDOW_UPDATE:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv WINDOW_UPDATE", stream_id));
     if((data->req.keepon & KEEP_SEND_HOLD) &&
        (data->req.keepon & KEEP_SEND)) {
       data->req.keepon &= ~KEEP_SEND_HOLD;
       Curl_expire(data, 0, EXPIRE_RUN_NOW);
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%u] unpausing after win update",
-             stream_id));
+      CURL_TRC_CF(data, cf, "[%d] unpausing after win update",
+                  stream_id);
     }
     break;
   default:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv frame %x",
-                  stream_id, frame->hd.type));
     break;
   }
   return 0;
 }
 
-static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
-                     const uint8_t *name, size_t namelen,
-                     const uint8_t *value, size_t valuelen,
-                     uint8_t flags,
-                     void *userp)
+static int proxy_h2_on_header(nghttp2_session *session,
+                              const nghttp2_frame *frame,
+                              const uint8_t *name, size_t namelen,
+                              const uint8_t *value, size_t valuelen,
+                              uint8_t flags,
+                              void *userp)
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
@@ -633,10 +759,9 @@
   (void)session;
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
   if(stream_id != ctx->tunnel.stream_id) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header for non-tunnel stream: "
-                  "%.*s: %.*s", stream_id,
-                  (int)namelen, name,
-                  (int)valuelen, value));
+    CURL_TRC_CF(data, cf, "[%d] header for non-tunnel stream: "
+                "%.*s: %.*s", stream_id,
+                (int)namelen, name, (int)valuelen, value);
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -664,8 +789,8 @@
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     resp->prev = ctx->tunnel.resp;
     ctx->tunnel.resp = resp;
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] status: HTTP/2 %03d",
-                  stream_id, ctx->tunnel.resp->status));
+    CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
+                stream_id, ctx->tunnel.resp->status);
     return 0;
   }
 
@@ -678,10 +803,8 @@
   if(result)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] header: %.*s: %.*s",
-                stream_id,
-                (int)namelen, name,
-                (int)valuelen, value));
+  CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
+              stream_id, (int)namelen, name, (int)valuelen, value);
 
   return 0; /* 0 is successful */
 }
@@ -721,8 +844,8 @@
   if(ts->closed && Curl_bufq_is_empty(&ts->sendbuf))
     *data_flags = NGHTTP2_DATA_FLAG_EOF;
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] tunnel_send_callback -> %zd",
-                ts->stream_id, nread));
+  CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd",
+              ts->stream_id, nread);
   return nread;
 }
 
@@ -752,8 +875,9 @@
   return 0;
 }
 
-static int on_stream_close(nghttp2_session *session, int32_t stream_id,
-                           uint32_t error_code, void *userp)
+static int proxy_h2_on_stream_close(nghttp2_session *session,
+                                    int32_t stream_id,
+                                    uint32_t error_code, void *userp)
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
@@ -765,23 +889,23 @@
   if(stream_id != ctx->tunnel.stream_id)
     return 0;
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] on_stream_close, %s (err %d)",
-                stream_id, nghttp2_http2_strerror(error_code), error_code));
+  CURL_TRC_CF(data, cf, "[%d] proxy_h2_on_stream_close, %s (err %d)",
+              stream_id, nghttp2_http2_strerror(error_code), error_code);
   ctx->tunnel.closed = TRUE;
   ctx->tunnel.error = error_code;
 
   return 0;
 }
 
-static CURLcode h2_submit(int32_t *pstream_id,
-                          struct Curl_cfilter *cf,
-                          struct Curl_easy *data,
-                          nghttp2_session *h2,
-                          struct httpreq *req,
-                          const nghttp2_priority_spec *pri_spec,
-                          void *stream_user_data,
-                          nghttp2_data_source_read_callback read_callback,
-                          void *read_ctx)
+static CURLcode proxy_h2_submit(int32_t *pstream_id,
+                                struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                nghttp2_session *h2,
+                                struct httpreq *req,
+                                const nghttp2_priority_spec *pri_spec,
+                                void *stream_user_data,
+                               nghttp2_data_source_read_callback read_callback,
+                                void *read_ctx)
 {
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
@@ -848,44 +972,17 @@
   CURLcode result;
   struct httpreq *req = NULL;
 
-  infof(data, "Establish HTTP/2 proxy tunnel to %s", ts->authority);
-
-  result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
-                              NULL, 0, ts->authority, strlen(ts->authority),
-                              NULL, 0);
+  result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
   if(result)
     goto out;
 
-  /* Setup the proxy-authorization header, if any */
-  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
-                                 req->authority, TRUE);
-  if(result)
-    goto out;
+  infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority);
 
-  if(data->state.aptr.proxyuserpwd) {
-    result = Curl_dynhds_h1_cadd_line(&req->headers,
-                                      data->state.aptr.proxyuserpwd);
-    if(result)
-      goto out;
-  }
-
-  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent"))
-     && data->set.str[STRING_USERAGENT]) {
-    result = Curl_dynhds_cadd(&req->headers, "User-Agent",
-                              data->set.str[STRING_USERAGENT]);
-    if(result)
-      goto out;
-  }
-
-  result = Curl_dynhds_add_custom(data, TRUE, &req->headers);
-  if(result)
-    goto out;
-
-  result = h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
-                     NULL, ts, tunnel_send_callback, cf);
+  result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
+                           NULL, ts, tunnel_send_callback, cf);
   if(result) {
-    DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
-                  nghttp2_strerror(ts->stream_id), ts->stream_id));
+    CURL_TRC_CF(data, cf, "[%d] send, nghttp2_submit_request error: %s",
+                ts->stream_id, nghttp2_strerror(ts->stream_id));
   }
 
 out:
@@ -907,7 +1004,7 @@
   DEBUGASSERT(ts->resp);
   if(ts->resp->status/100 == 2) {
     infof(data, "CONNECT tunnel established, response %d", ts->resp->status);
-    tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
+    h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data);
     return CURLE_OK;
   }
 
@@ -919,16 +1016,16 @@
   }
 
   if(auth_reply) {
-    DEBUGF(LOG_CF(data, cf, "CONNECT: fwd auth header '%s'",
-                  auth_reply->value));
+    CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'",
+                auth_reply->value);
     result = Curl_http_input_auth(data, ts->resp->status == 407,
                                   auth_reply->value);
     if(result)
       return result;
     if(data->req.newurl) {
-      /* Inidicator that we should try again */
+      /* Indicator that we should try again */
       Curl_safefree(data->req.newurl);
-      tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+      h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data);
       return CURLE_OK;
     }
   }
@@ -937,9 +1034,9 @@
   return CURLE_RECV_ERROR;
 }
 
-static CURLcode CONNECT(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,
-                        struct tunnel_stream *ts)
+static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct tunnel_stream *ts)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -948,27 +1045,27 @@
   DEBUGASSERT(ts->authority);
   do {
     switch(ts->state) {
-    case TUNNEL_INIT:
+    case H2_TUNNEL_INIT:
       /* Prepare the CONNECT request and make a first attempt to send. */
-      DEBUGF(LOG_CF(data, cf, "CONNECT start for %s", ts->authority));
+      CURL_TRC_CF(data, cf, "[0] CONNECT start for %s", ts->authority);
       result = submit_CONNECT(cf, data, ts);
       if(result)
         goto out;
-      tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
+      h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_CONNECT:
+    case H2_TUNNEL_CONNECT:
       /* see that the request is completely sent */
-      result = h2_progress_ingress(cf, data);
+      result = proxy_h2_progress_ingress(cf, data);
       if(!result)
-        result = h2_progress_egress(cf, data);
-      if(result) {
-        tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+        result = proxy_h2_progress_egress(cf, data);
+      if(result && result != CURLE_AGAIN) {
+        h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
         break;
       }
 
       if(ts->has_final_response) {
-        tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
+        h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data);
       }
       else {
         result = CURLE_OK;
@@ -976,28 +1073,28 @@
       }
       /* FALLTHROUGH */
 
-    case TUNNEL_RESPONSE:
+    case H2_TUNNEL_RESPONSE:
       DEBUGASSERT(ts->has_final_response);
       result = inspect_response(cf, data, ts);
       if(result)
         goto out;
       break;
 
-    case TUNNEL_ESTABLISHED:
+    case H2_TUNNEL_ESTABLISHED:
       return CURLE_OK;
 
-    case TUNNEL_FAILED:
+    case H2_TUNNEL_FAILED:
       return CURLE_RECV_ERROR;
 
     default:
       break;
     }
 
-  } while(ts->state == TUNNEL_INIT);
+  } while(ts->state == H2_TUNNEL_INIT);
 
 out:
   if(result || ctx->tunnel.closed)
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
   return result;
 }
 
@@ -1043,10 +1140,10 @@
   /* for the secondary socket (FTP), use the "connect to host"
    * but ignore the "connect to port" (use the secondary port)
    */
-  result = CONNECT(cf, data, ts);
+  result = H2_CONNECT(cf, data, ts);
 
 out:
-  *done = (result == CURLE_OK) && (ts->state == TUNNEL_ESTABLISHED);
+  *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED);
   cf->connected = *done;
   CF_DATA_RESTORE(cf, save);
   return result;
@@ -1063,6 +1160,8 @@
     cf_h2_proxy_ctx_clear(ctx);
     CF_DATA_RESTORE(cf, save);
   }
+  if(cf->next)
+    cf->next->cft->do_close(cf->next, data);
 }
 
 static void cf_h2_proxy_destroy(struct Curl_cfilter *cf,
@@ -1082,7 +1181,7 @@
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) ||
-     (ctx && ctx->tunnel.state == TUNNEL_ESTABLISHED &&
+     (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED &&
       !Curl_bufq_is_empty(&ctx->tunnel.recvbuf)))
     return TRUE;
   return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
@@ -1117,8 +1216,8 @@
   ssize_t rv = 0;
 
   if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] REFUSED_STREAM, try again on a new "
-                  "connection", ctx->tunnel.stream_id));
+    CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
+                "connection", ctx->tunnel.stream_id);
     connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */
     *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
     return -1;
@@ -1138,7 +1237,8 @@
 
   *err = CURLE_OK;
   rv = 0;
-  DEBUGF(LOG_CF(data, cf, "handle_tunnel_close -> %zd, %d", rv, *err));
+  CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d",
+              ctx->tunnel.stream_id, rv, *err);
   return rv;
 }
 
@@ -1174,8 +1274,8 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "tunnel_recv(len=%zu) -> %zd, %d",
-                len, nread, *err));
+  CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d",
+              ctx->tunnel.stream_id, len, nread, *err);
   return nread;
 }
 
@@ -1188,14 +1288,14 @@
   struct cf_call_data save;
   CURLcode result;
 
-  if(ctx->tunnel.state != TUNNEL_ESTABLISHED) {
+  if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
     *err = CURLE_RECV_ERROR;
     return -1;
   }
   CF_DATA_SAVE(save, cf, data);
 
   if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) {
-    *err = h2_progress_ingress(cf, data);
+    *err = proxy_h2_progress_ingress(cf, data);
     if(*err)
       goto out;
   }
@@ -1203,117 +1303,241 @@
   nread = tunnel_recv(cf, data, buf, len, err);
 
   if(nread > 0) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] increase window by %zd",
-                  ctx->tunnel.stream_id, nread));
+    CURL_TRC_CF(data, cf, "[%d] increase window by %zd",
+                ctx->tunnel.stream_id, nread);
     nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread);
   }
 
-  result = h2_progress_egress(cf, data);
-  if(result) {
+  result = proxy_h2_progress_egress(cf, data);
+  if(result == CURLE_AGAIN) {
+    /* pending data to send, need to be called again. Ideally, we'd
+     * monitor the socket for POLLOUT, but we might not be in SENDING
+     * transfer state any longer and are unable to make this happen.
+     */
+    CURL_TRC_CF(data, cf, "[%d] egress blocked, DRAIN",
+                ctx->tunnel.stream_id);
+    drain_tunnel(cf, data, &ctx->tunnel);
+  }
+  else if(result) {
     *err = result;
     nread = -1;
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv(len=%zu) -> %zd %d",
-                ctx->tunnel.stream_id, len, nread, *err));
+  if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
+     (nread >= 0 || *err == CURLE_AGAIN)) {
+    /* data pending and no fatal error to report. Need to trigger
+     * draining to avoid stalling when no socket events happen. */
+    drain_tunnel(cf, data, &ctx->tunnel);
+  }
+  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d",
+              ctx->tunnel.stream_id, len, nread, *err);
   CF_DATA_RESTORE(cf, save);
   return nread;
 }
 
 static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
                                 struct Curl_easy *data,
-                                const void *mem, size_t len, CURLcode *err)
+                                const void *buf, size_t len, CURLcode *err)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_call_data save;
-  ssize_t nwritten = -1;
-  const unsigned char *buf = mem;
-  size_t start_len = len;
   int rv;
+  ssize_t nwritten;
+  CURLcode result;
+  int blocked = 0;
 
-  if(ctx->tunnel.state != TUNNEL_ESTABLISHED) {
+  if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
     *err = CURLE_SEND_ERROR;
     return -1;
   }
   CF_DATA_SAVE(save, cf, data);
 
-  while(len) {
+  if(ctx->tunnel.closed) {
+    nwritten = -1;
+    *err = CURLE_SEND_ERROR;
+    goto out;
+  }
+  else if(ctx->tunnel.upload_blocked_len) {
+    /* the data in `buf` has already been submitted or added to the
+     * buffers, but have been EAGAINed on the last invocation. */
+    DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len);
+    if(len < ctx->tunnel.upload_blocked_len) {
+      /* Did we get called again with a smaller `len`? This should not
+       * happen. We are not prepared to handle that. */
+      failf(data, "HTTP/2 proxy, send again with decreased length");
+      *err = CURLE_HTTP2;
+      nwritten = -1;
+      goto out;
+    }
+    nwritten = (ssize_t)ctx->tunnel.upload_blocked_len;
+    ctx->tunnel.upload_blocked_len = 0;
+    *err = CURLE_OK;
+  }
+  else {
     nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err);
-    if(nwritten <= 0) {
-      if(*err && *err != CURLE_AGAIN) {
-        DEBUGF(LOG_CF(data, cf, "error adding data to tunnel sendbuf: %d",
-               *err));
-        nwritten = -1;
+    if(nwritten < 0) {
+      if(*err != CURLE_AGAIN)
         goto out;
-      }
-      /* blocked */
       nwritten = 0;
     }
-    else {
-      DEBUGASSERT((size_t)nwritten <= len);
-      buf += (size_t)nwritten;
-      len -= (size_t)nwritten;
-    }
+  }
 
-    /* resume the tunnel stream and let the h2 session send, which
-     * triggers reading from tunnel.sendbuf */
+  if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
+    /* req body data is buffered, resume the potentially suspended stream */
     rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
     if(nghttp2_is_fatal(rv)) {
       *err = CURLE_SEND_ERROR;
       nwritten = -1;
       goto out;
     }
-    *err = h2_progress_egress(cf, data);
-    if(*err) {
-      nwritten = -1;
-      goto out;
-    }
-
-    if(!nwritten && Curl_bufq_is_full(&ctx->tunnel.sendbuf)) {
-      size_t rwin;
-      /* we could not add to the buffer and after session processing,
-       * it is still full. */
-      rwin = nghttp2_session_get_stream_remote_window_size(
-                                        ctx->h2, ctx->tunnel.stream_id);
-      DEBUGF(LOG_CF(data, cf, "cf_send: tunnel win %u/%zu",
-             nghttp2_session_get_remote_window_size(ctx->h2), rwin));
-      if(rwin == 0) {
-        /* We cannot upload more as the stream's remote window size
-         * is 0. We need to receive WIN_UPDATEs before we can continue.
-         */
-        data->req.keepon |= KEEP_SEND_HOLD;
-        DEBUGF(LOG_CF(data, cf, "pausing send as remote flow "
-               "window is exhausted"));
-      }
-      break;
-    }
   }
 
-  nwritten = start_len - len;
-  if(nwritten > 0) {
-    *err = CURLE_OK;
-  }
-  else if(ctx->tunnel.closed) {
+  result = proxy_h2_progress_ingress(cf, data);
+  if(result) {
+    *err = result;
     nwritten = -1;
-    *err = CURLE_SEND_ERROR;
+    goto out;
   }
-  else {
+
+  /* Call the nghttp2 send loop and flush to write ALL buffered data,
+   * headers and/or request body completely out to the network */
+  result = proxy_h2_progress_egress(cf, data);
+  if(result == CURLE_AGAIN) {
+    blocked = 1;
+  }
+  else if(result) {
+    *err = result;
     nwritten = -1;
+    goto out;
+  }
+  else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
+    /* although we wrote everything that nghttp2 wants to send now,
+     * there is data left in our stream send buffer unwritten. This may
+     * be due to the stream's HTTP/2 flow window being exhausted. */
+    blocked = 1;
+  }
+
+  if(blocked) {
+    /* Unable to send all data, due to connection blocked or H2 window
+     * exhaustion. Data is left in our stream buffer, or nghttp2's internal
+     * frame buffer or our network out buffer. */
+    size_t rwin = nghttp2_session_get_stream_remote_window_size(
+                    ctx->h2, ctx->tunnel.stream_id);
+    if(rwin == 0) {
+      /* H2 flow window exhaustion.
+       * FIXME: there is no way to HOLD all transfers that use this
+       * proxy connection AND to UNHOLD all of them again when the
+       * window increases.
+       * We *could* iterate over all data on this conn maybe? */
+      CURL_TRC_CF(data, cf, "[%d] remote flow "
+                  "window is exhausted", ctx->tunnel.stream_id);
+    }
+
+    /* Whatever the cause, we need to return CURL_EAGAIN for this call.
+     * We have unwritten state that needs us being invoked again and EAGAIN
+     * is the only way to ensure that. */
+    ctx->tunnel.upload_blocked_len = nwritten;
+    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
+                "blocked_len=%zu",
+                ctx->tunnel.stream_id, len,
+                nghttp2_session_get_remote_window_size(ctx->h2), rwin,
+                nwritten);
+    drain_tunnel(cf, data, &ctx->tunnel);
     *err = CURLE_AGAIN;
+    nwritten = -1;
+    goto out;
+  }
+  else if(proxy_h2_should_close_session(ctx)) {
+    /* nghttp2 thinks this session is done. If the stream has not been
+     * closed, this is an error state for out transfer */
+    if(ctx->tunnel.closed) {
+      *err = CURLE_SEND_ERROR;
+      nwritten = -1;
+    }
+    else {
+      CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session");
+      *err = CURLE_HTTP2;
+      nwritten = -1;
+    }
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d ",
-         start_len, nwritten, *err));
+  if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
+     (nwritten >= 0 || *err == CURLE_AGAIN)) {
+    /* data pending and no fatal error to report. Need to trigger
+     * draining to avoid stalling when no socket events happen. */
+    drain_tunnel(cf, data, &ctx->tunnel);
+  }
+  CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
+              "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
+              ctx->tunnel.stream_id, len, nwritten, *err,
+              nghttp2_session_get_stream_remote_window_size(
+                  ctx->h2, ctx->tunnel.stream_id),
+              nghttp2_session_get_remote_window_size(ctx->h2),
+              Curl_bufq_len(&ctx->tunnel.sendbuf),
+              Curl_bufq_len(&ctx->outbufq));
   CF_DATA_RESTORE(cf, save);
   return nwritten;
 }
 
+static bool proxy_h2_connisalive(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool *input_pending)
+{
+  struct cf_h2_proxy_ctx *ctx = cf->ctx;
+  bool alive = TRUE;
+
+  *input_pending = FALSE;
+  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+    return FALSE;
+
+  if(*input_pending) {
+    /* This happens before we've sent off a request and the connection is
+       not in use by any other transfer, there shouldn't be any data here,
+       only "protocol frames" */
+    CURLcode result;
+    ssize_t nread = -1;
+
+    *input_pending = FALSE;
+    nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
+    if(nread != -1) {
+      if(proxy_h2_process_pending_input(cf, data, &result) < 0)
+        /* immediate error, considered dead */
+        alive = FALSE;
+      else {
+        alive = !proxy_h2_should_close_session(ctx);
+      }
+    }
+    else if(result != CURLE_AGAIN) {
+      /* the read failed so let's say this is dead anyway */
+      alive = FALSE;
+    }
+  }
+
+  return alive;
+}
+
+static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool *input_pending)
+{
+  struct cf_h2_proxy_ctx *ctx = cf->ctx;
+  CURLcode result;
+  struct cf_call_data save;
+
+  CF_DATA_SAVE(save, cf, data);
+  result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending));
+  CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d",
+              result, *input_pending);
+  CF_DATA_RESTORE(cf, save);
+  return result;
+}
+
 struct Curl_cftype Curl_cft_h2_proxy = {
   "H2-PROXY",
   CF_TYPE_IP_CONNECT,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_h2_proxy_destroy,
   cf_h2_proxy_connect,
   cf_h2_proxy_close,
@@ -1323,7 +1547,7 @@
   cf_h2_proxy_send,
   cf_h2_proxy_recv,
   Curl_cf_def_cntrl,
-  Curl_cf_def_conn_is_alive,
+  cf_h2_proxy_is_alive,
   Curl_cf_def_conn_keep_alive,
   Curl_cf_def_query,
 };
diff --git a/Utilities/cmcurl/lib/cf-haproxy.c b/Utilities/cmcurl/lib/cf-haproxy.c
index 86d7fd1..39ac415 100644
--- a/Utilities/cmcurl/lib/cf-haproxy.c
+++ b/Utilities/cmcurl/lib/cf-haproxy.c
@@ -30,7 +30,7 @@
 #include "urldata.h"
 #include "cfilters.h"
 #include "cf-haproxy.h"
-#include "curl_log.h"
+#include "curl_trc.h"
 #include "multiif.h"
 
 /* The last 3 #include files should be in this order */
@@ -71,6 +71,7 @@
   struct cf_haproxy_ctx *ctx = cf->ctx;
   CURLcode result;
   const char *tcp_version;
+  const char *client_ip;
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(ctx->state == HAPROXY_INIT);
@@ -82,10 +83,14 @@
 #endif /* USE_UNIX_SOCKETS */
   /* Emit the correct prefix for IPv6 */
   tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
+  if(data->set.str[STRING_HAPROXY_CLIENT_IP])
+    client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
+  else
+    client_ip = data->info.conn_local_ip;
 
   result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
                          tcp_version,
-                         data->info.conn_local_ip,
+                         client_ip,
                          data->info.conn_primary_ip,
                          data->info.conn_local_port,
                          data->info.conn_primary_port);
@@ -110,7 +115,7 @@
     return CURLE_OK;
   }
 
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -152,18 +157,18 @@
                                struct Curl_easy *data)
 {
   (void)data;
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   cf_haproxy_ctx_free(cf->ctx);
 }
 
 static void cf_haproxy_close(struct Curl_cfilter *cf,
                              struct Curl_easy *data)
 {
-  DEBUGF(LOG_CF(data, cf, "close"));
+  CURL_TRC_CF(data, cf, "close");
   cf->connected = FALSE;
   cf_haproxy_ctx_reset(cf->ctx);
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 
 static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/cf-https-connect.c b/Utilities/cmcurl/lib/cf-https-connect.c
index d03cd1e..be54aec 100644
--- a/Utilities/cmcurl/lib/cf-https-connect.c
+++ b/Utilities/cmcurl/lib/cf-https-connect.c
@@ -28,7 +28,7 @@
 
 #include "urldata.h"
 #include <curl/curl.h>
-#include "curl_log.h"
+#include "curl_trc.h"
 #include "cfilters.h"
 #include "connect.h"
 #include "multiif.h"
@@ -165,9 +165,9 @@
   if(winner != &ctx->h21_baller)
     cf_hc_baller_reset(&ctx->h21_baller, data);
 
-  DEBUGF(LOG_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
-                winner->name, (int)Curl_timediff(Curl_now(), winner->started),
-                cf_hc_baller_reply_ms(winner, data)));
+  CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
+              winner->name, (int)Curl_timediff(Curl_now(), winner->started),
+              cf_hc_baller_reply_ms(winner, data));
   cf->next = winner->cf;
   winner->cf = NULL;
 
@@ -218,16 +218,16 @@
 
   elapsed_ms = Curl_timediff(now, ctx->started);
   if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
-    DEBUGF(LOG_CF(data, cf, "hard timeout of %dms reached, starting h21",
-                  ctx->hard_eyeballs_timeout_ms));
+    CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21",
+                ctx->hard_eyeballs_timeout_ms);
     return TRUE;
   }
 
   if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
     if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) {
-      DEBUGF(LOG_CF(data, cf, "soft timeout of %dms reached, h3 has not "
-                    "seen any data, starting h21",
-                    ctx->soft_eyeballs_timeout_ms));
+      CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not "
+                  "seen any data, starting h21",
+                  ctx->soft_eyeballs_timeout_ms);
       return TRUE;
     }
     /* set the effective hard timeout again */
@@ -258,7 +258,7 @@
     DEBUGASSERT(!ctx->h3_baller.cf);
     DEBUGASSERT(!ctx->h21_baller.cf);
     DEBUGASSERT(!cf->next);
-    DEBUGF(LOG_CF(data, cf, "connect, init"));
+    CURL_TRC_CF(data, cf, "connect, init");
     ctx->started = now;
     if(ctx->h3_baller.enabled) {
       cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC);
@@ -286,7 +286,7 @@
     }
 
     if(cf_hc_baller_is_active(&ctx->h21_baller)) {
-      DEBUGF(LOG_CF(data, cf, "connect, check h21"));
+      CURL_TRC_CF(data, cf, "connect, check h21");
       result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done);
       if(!result && *done) {
         result = baller_connected(cf, data, &ctx->h21_baller);
@@ -297,7 +297,7 @@
     if((!ctx->h3_baller.enabled || ctx->h3_baller.result) &&
        (!ctx->h21_baller.enabled || ctx->h21_baller.result)) {
       /* both failed or disabled. we give up */
-      DEBUGF(LOG_CF(data, cf, "connect, all failed"));
+      CURL_TRC_CF(data, cf, "connect, all failed");
       result = ctx->result = ctx->h3_baller.enabled?
                               ctx->h3_baller.result : ctx->h21_baller.result;
       ctx->state = CF_HC_FAILURE;
@@ -321,7 +321,7 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+  CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
   return result;
 }
 
@@ -345,7 +345,7 @@
     if(!cf_hc_baller_is_active(b))
       continue;
     brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks);
-    DEBUGF(LOG_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc));
+    CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc);
     if(!brc)
       continue;
     for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) {
@@ -359,7 +359,7 @@
       }
     }
   }
-  DEBUGF(LOG_CF(data, cf, "get_selected_socks -> %x", rc));
+  CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc);
   return rc;
 }
 
@@ -371,14 +371,14 @@
   if(cf->connected)
     return cf->next->cft->has_data_pending(cf->next, data);
 
-  DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending"));
+  CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
   return cf_hc_baller_data_pending(&ctx->h3_baller, data)
          || cf_hc_baller_data_pending(&ctx->h21_baller, data);
 }
 
-static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data,
-                                          int query)
+static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
+                                              struct Curl_easy *data,
+                                              int query)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
   struct Curl_cfilter *cfb;
@@ -408,12 +408,12 @@
     switch(query) {
     case CF_QUERY_TIMER_CONNECT: {
       struct curltime *when = pres2;
-      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
+      *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
       return CURLE_OK;
     }
     case CF_QUERY_TIMER_APPCONNECT: {
       struct curltime *when = pres2;
-      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
+      *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
       return CURLE_OK;
     }
     default:
@@ -427,12 +427,12 @@
 
 static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
-  DEBUGF(LOG_CF(data, cf, "close"));
+  CURL_TRC_CF(data, cf, "close");
   cf_hc_reset(cf, data);
   cf->connected = FALSE;
 
   if(cf->next) {
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
     Curl_conn_cf_discard_chain(&cf->next, data);
   }
 }
@@ -442,7 +442,7 @@
   struct cf_hc_ctx *ctx = cf->ctx;
 
   (void)data;
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   cf_hc_reset(cf, data);
   Curl_safefree(ctx);
 }
@@ -450,7 +450,7 @@
 struct Curl_cftype Curl_cft_http_connect = {
   "HTTPS-CONNECT",
   0,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_hc_destroy,
   cf_hc_connect,
   cf_hc_close,
diff --git a/Utilities/cmcurl/lib/cf-socket.c b/Utilities/cmcurl/lib/cf-socket.c
index 960979b..ce3f9e9 100644
--- a/Utilities/cmcurl/lib/cf-socket.c
+++ b/Utilities/cmcurl/lib/cf-socket.c
@@ -71,6 +71,7 @@
 #include "warnless.h"
 #include "conncache.h"
 #include "multihandle.h"
+#include "rand.h"
 #include "share.h"
 #include "version_win32.h"
 
@@ -390,6 +391,7 @@
 }
 #endif
 
+#ifndef CURL_DISABLE_BINDLOCAL
 static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
                           curl_socket_t sockfd, int af, unsigned int scope)
 {
@@ -444,29 +446,24 @@
     /* interface */
     if(!is_host) {
 #ifdef SO_BINDTODEVICE
-      /* I am not sure any other OSs than Linux that provide this feature,
-       * and at the least I cannot test. --Ben
+      /*
+       * This binds the local socket to a particular interface. This will
+       * force even requests to other local interfaces to go out the external
+       * interface. Only bind to the interface when specified as interface,
+       * not just as a hostname or ip address.
        *
-       * This feature allows one to tightly bind the local socket to a
-       * particular interface.  This will force even requests to other
-       * local interfaces to go out the external interface.
-       *
-       *
-       * Only bind to the interface when specified as interface, not just
-       * as a hostname or ip address.
-       *
-       * interface might be a VRF, eg: vrf-blue, which means it cannot be
-       * converted to an IP address and would fail Curl_if2ip. Simply try
-       * to use it straight away.
+       * The interface might be a VRF, eg: vrf-blue, which means it cannot be
+       * converted to an IP address and would fail Curl_if2ip. Simply try to
+       * use it straight away.
        */
       if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
                     dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
-        /* This is typically "errno 1, error: Operation not permitted" if
-         * you're not running as root or another suitable privileged
-         * user.
-         * If it succeeds it means the parameter was a valid interface and
-         * not an IP address. Return immediately.
+        /* This is often "errno 1, error: Operation not permitted" if you're
+         * not running as root or another suitable privileged user. If it
+         * succeeds it means the parameter was a valid interface and not an IP
+         * address. Return immediately.
          */
+        infof(data, "socket successfully bound to interface '%s'", dev);
         return CURLE_OK;
       }
 #endif
@@ -632,8 +629,8 @@
       port++; /* try next port */
       if(port == 0)
         break;
-      infof(data, "Bind to local port %hu failed, trying next", port - 1);
-      /* We re-use/clobber the port variable here below */
+      infof(data, "Bind to local port %d failed, trying next", port - 1);
+      /* We reuse/clobber the port variable here below */
       if(sock->sa_family == AF_INET)
         si4->sin_port = ntohs(port);
 #ifdef ENABLE_IPV6
@@ -653,6 +650,7 @@
 
   return CURLE_INTERFACE_FAILED;
 }
+#endif
 
 /*
  * verifyconnect() returns TRUE if the connect really has happened.
@@ -727,8 +725,6 @@
 static CURLcode socket_connect_result(struct Curl_easy *data,
                                       const char *ipaddress, int error)
 {
-  char buffer[STRERROR_LEN];
-
   switch(error) {
   case EINPROGRESS:
   case EWOULDBLOCK:
@@ -745,8 +741,15 @@
 
   default:
     /* unknown error, fallthrough and try another address! */
-    infof(data, "Immediate connect fail for %s: %s",
-          ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+    (void)ipaddress;
+#else
+    {
+      char buffer[STRERROR_LEN];
+      infof(data, "Immediate connect fail for %s: %s",
+            ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+    }
+#endif
     data->state.os_errno = error;
     /* connect failed */
     return CURLE_COULDNT_CONNECT;
@@ -775,6 +778,12 @@
   struct curltime connected_at;      /* when socket connected/got first byte */
   struct curltime first_byte_at;     /* when first byte was recvd */
   int error;                         /* errno of last failure or 0 */
+#ifdef DEBUGBUILD
+  int wblock_percent;                /* percent of writes doing EAGAIN */
+  int wpartial_percent;              /* percent of bytes written in send */
+  int rblock_percent;                /* percent of reads doing EAGAIN */
+  size_t recv_max;                  /* max enforced read size */
+#endif
   BIT(got_first_byte);               /* if first byte was received */
   BIT(accepted);                     /* socket was accepted, not connected */
   BIT(active);
@@ -790,6 +799,34 @@
   ctx->transport = transport;
   Curl_sock_assign_addr(&ctx->addr, ai, transport);
   Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
+#ifdef DEBUGBUILD
+  {
+    char *p = getenv("CURL_DBG_SOCK_WBLOCK");
+    if(p) {
+      long l = strtol(p, NULL, 10);
+      if(l >= 0 && l <= 100)
+        ctx->wblock_percent = (int)l;
+    }
+    p = getenv("CURL_DBG_SOCK_WPARTIAL");
+    if(p) {
+      long l = strtol(p, NULL, 10);
+      if(l >= 0 && l <= 100)
+        ctx->wpartial_percent = (int)l;
+    }
+    p = getenv("CURL_DBG_SOCK_RBLOCK");
+    if(p) {
+      long l = strtol(p, NULL, 10);
+      if(l >= 0 && l <= 100)
+        ctx->rblock_percent = (int)l;
+    }
+    p = getenv("CURL_DBG_SOCK_RMAX");
+    if(p) {
+      long l = strtol(p, NULL, 10);
+      if(l >= 0)
+        ctx->recv_max = (size_t)l;
+    }
+  }
+#endif
 }
 
 struct reader_ctx {
@@ -836,8 +873,8 @@
       nread = -1;
     }
   }
-  DEBUGF(LOG_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
-               len, (int)nread, *err));
+  CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d",
+              len, (int)nread, *err);
   return nread;
 }
 
@@ -852,14 +889,14 @@
        * closed it) and we just forget about it.
        */
       if(ctx->sock == cf->conn->sock[cf->sockindex]) {
-        DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                      ", active)", ctx->sock));
+        CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+                    ", active)", ctx->sock);
         socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
         cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
       }
       else {
-        DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                      ") no longer at conn->sock[], discarding", ctx->sock));
+        CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+                    ") no longer at conn->sock[], discarding", ctx->sock);
         /* TODO: we do not want this to happen. Need to check which
          * code is messing with conn->sock[cf->sockindex] */
       }
@@ -869,9 +906,9 @@
     }
     else {
       /* this is our local socket, we did never publish it */
-      DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                    ", not active)", ctx->sock));
-      sclose(ctx->sock);
+      CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+                  ", not active)", ctx->sock);
+      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
       ctx->sock = CURL_SOCKET_BAD;
     }
     Curl_bufq_reset(&ctx->recvbuf);
@@ -889,7 +926,7 @@
   struct cf_socket_ctx *ctx = cf->ctx;
 
   cf_socket_close(cf, data);
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   Curl_bufq_free(&ctx->recvbuf);
   free(ctx);
   cf->ctx = NULL;
@@ -901,22 +938,26 @@
   struct cf_socket_ctx *ctx = cf->ctx;
 
 #ifdef HAVE_GETSOCKNAME
-  char buffer[STRERROR_LEN];
-  struct Curl_sockaddr_storage ssloc;
-  curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
+  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
+    /* TFTP does not connect, so it cannot get the IP like this */
 
-  memset(&ssloc, 0, sizeof(ssloc));
-  if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
-    int error = SOCKERRNO;
-    failf(data, "getsockname() failed with errno %d: %s",
-          error, Curl_strerror(error, buffer, sizeof(buffer)));
-    return CURLE_FAILED_INIT;
-  }
-  if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
-                       ctx->l_ip, &ctx->l_port)) {
-    failf(data, "ssloc inet_ntop() failed with errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    return CURLE_FAILED_INIT;
+    char buffer[STRERROR_LEN];
+    struct Curl_sockaddr_storage ssloc;
+    curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
+
+    memset(&ssloc, 0, sizeof(ssloc));
+    if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
+      int error = SOCKERRNO;
+      failf(data, "getsockname() failed with errno %d: %s",
+            error, Curl_strerror(error, buffer, sizeof(buffer)));
+      return CURLE_FAILED_INIT;
+    }
+    if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+                         ctx->l_ip, &ctx->l_port)) {
+      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
+            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+      return CURLE_FAILED_INIT;
+    }
   }
 #else
   (void)data;
@@ -953,7 +994,6 @@
   bool isconnected = FALSE;
   CURLcode result = CURLE_COULDNT_CONNECT;
   bool is_tcp;
-  const char *ipmsg;
 
   (void)data;
   DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
@@ -966,15 +1006,20 @@
   if(result)
     goto out;
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  {
+    const char *ipmsg;
 #ifdef ENABLE_IPV6
-  if(ctx->addr.family == AF_INET6) {
-    set_ipv6_v6only(ctx->sock, 0);
-    ipmsg = "  Trying [%s]:%d...";
-  }
-  else
+    if(ctx->addr.family == AF_INET6) {
+      set_ipv6_v6only(ctx->sock, 0);
+      ipmsg = "  Trying [%s]:%d...";
+    }
+    else
 #endif
-    ipmsg = "  Trying %s:%d...";
-  infof(data, ipmsg, ctx->r_ip, ctx->r_port);
+      ipmsg = "  Trying %s:%d...";
+    infof(data, ipmsg, ctx->r_ip, ctx->r_port);
+  }
+#endif
 
 #ifdef ENABLE_IPV6
   is_tcp = (ctx->addr.family == AF_INET
@@ -1010,6 +1055,7 @@
     }
   }
 
+#ifndef CURL_DISABLE_BINDLOCAL
   /* possibly bind the local end to an IP, interface or port */
   if(ctx->addr.family == AF_INET
 #ifdef ENABLE_IPV6
@@ -1027,6 +1073,7 @@
       goto out;
     }
   }
+#endif
 
   /* set socket non-blocking */
   (void)curlx_nonblock(ctx->sock, TRUE);
@@ -1043,8 +1090,8 @@
     ctx->connected_at = Curl_now();
     cf->connected = TRUE;
   }
-  DEBUGF(LOG_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
-                result, ctx->sock));
+  CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
+              result, ctx->sock);
   return result;
 }
 
@@ -1151,7 +1198,7 @@
   rc = SOCKET_WRITABLE(ctx->sock, 0);
 
   if(rc == 0) { /* no connection yet */
-    DEBUGF(LOG_CF(data, cf, "not connected yet"));
+    CURL_TRC_CF(data, cf, "not connected yet");
     return CURLE_OK;
   }
   else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
@@ -1161,7 +1208,7 @@
       set_local_ip(cf, data);
       *done = TRUE;
       cf->connected = TRUE;
-      DEBUGF(LOG_CF(data, cf, "connected"));
+      CURL_TRC_CF(data, cf, "connected");
       return CURLE_OK;
     }
   }
@@ -1241,11 +1288,34 @@
   struct cf_socket_ctx *ctx = cf->ctx;
   curl_socket_t fdsave;
   ssize_t nwritten;
+  size_t orig_len = len;
 
   *err = CURLE_OK;
   fdsave = cf->conn->sock[cf->sockindex];
   cf->conn->sock[cf->sockindex] = ctx->sock;
 
+#ifdef DEBUGBUILD
+  /* simulate network blocking/partial writes */
+  if(ctx->wblock_percent > 0) {
+    unsigned char c;
+    Curl_rand(data, &c, 1);
+    if(c >= ((100-ctx->wblock_percent)*256/100)) {
+      CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
+      *err = CURLE_AGAIN;
+      nwritten = -1;
+      cf->conn->sock[cf->sockindex] = fdsave;
+      return nwritten;
+    }
+  }
+  if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
+    len = len * ctx->wpartial_percent / 100;
+    if(!len)
+      len = 1;
+    CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
+                orig_len, len);
+  }
+#endif
+
 #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
   if(cf->conn->bits.tcp_fastopen) {
     nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
@@ -1284,8 +1354,8 @@
     }
   }
 
-  DEBUGF(LOG_CF(data, cf, "send(len=%zu) -> %d, err=%d",
-                len, (int)nwritten, *err));
+  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
+              orig_len, (int)nwritten, *err);
   cf->conn->sock[cf->sockindex] = fdsave;
   return nwritten;
 }
@@ -1302,8 +1372,29 @@
   fdsave = cf->conn->sock[cf->sockindex];
   cf->conn->sock[cf->sockindex] = ctx->sock;
 
+#ifdef DEBUGBUILD
+  /* simulate network blocking/partial reads */
+  if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
+    unsigned char c;
+    Curl_rand(data, &c, 1);
+    if(c >= ((100-ctx->rblock_percent)*256/100)) {
+      CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
+      *err = CURLE_AGAIN;
+      nread = -1;
+      cf->conn->sock[cf->sockindex] = fdsave;
+      return nread;
+    }
+  }
+  if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
+    size_t orig_len = len;
+    len = ctx->recv_max;
+    CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
+                orig_len, len);
+  }
+#endif
+
   if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
-    DEBUGF(LOG_CF(data, cf, "recv from buffer"));
+    CURL_TRC_CF(data, cf, "recv from buffer");
     nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
   }
   else {
@@ -1320,7 +1411,7 @@
       if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
         /* we have a partial read with an error. need to deliver
          * what we got, return the error later. */
-        DEBUGF(LOG_CF(data, cf, "partial read: empty buffer first"));
+        CURL_TRC_CF(data, cf, "partial read: empty buffer first");
         nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
       }
       else if(nwritten < 0) {
@@ -1333,7 +1424,7 @@
         nread = 0;
       }
       else {
-        DEBUGF(LOG_CF(data, cf, "buffered %zd additional bytes", nwritten));
+        CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
         nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
       }
     }
@@ -1343,8 +1434,8 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
-                *err));
+  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
+              *err);
   if(nread > 0 && !ctx->got_first_byte) {
     ctx->first_byte_at = Curl_now();
     ctx->got_first_byte = TRUE;
@@ -1356,26 +1447,31 @@
 static void conn_set_primary_ip(struct Curl_cfilter *cf,
                                 struct Curl_easy *data)
 {
-  struct cf_socket_ctx *ctx = cf->ctx;
 #ifdef HAVE_GETPEERNAME
-  char buffer[STRERROR_LEN];
-  struct Curl_sockaddr_storage ssrem;
-  curl_socklen_t plen;
-  int port;
+  struct cf_socket_ctx *ctx = cf->ctx;
+  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
+    /* TFTP does not connect the endpoint: getpeername() failed with errno
+       107: Transport endpoint is not connected */
 
-  plen = sizeof(ssrem);
-  memset(&ssrem, 0, plen);
-  if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
-    int error = SOCKERRNO;
-    failf(data, "getpeername() failed with errno %d: %s",
-          error, Curl_strerror(error, buffer, sizeof(buffer)));
-    return;
-  }
-  if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
-                       cf->conn->primary_ip, &port)) {
-    failf(data, "ssrem inet_ntop() failed with errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    return;
+    char buffer[STRERROR_LEN];
+    struct Curl_sockaddr_storage ssrem;
+    curl_socklen_t plen;
+    int port;
+
+    plen = sizeof(ssrem);
+    memset(&ssrem, 0, plen);
+    if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
+      int error = SOCKERRNO;
+      failf(data, "getpeername() failed with errno %d: %s",
+            error, Curl_strerror(error, buffer, sizeof(buffer)));
+      return;
+    }
+    if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+                         cf->conn->primary_ip, &port)) {
+      failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+      return;
+    }
   }
 #else
   cf->conn->primary_ip[0] = 0;
@@ -1446,19 +1542,19 @@
 
   r = Curl_poll(pfd, 1, 0);
   if(r < 0) {
-    DEBUGF(LOG_CF(data, cf, "is_alive: poll error, assume dead"));
+    CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
     return FALSE;
   }
   else if(r == 0) {
-    DEBUGF(LOG_CF(data, cf, "is_alive: poll timeout, assume alive"));
+    CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
     return TRUE;
   }
   else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
-    DEBUGF(LOG_CF(data, cf, "is_alive: err/hup/etc events, assume dead"));
+    CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
     return FALSE;
   }
 
-  DEBUGF(LOG_CF(data, cf, "is_alive: valid events, looks alive"));
+  CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
   *input_pending = TRUE;
   return TRUE;
 }
@@ -1511,7 +1607,7 @@
 struct Curl_cftype Curl_cft_tcp = {
   "TCP",
   CF_TYPE_IP_CONNECT,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_socket_destroy,
   cf_tcp_connect,
   cf_socket_close,
@@ -1572,10 +1668,10 @@
     return socket_connect_result(data, ctx->r_ip, SOCKERRNO);
   }
   set_local_ip(cf, data);
-  DEBUGF(LOG_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
-                " connected: [%s:%d] -> [%s:%d]",
-                (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
-                ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port));
+  CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
+              " connected: [%s:%d] -> [%s:%d]",
+              (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
+              ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port);
 
   (void)curlx_nonblock(ctx->sock, TRUE);
   switch(ctx->addr.family) {
@@ -1615,7 +1711,7 @@
   if(ctx->sock == CURL_SOCKET_BAD) {
     result = cf_socket_open(cf, data);
     if(result) {
-      DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), open failed -> %d", result));
+      CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
       goto out;
     }
 
@@ -1623,13 +1719,13 @@
       result = cf_udp_setup_quic(cf, data);
       if(result)
         goto out;
-      DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%"
-                    CURL_FORMAT_SOCKET_T " (%s:%d)",
-                    ctx->sock, ctx->l_ip, ctx->l_port));
+      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
+                  CURL_FORMAT_SOCKET_T " (%s:%d)",
+                  ctx->sock, ctx->l_ip, ctx->l_port);
     }
     else {
-      DEBUGF(LOG_CF(data, cf, "cf_udp_connect(), opened socket=%"
-                    CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock));
+      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
+                  CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
     }
     *done = TRUE;
     cf->connected = TRUE;
@@ -1641,7 +1737,7 @@
 struct Curl_cftype Curl_cft_udp = {
   "UDP",
   CF_TYPE_IP_CONNECT,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_socket_destroy,
   cf_udp_connect,
   cf_socket_close,
@@ -1692,7 +1788,7 @@
 struct Curl_cftype Curl_cft_unix = {
   "UNIX",
   CF_TYPE_IP_CONNECT,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_socket_destroy,
   cf_tcp_connect,
   cf_socket_close,
@@ -1756,7 +1852,7 @@
 struct Curl_cftype Curl_cft_tcp_accept = {
   "TCP-ACCEPT",
   CF_TYPE_IP_CONNECT,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_socket_destroy,
   cf_tcp_accept_connect,
   cf_socket_close,
@@ -1801,8 +1897,8 @@
   ctx->active = TRUE;
   ctx->connected_at = Curl_now();
   cf->connected = TRUE;
-  DEBUGF(LOG_CF(data, cf, "Curl_conn_tcp_listen_set(%"
-                CURL_FORMAT_SOCKET_T ")", ctx->sock));
+  CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
+              CURL_FORMAT_SOCKET_T ")", ctx->sock);
 
 out:
   if(result) {
@@ -1866,9 +1962,9 @@
   ctx->accepted = TRUE;
   ctx->connected_at = Curl_now();
   cf->connected = TRUE;
-  DEBUGF(LOG_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
-                ", remote=%s port=%d)",
-                ctx->sock, ctx->r_ip, ctx->r_port));
+  CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
+              ", remote=%s port=%d)",
+              ctx->sock, ctx->r_ip, ctx->r_port);
 
   return CURLE_OK;
 }
@@ -1913,4 +2009,3 @@
   }
   return CURLE_FAILED_INIT;
 }
-
diff --git a/Utilities/cmcurl/lib/cfilters.c b/Utilities/cmcurl/lib/cfilters.c
index 291c823..f74eb40 100644
--- a/Utilities/cmcurl/lib/cfilters.c
+++ b/Utilities/cmcurl/lib/cfilters.c
@@ -50,7 +50,7 @@
 {
   cf->connected = FALSE;
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 #endif
 
@@ -161,7 +161,7 @@
   /* it is valid to call that without filters being present */
   cf = data->conn->cfilter[index];
   if(cf) {
-    cf->cft->close(cf, data);
+    cf->cft->do_close(cf, data);
   }
 }
 
@@ -179,7 +179,7 @@
   if(cf) {
     return cf->cft->do_recv(cf, data, buf, len, code);
   }
-  failf(data, CMSGI(data->conn, num, "recv: no filter connected"));
+  failf(data, "recv: no filter connected");
   *code = CURLE_FAILED_INIT;
   return -1;
 }
@@ -198,7 +198,7 @@
   if(cf) {
     return cf->cft->do_send(cf, data, mem, len, code);
   }
-  failf(data, CMSGI(data->conn, num, "send: no filter connected"));
+  failf(data, "send: no filter connected");
   DEBUGASSERT(0);
   *code = CURLE_FAILED_INIT;
   return -1;
@@ -238,7 +238,7 @@
   cf->conn = conn;
   cf->sockindex = index;
   conn->cfilter[index] = cf;
-  DEBUGF(LOG_CF(data, cf, "added"));
+  CURL_TRC_CF(data, cf, "added");
 }
 
 void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
@@ -293,14 +293,14 @@
                               bool blocking, bool *done)
 {
   if(cf)
-    return cf->cft->connect(cf, data, blocking, done);
+    return cf->cft->do_connect(cf, data, blocking, done);
   return CURLE_FAILED_INIT;
 }
 
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   if(cf)
-    cf->cft->close(cf, data);
+    cf->cft->do_close(cf, data);
 }
 
 int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
@@ -348,7 +348,7 @@
 
   *done = cf->connected;
   if(!*done) {
-    result = cf->cft->connect(cf, data, blocking, done);
+    result = cf->cft->do_connect(cf, data, blocking, done);
     if(!result && *done) {
       Curl_conn_ev_update_info(data, data->conn);
       conn_report_connect_stats(data, data->conn);
@@ -646,4 +646,3 @@
                               &n, NULL) : CURLE_UNKNOWN_OPTION;
   return (result || n <= 0)? 1 : (size_t)n;
 }
-
diff --git a/Utilities/cmcurl/lib/cfilters.h b/Utilities/cmcurl/lib/cfilters.h
index 70dcbe7..2c65264 100644
--- a/Utilities/cmcurl/lib/cfilters.h
+++ b/Utilities/cmcurl/lib/cfilters.h
@@ -168,8 +168,8 @@
   int flags;                              /* flags of filter type */
   int log_level;                          /* log level for such filters */
   Curl_cft_destroy_this *destroy;         /* destroy resources of this cf */
-  Curl_cft_connect *connect;              /* establish connection */
-  Curl_cft_close *close;                  /* close conn */
+  Curl_cft_connect *do_connect;           /* establish connection */
+  Curl_cft_close *do_close;               /* close conn */
   Curl_cft_get_host *get_host;            /* host filter talks to */
   Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */
   Curl_cft_data_pending *has_data_pending;/* conn has data pending */
diff --git a/Utilities/cmcurl/lib/conncache.c b/Utilities/cmcurl/lib/conncache.c
index a21409c..93d8768 100644
--- a/Utilities/cmcurl/lib/conncache.c
+++ b/Utilities/cmcurl/lib/conncache.c
@@ -107,6 +107,7 @@
   connc->closure_handle = curl_easy_init();
   if(!connc->closure_handle)
     return 1; /* bad */
+  connc->closure_handle->internal = true;
 
   Curl_hash_init(&connc->hash, size, Curl_hash_str,
                  Curl_str_key_compare, free_bundle_hash_entry);
diff --git a/Utilities/cmcurl/lib/conncache.h b/Utilities/cmcurl/lib/conncache.h
index 959767d..c60f844 100644
--- a/Utilities/cmcurl/lib/conncache.h
+++ b/Utilities/cmcurl/lib/conncache.h
@@ -39,7 +39,8 @@
 struct conncache {
   struct Curl_hash hash;
   size_t num_conn;
-  long next_connection_id;
+  curl_off_t next_connection_id;
+  curl_off_t next_easy_id;
   struct curltime last_cleanup;
   /* handle used for closing cached connections */
   struct Curl_easy *closure_handle;
diff --git a/Utilities/cmcurl/lib/connect.c b/Utilities/cmcurl/lib/connect.c
index ed55121..c7ba3e2 100644
--- a/Utilities/cmcurl/lib/connect.c
+++ b/Utilities/cmcurl/lib/connect.c
@@ -253,7 +253,7 @@
 }
 
 struct connfind {
-  long id_tofind;
+  curl_off_t id_tofind;
   struct connectdata *found;
 };
 
@@ -381,6 +381,11 @@
   struct curltime started;
 };
 
+/* when there are more than one IP address left to use, this macro returns how
+   much of the given timeout to spend on *this* attempt */
+#define TIMEOUT_LARGE 600
+#define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms)
+
 static CURLcode eyeballer_new(struct eyeballer **pballer,
                               cf_ip_connect_create *cf_create,
                               const struct Curl_addrinfo *addr,
@@ -408,7 +413,7 @@
   baller->primary = primary;
   baller->delay_ms = delay_ms;
   baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)?
-                        timeout_ms / 2 : timeout_ms;
+    USETIME(timeout_ms) : timeout_ms;
   baller->timeout_id = timeout_id;
   baller->result = CURLE_COULDNT_CONNECT;
 
@@ -475,7 +480,7 @@
 
 out:
   if(result) {
-    DEBUGF(LOG_CF(data, cf, "%s failed", baller->name));
+    CURL_TRC_CF(data, cf, "%s failed", baller->name);
     baller_close(baller, data);
   }
   if(cf_prev)
@@ -501,7 +506,7 @@
   while(baller->addr) {
     baller->started = Curl_now();
     baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
-                         timeoutms / 2 : timeoutms;
+      USETIME(timeoutms) : timeoutms;
     baller_initiate(cf, data, baller);
     if(!baller->result)
       break;
@@ -601,8 +606,8 @@
       continue;
     }
     baller->result = baller_connect(cf, data, baller, &now, connected);
-    DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d",
-                  baller->name, baller->result, *connected));
+    CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d",
+                baller->name, baller->result, *connected);
 
     if(!baller->result) {
       if(*connected) {
@@ -623,12 +628,13 @@
       }
       baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
       if(baller->is_done) {
-        DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
+        CURL_TRC_CF(data, cf, "%s done", baller->name);
       }
       else {
         /* next attempt was started */
-        DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name));
+        CURL_TRC_CF(data, cf, "%s trying next", baller->name);
         ++ongoing;
+        Curl_expire(data, 0, EXPIRE_RUN_NOW);
       }
     }
   }
@@ -641,7 +647,7 @@
   /* Nothing connected, check the time before we might
    * start new ballers or return ok. */
   if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
-    failf(data, "Connection timeout after %ld ms",
+    failf(data, "Connection timeout after %" CURL_FORMAT_CURL_OFF_T " ms",
           Curl_timediff(now, data->progress.t_startsingle));
     return CURLE_OPERATION_TIMEDOUT;
   }
@@ -661,12 +667,12 @@
           Curl_timediff(now, ctx->started) >= baller->delay_ms) {
         baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
         if(baller->is_done) {
-          DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
+          CURL_TRC_CF(data, cf, "%s done", baller->name);
         }
         else {
-          DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%"
-                        CURL_FORMAT_TIMEDIFF_T "ms)",
-                        baller->name, baller->timeoutms));
+          CURL_TRC_CF(data, cf, "%s starting (timeout=%"
+                      CURL_FORMAT_TIMEDIFF_T "ms)",
+                      baller->name, baller->timeoutms);
           ++ongoing;
           ++added;
         }
@@ -683,14 +689,14 @@
   }
 
   /* all ballers have failed to connect. */
-  DEBUGF(LOG_CF(data, cf, "all eyeballers failed"));
+  CURL_TRC_CF(data, cf, "all eyeballers failed");
   result = CURLE_COULDNT_CONNECT;
   for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
     struct eyeballer *baller = ctx->baller[i];
-    DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d",
-                  baller?baller->name:NULL,
-                  baller?baller->has_started:0,
-                  baller?baller->result:0));
+    CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
+                baller?baller->name:NULL,
+                baller?baller->has_started:0,
+                baller?baller->result:0);
     if(baller && baller->has_started && baller->result) {
       result = baller->result;
       break;
@@ -803,9 +809,9 @@
                           timeout_ms,  EXPIRE_DNS_PER_NAME);
   if(result)
     return result;
-  DEBUGF(LOG_CF(data, cf, "created %s (timeout %"
-                CURL_FORMAT_TIMEDIFF_T "ms)",
-                ctx->baller[0]->name, ctx->baller[0]->timeoutms));
+  CURL_TRC_CF(data, cf, "created %s (timeout %"
+              CURL_FORMAT_TIMEDIFF_T "ms)",
+              ctx->baller[0]->name, ctx->baller[0]->timeoutms);
   if(addr1) {
     /* second one gets a delayed start */
     result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
@@ -815,14 +821,13 @@
                             timeout_ms,  EXPIRE_DNS_PER_NAME2);
     if(result)
       return result;
-    DEBUGF(LOG_CF(data, cf, "created %s (timeout %"
-                  CURL_FORMAT_TIMEDIFF_T "ms)",
-                  ctx->baller[1]->name, ctx->baller[1]->timeoutms));
+    CURL_TRC_CF(data, cf, "created %s (timeout %"
+                CURL_FORMAT_TIMEDIFF_T "ms)",
+                ctx->baller[1]->name, ctx->baller[1]->timeoutms);
+    Curl_expire(data, data->set.happy_eyeballs_timeout,
+                EXPIRE_HAPPY_EYEBALLS);
   }
 
-  Curl_expire(data, data->set.happy_eyeballs_timeout,
-              EXPIRE_HAPPY_EYEBALLS);
-
   return CURLE_OK;
 }
 
@@ -931,13 +936,13 @@
 {
   struct cf_he_ctx *ctx = cf->ctx;
 
-  DEBUGF(LOG_CF(data, cf, "close"));
+  CURL_TRC_CF(data, cf, "close");
   cf_he_ctx_clear(cf, data);
   cf->connected = FALSE;
   ctx->state = SCFST_INIT;
 
   if(cf->next) {
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
     Curl_conn_cf_discard_chain(&cf->next, data);
   }
 }
@@ -1007,7 +1012,7 @@
         }
       }
       *pres1 = reply_ms;
-      DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1));
+      CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
       return CURLE_OK;
     }
     case CF_QUERY_TIMER_CONNECT: {
@@ -1034,7 +1039,7 @@
 {
   struct cf_he_ctx *ctx = cf->ctx;
 
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   if(ctx) {
     cf_he_ctx_clear(cf, data);
   }
@@ -1045,7 +1050,7 @@
 struct Curl_cftype Curl_cft_happy_eyeballs = {
   "HAPPY-EYEBALLS",
   0,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_he_destroy,
   cf_he_connect,
   cf_he_close,
@@ -1148,7 +1153,7 @@
   DEBUGASSERT(cf_at);
   cf_create = get_cf_create(transport);
   if(!cf_create) {
-    DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport));
+    CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
   result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
@@ -1286,12 +1291,12 @@
 {
   struct cf_setup_ctx *ctx = cf->ctx;
 
-  DEBUGF(LOG_CF(data, cf, "close"));
+  CURL_TRC_CF(data, cf, "close");
   cf->connected = FALSE;
   ctx->state = CF_SETUP_INIT;
 
   if(cf->next) {
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
     Curl_conn_cf_discard_chain(&cf->next, data);
   }
 }
@@ -1301,7 +1306,7 @@
   struct cf_setup_ctx *ctx = cf->ctx;
 
   (void)data;
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   Curl_safefree(ctx);
 }
 
@@ -1309,7 +1314,7 @@
 struct Curl_cftype Curl_cft_setup = {
   "SETUP",
   0,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_setup_destroy,
   cf_setup_connect,
   cf_setup_close,
@@ -1441,4 +1446,3 @@
 out:
   return result;
 }
-
diff --git a/Utilities/cmcurl/lib/content_encoding.c b/Utilities/cmcurl/lib/content_encoding.c
index 5b2fc6f..ec4750e 100644
--- a/Utilities/cmcurl/lib/content_encoding.c
+++ b/Utilities/cmcurl/lib/content_encoding.c
@@ -280,9 +280,6 @@
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
-  if(!writer->downstream)
-    return CURLE_WRITE_ERROR;
-
   /* Initialize zlib */
   z->zalloc = (alloc_func) zalloc_cb;
   z->zfree = (free_func) zfree_cb;
@@ -337,9 +334,6 @@
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
-  if(!writer->downstream)
-    return CURLE_WRITE_ERROR;
-
   /* Initialize zlib */
   z->zalloc = (alloc_func) zalloc_cb;
   z->zfree = (free_func) zfree_cb;
@@ -647,9 +641,6 @@
   struct brotli_writer *bp = (struct brotli_writer *) writer;
   (void) data;
 
-  if(!writer->downstream)
-    return CURLE_WRITE_ERROR;
-
   bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
   return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
 }
@@ -741,9 +732,6 @@
 
   (void)data;
 
-  if(!writer->downstream)
-    return CURLE_WRITE_ERROR;
-
   zp->zds = ZSTD_createDStream();
   zp->decomp = NULL;
   return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
@@ -822,8 +810,9 @@
 static CURLcode identity_init_writer(struct Curl_easy *data,
                                      struct contenc_writer *writer)
 {
-  (void) data;
-  return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
+  (void)data;
+  (void)writer;
+  return CURLE_OK;
 }
 
 static CURLcode identity_unencode_write(struct Curl_easy *data,
@@ -903,51 +892,13 @@
 }
 
 
-/* Real client writer: no downstream. */
-static CURLcode client_init_writer(struct Curl_easy *data,
-                                   struct contenc_writer *writer)
-{
-  (void) data;
-  return writer->downstream? CURLE_WRITE_ERROR: CURLE_OK;
-}
-
-static CURLcode client_unencode_write(struct Curl_easy *data,
-                                      struct contenc_writer *writer,
-                                      const char *buf, size_t nbytes)
-{
-  struct SingleRequest *k = &data->req;
-
-  (void) writer;
-
-  if(!nbytes || k->ignorebody)
-    return CURLE_OK;
-
-  return Curl_client_write(data, CLIENTWRITE_BODY, (char *) buf, nbytes);
-}
-
-static void client_close_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer)
-{
-  (void) data;
-  (void) writer;
-}
-
-static const struct content_encoding client_encoding = {
-  NULL,
-  NULL,
-  client_init_writer,
-  client_unencode_write,
-  client_close_writer,
-  sizeof(struct contenc_writer)
-};
-
-
 /* Deferred error dummy writer. */
 static CURLcode error_init_writer(struct Curl_easy *data,
                                   struct contenc_writer *writer)
 {
-  (void) data;
-  return writer->downstream? CURLE_OK: CURLE_WRITE_ERROR;
+  (void)data;
+  (void)writer;
+  return CURLE_OK;
 }
 
 static CURLcode error_unencode_write(struct Curl_easy *data,
@@ -984,31 +935,6 @@
   sizeof(struct contenc_writer)
 };
 
-/* Create an unencoding writer stage using the given handler. */
-static struct contenc_writer *
-new_unencoding_writer(struct Curl_easy *data,
-                      const struct content_encoding *handler,
-                      struct contenc_writer *downstream,
-                      int order)
-{
-  struct contenc_writer *writer;
-
-  DEBUGASSERT(handler->writersize >= sizeof(struct contenc_writer));
-  writer = (struct contenc_writer *) calloc(1, handler->writersize);
-
-  if(writer) {
-    writer->handler = handler;
-    writer->downstream = downstream;
-    writer->order = order;
-    if(handler->init_writer(data, writer)) {
-      free(writer);
-      writer = NULL;
-    }
-  }
-
-  return writer;
-}
-
 /* Write data using an unencoding writer stack. "nbytes" is not
    allowed to be 0. */
 CURLcode Curl_unencode_write(struct Curl_easy *data,
@@ -1017,23 +943,11 @@
 {
   if(!nbytes)
     return CURLE_OK;
+  if(!writer)
+    return CURLE_WRITE_ERROR;
   return writer->handler->unencode_write(data, writer, buf, nbytes);
 }
 
-/* Close and clean-up the connection's writer stack. */
-void Curl_unencode_cleanup(struct Curl_easy *data)
-{
-  struct SingleRequest *k = &data->req;
-  struct contenc_writer *writer = k->writer_stack;
-
-  while(writer) {
-    k->writer_stack = writer->downstream;
-    writer->handler->close_writer(data, writer);
-    free(writer);
-    writer = k->writer_stack;
-  }
-}
-
 /* Find the content encoding by name. */
 static const struct content_encoding *find_encoding(const char *name,
                                                     size_t len)
@@ -1049,9 +963,6 @@
   return NULL;
 }
 
-/* allow no more than 5 "chained" compression steps */
-#define MAX_ENCODE_STACK 5
-
 /* Set-up the unencoding stack from the Content-Encoding header value.
  * See RFC 7231 section 3.1.2.2. */
 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
@@ -1059,6 +970,7 @@
 {
   struct SingleRequest *k = &data->req;
   unsigned int order = is_transfer? 2: 1;
+  CURLcode result;
 
   do {
     const char *name;
@@ -1085,41 +997,19 @@
       if(is_transfer && !data->set.http_transfer_encoding)
         /* not requested, ignore */
         return CURLE_OK;
+
       encoding = find_encoding(name, namelen);
-
-      if(!k->writer_stack) {
-        k->writer_stack = new_unencoding_writer(data, &client_encoding,
-                                                NULL, 0);
-
-        if(!k->writer_stack)
-          return CURLE_OUT_OF_MEMORY;
-      }
-
       if(!encoding)
         encoding = &error_encoding;  /* Defer error at stack use. */
 
-      if(k->writer_stack_depth++ >= MAX_ENCODE_STACK) {
-        failf(data, "Reject response due to more than %u content encodings",
-              MAX_ENCODE_STACK);
-        return CURLE_BAD_CONTENT_ENCODING;
-      }
-      /* Stack the unencoding stage. */
-      if(order >= k->writer_stack->order) {
-        writer = new_unencoding_writer(data, encoding,
-                                       k->writer_stack, order);
-        if(!writer)
-          return CURLE_OUT_OF_MEMORY;
-        k->writer_stack = writer;
-      }
-      else {
-        struct contenc_writer *w = k->writer_stack;
-        while(w->downstream && order < w->downstream->order)
-          w = w->downstream;
-        writer = new_unencoding_writer(data, encoding,
-                                       w->downstream, order);
-        if(!writer)
-          return CURLE_OUT_OF_MEMORY;
-        w->downstream = writer;
+      result = Curl_client_create_writer(&writer, data, encoding, order);
+      if(result)
+        return result;
+
+      result = Curl_client_add_writer(data, writer);
+      if(result) {
+        Curl_client_free_writer(data, writer);
+        return result;
       }
     }
   } while(*enclist);
@@ -1149,11 +1039,6 @@
   return CURLE_NOT_BUILT_IN;
 }
 
-void Curl_unencode_cleanup(struct Curl_easy *data)
-{
-  (void) data;
-}
-
 char *Curl_all_content_encodings(void)
 {
   return strdup(CONTENT_ENCODING_DEFAULT);  /* Satisfy caller. */
diff --git a/Utilities/cmcurl/lib/content_encoding.h b/Utilities/cmcurl/lib/content_encoding.h
index 56e7f97..ef7930c 100644
--- a/Utilities/cmcurl/lib/content_encoding.h
+++ b/Utilities/cmcurl/lib/content_encoding.h
@@ -25,26 +25,9 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-struct contenc_writer {
-  const struct content_encoding *handler;  /* Encoding handler. */
-  struct contenc_writer *downstream;  /* Downstream writer. */
-  unsigned int order; /* Ordering within writer stack. */
-};
+struct contenc_writer;
 
-/* Content encoding writer. */
-struct content_encoding {
-  const char *name;        /* Encoding name. */
-  const char *alias;       /* Encoding name alias. */
-  CURLcode (*init_writer)(struct Curl_easy *data,
-                          struct contenc_writer *writer);
-  CURLcode (*unencode_write)(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes);
-  void (*close_writer)(struct Curl_easy *data,
-                       struct contenc_writer *writer);
-  size_t writersize;
-};
-
+char *Curl_all_content_encodings(void);
 
 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
                                      const char *enclist, int is_transfer);
@@ -52,6 +35,5 @@
                              struct contenc_writer *writer,
                              const char *buf, size_t nbytes);
 void Curl_unencode_cleanup(struct Curl_easy *data);
-char *Curl_all_content_encodings(void);
 
 #endif /* HEADER_CURL_CONTENT_ENCODING_H */
diff --git a/Utilities/cmcurl/lib/cookie.c b/Utilities/cmcurl/lib/cookie.c
index 0303efb..af01203 100644
--- a/Utilities/cmcurl/lib/cookie.c
+++ b/Utilities/cmcurl/lib/cookie.c
@@ -112,19 +112,17 @@
 
 static void freecookie(struct Cookie *co)
 {
-  free(co->expirestr);
   free(co->domain);
   free(co->path);
   free(co->spath);
   free(co->name);
   free(co->value);
-  free(co->maxage);
-  free(co->version);
   free(co);
 }
 
-static bool tailmatch(const char *cookie_domain, size_t cookie_domain_len,
-                      const char *hostname)
+static bool cookie_tailmatch(const char *cookie_domain,
+                             size_t cookie_domain_len,
+                             const char *hostname)
 {
   size_t hostname_len = strlen(hostname);
 
@@ -486,7 +484,7 @@
                 struct CookieInfo *c,
                 bool httpheader, /* TRUE if HTTP header-style line */
                 bool noexpire, /* if TRUE, skip remove_expired() */
-                char *lineptr,   /* first character of the line */
+                const char *lineptr,   /* first character of the line */
                 const char *domain, /* default domain */
                 const char *path,   /* full path used when this cookie is set,
                                        used to get default path for the cookie
@@ -696,7 +694,7 @@
           if(!domain
              || (is_ip && !strncmp(valuep, domain, vlen) &&
                  (vlen == strlen(domain)))
-             || (!is_ip && tailmatch(valuep, vlen, domain))) {
+             || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
             strstore(&co->domain, valuep, vlen);
             if(!co->domain) {
               badcookie = TRUE;
@@ -717,11 +715,7 @@
           }
         }
         else if((nlen == 7) && strncasecompare("version", namep, 7)) {
-          strstore(&co->version, valuep, vlen);
-          if(!co->version) {
-            badcookie = TRUE;
-            break;
-          }
+          /* just ignore */
         }
         else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
           /*
@@ -733,17 +727,55 @@
            * client should discard the cookie.  A value of zero means the
            * cookie should be discarded immediately.
            */
-          strstore(&co->maxage, valuep, vlen);
-          if(!co->maxage) {
-            badcookie = TRUE;
+          CURLofft offt;
+          const char *maxage = valuep;
+          offt = curlx_strtoofft((*maxage == '\"')?
+                                 &maxage[1]:&maxage[0], NULL, 10,
+                                 &co->expires);
+          switch(offt) {
+          case CURL_OFFT_FLOW:
+            /* overflow, used max value */
+            co->expires = CURL_OFF_T_MAX;
+            break;
+          case CURL_OFFT_INVAL:
+            /* negative or otherwise bad, expire */
+            co->expires = 1;
+            break;
+          case CURL_OFFT_OK:
+            if(!co->expires)
+              /* already expired */
+              co->expires = 1;
+            else if(CURL_OFF_T_MAX - now < co->expires)
+              /* would overflow */
+              co->expires = CURL_OFF_T_MAX;
+            else
+              co->expires += now;
             break;
           }
         }
         else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
-          strstore(&co->expirestr, valuep, vlen);
-          if(!co->expirestr) {
-            badcookie = TRUE;
-            break;
+          char date[128];
+          if(!co->expires && (vlen < sizeof(date))) {
+            /* copy the date so that it can be null terminated */
+            memcpy(date, valuep, vlen);
+            date[vlen] = 0;
+            /*
+             * Let max-age have priority.
+             *
+             * If the date cannot get parsed for whatever reason, the cookie
+             * will be treated as a session cookie
+             */
+            co->expires = Curl_getdate_capped(date);
+
+            /*
+             * Session cookies have expires set to 0 so if we get that back
+             * from the date parser let's add a second to make it a
+             * non-session cookie
+             */
+            if(co->expires == 0)
+              co->expires = 1;
+            else if(co->expires < 0)
+              co->expires = 0;
           }
         }
 
@@ -763,49 +795,6 @@
         break;
     } while(1);
 
-    if(co->maxage) {
-      CURLofft offt;
-      offt = curlx_strtoofft((*co->maxage == '\"')?
-                             &co->maxage[1]:&co->maxage[0], NULL, 10,
-                             &co->expires);
-      switch(offt) {
-      case CURL_OFFT_FLOW:
-        /* overflow, used max value */
-        co->expires = CURL_OFF_T_MAX;
-        break;
-      case CURL_OFFT_INVAL:
-        /* negative or otherwise bad, expire */
-        co->expires = 1;
-        break;
-      case CURL_OFFT_OK:
-        if(!co->expires)
-          /* already expired */
-          co->expires = 1;
-        else if(CURL_OFF_T_MAX - now < co->expires)
-          /* would overflow */
-          co->expires = CURL_OFF_T_MAX;
-        else
-          co->expires += now;
-        break;
-      }
-    }
-    else if(co->expirestr) {
-      /*
-       * Note that if the date couldn't get parsed for whatever reason, the
-       * cookie will be treated as a session cookie
-       */
-      co->expires = Curl_getdate_capped(co->expirestr);
-
-      /*
-       * Session cookies have expires set to 0 so if we get that back from the
-       * date parser let's add a second to make it a non-session cookie
-       */
-      if(co->expires == 0)
-        co->expires = 1;
-      else if(co->expires < 0)
-        co->expires = 0;
-    }
-
     if(!badcookie && !co->domain) {
       if(domain) {
         /* no domain was given in the header line, set the default */
@@ -893,7 +882,7 @@
     if(ptr)
       *ptr = 0; /* clear it */
 
-    firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
+    firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */
 
     /*
      * Now loop through the fields and init the struct we already have
@@ -1158,9 +1147,6 @@
     free(clist->domain);
     free(clist->path);
     free(clist->spath);
-    free(clist->expirestr);
-    free(clist->version);
-    free(clist->maxage);
 
     *clist = *co;  /* then store all the new data */
 
@@ -1223,9 +1209,6 @@
     c = calloc(1, sizeof(struct CookieInfo));
     if(!c)
       return NULL; /* failed to get memory */
-    c->filename = strdup(file?file:"none"); /* copy the name just in case */
-    if(!c->filename)
-      goto fail; /* failed to get memory */
     /*
      * Initialize the next_expiration time to signal that we don't have enough
      * information yet.
@@ -1254,24 +1237,20 @@
 
     c->running = FALSE; /* this is not running, this is init */
     if(fp) {
-      char *lineptr;
-      bool headerline;
 
       line = malloc(MAX_COOKIE_LINE);
       if(!line)
         goto fail;
       while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
+        char *lineptr = line;
+        bool headerline = FALSE;
         if(checkprefix("Set-Cookie:", line)) {
           /* This is a cookie line, get it! */
           lineptr = &line[11];
           headerline = TRUE;
+          while(*lineptr && ISBLANK(*lineptr))
+            lineptr++;
         }
-        else {
-          lineptr = line;
-          headerline = FALSE;
-        }
-        while(*lineptr && ISBLANK(*lineptr))
-          lineptr++;
 
         Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
       }
@@ -1287,8 +1266,8 @@
         fclose(handle);
     }
     data->state.cookie_engine = TRUE;
-    c->running = TRUE;          /* now, we're running */
   }
+  c->running = TRUE;          /* now, we're running */
 
   return c;
 
@@ -1370,14 +1349,11 @@
 {
   struct Cookie *d = calloc(sizeof(struct Cookie), 1);
   if(d) {
-    CLONE(expirestr);
     CLONE(domain);
     CLONE(path);
     CLONE(spath);
     CLONE(name);
     CLONE(value);
-    CLONE(maxage);
-    CLONE(version);
     d->expires = src->expires;
     d->tailmatch = src->tailmatch;
     d->secure = src->secure;
@@ -1431,7 +1407,7 @@
       /* now check if the domain is correct */
       if(!co->domain ||
          (co->tailmatch && !is_ip &&
-          tailmatch(co->domain, strlen(co->domain), host)) ||
+          cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
          ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
         /*
          * the right part of the host matches the domain stuff in the
@@ -1594,7 +1570,6 @@
 {
   if(c) {
     unsigned int i;
-    free(c->filename);
     for(i = 0; i < COOKIE_HASH_SIZE; i++)
       Curl_cookie_freelist(c->cookies[i]);
     free(c); /* free the base struct as well */
diff --git a/Utilities/cmcurl/lib/cookie.h b/Utilities/cmcurl/lib/cookie.h
index b3c0063..012dd89 100644
--- a/Utilities/cmcurl/lib/cookie.h
+++ b/Utilities/cmcurl/lib/cookie.h
@@ -35,12 +35,6 @@
   char *spath;        /* sanitized cookie path */
   char *domain;      /* domain = <this> */
   curl_off_t expires;  /* expires = <this> */
-  char *expirestr;   /* the plain text version */
-
-  /* RFC 2109 keywords. Version=1 means 2109-compliant cookie sending */
-  char *version;     /* Version = <value> */
-  char *maxage;      /* Max-Age = <value> */
-
   bool tailmatch;    /* whether we do tail-matching of the domain name */
   bool secure;       /* whether the 'secure' keyword was used */
   bool livecookie;   /* updated from a server, not a stored file */
@@ -56,17 +50,16 @@
 #define COOKIE_PREFIX__SECURE (1<<0)
 #define COOKIE_PREFIX__HOST (1<<1)
 
-#define COOKIE_HASH_SIZE 256
+#define COOKIE_HASH_SIZE 63
 
 struct CookieInfo {
   /* linked list of cookies we know of */
   struct Cookie *cookies[COOKIE_HASH_SIZE];
-  char *filename;  /* file we read from/write to */
-  long numcookies; /* number of cookies in the "jar" */
+  curl_off_t next_expiration; /* the next time at which expiration happens */
+  int numcookies;  /* number of cookies in the "jar" */
+  int lastct;      /* last creation-time used in the jar */
   bool running;    /* state info, for cookie adding information */
   bool newsession; /* new session, discard session cookies on load */
-  int lastct;      /* last creation-time used in the jar */
-  curl_off_t next_expiration; /* the next time at which expiration happens */
 };
 
 /* The maximum sizes we accept for cookies. RFC 6265 section 6.1 says
@@ -75,7 +68,6 @@
 
    - At least 4096 bytes per cookie (as measured by the sum of the length of
      the cookie's name, value, and attributes).
-
    In the 6265bis draft document section 5.4 it is phrased even stronger: "If
    the sum of the lengths of the name string and the value string is more than
    4096 octets, abort these steps and ignore the set-cookie-string entirely."
@@ -116,7 +108,7 @@
 
 struct Cookie *Curl_cookie_add(struct Curl_easy *data,
                                struct CookieInfo *c, bool header,
-                               bool noexpiry, char *lineptr,
+                               bool noexpiry, const char *lineptr,
                                const char *domain, const char *path,
                                bool secure);
 
diff --git a/Utilities/cmcurl/lib/curl_base64.h b/Utilities/cmcurl/lib/curl_base64.h
index 806d443..7f7cd1d 100644
--- a/Utilities/cmcurl/lib/curl_base64.h
+++ b/Utilities/cmcurl/lib/curl_base64.h
@@ -24,11 +24,18 @@
  *
  ***************************************************************************/
 
+#ifndef BUILDING_LIBCURL
+/* this renames functions so that the tool code can use the same code
+   without getting symbol collisions */
+#define Curl_base64_encode(a,b,c,d) curlx_base64_encode(a,b,c,d)
+#define Curl_base64url_encode(a,b,c,d) curlx_base64url_encode(a,b,c,d)
+#define Curl_base64_decode(a,b,c) curlx_base64_decode(a,b,c)
+#endif
+
 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
                             char **outptr, size_t *outlen);
 CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
                                char **outptr, size_t *outlen);
 CURLcode Curl_base64_decode(const char *src,
                             unsigned char **outptr, size_t *outlen);
-
 #endif /* HEADER_CURL_BASE64_H */
diff --git a/Utilities/cmcurl/lib/curl_config.h.cmake b/Utilities/cmcurl/lib/curl_config.h.cmake
index 6e2458d..d4bb274 100644
--- a/Utilities/cmcurl/lib/curl_config.h.cmake
+++ b/Utilities/cmcurl/lib/curl_config.h.cmake
@@ -25,14 +25,32 @@
 
 #include <cm3p/kwiml/abi.h>
 
+/* Default SSL backend */
+#cmakedefine CURL_DEFAULT_SSL_BACKEND "${CURL_DEFAULT_SSL_BACKEND}"
+
 /* disables alt-svc */
 #cmakedefine CURL_DISABLE_ALTSVC 1
 
 /* disables cookies support */
 #cmakedefine CURL_DISABLE_COOKIES 1
 
-/* disables cryptographic authentication */
-#cmakedefine CURL_DISABLE_CRYPTO_AUTH 1
+/* disables Basic authentication */
+#cmakedefine CURL_DISABLE_BASIC_AUTH 1
+
+/* disables Bearer authentication */
+#cmakedefine CURL_DISABLE_BEARER_AUTH 1
+
+/* disables Digest authentication */
+#cmakedefine CURL_DISABLE_DIGEST_AUTH 1
+
+/* disables Kerberos authentication */
+#cmakedefine CURL_DISABLE_KERBEROS_AUTH 1
+
+/* disables negotiate authentication */
+#cmakedefine CURL_DISABLE_NEGOTIATE_AUTH 1
+
+/* disables AWS-SIG4 */
+#cmakedefine CURL_DISABLE_AWS 1
 
 /* disables DICT */
 #cmakedefine CURL_DISABLE_DICT 1
@@ -43,6 +61,9 @@
 /* disables FILE */
 #cmakedefine CURL_DISABLE_FILE 1
 
+/* disables form api */
+#cmakedefine CURL_DISABLE_FORM_API 1
+
 /* disables FTP */
 #cmakedefine CURL_DISABLE_FTP 1
 
@@ -125,30 +146,27 @@
 /* Use Windows LDAP implementation */
 #cmakedefine USE_WIN32_LDAP 1
 
-/* when not building a shared library */
-#cmakedefine CURL_STATICLIB 1
-
-/* your Entropy Gathering Daemon socket pathname */
-#cmakedefine EGD_SOCKET ${EGD_SOCKET}
-
 /* Define if you want to enable IPv6 support */
 #cmakedefine ENABLE_IPV6 1
 
 /* Define to 1 if you have the alarm function. */
 #cmakedefine HAVE_ALARM 1
 
+/* Define to 1 if you have the arc4random function. */
+#cmakedefine HAVE_ARC4RANDOM 1
+
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #cmakedefine HAVE_ARPA_INET_H 1
 
-/* Define to 1 if you have the <arpa/tftp.h> header file. */
-#cmakedefine HAVE_ARPA_TFTP_H 1
-
 /* Define to 1 if you have _Atomic support. */
 #cmakedefine HAVE_ATOMIC 1
 
 /* Define to 1 if you have the `fchmod' function. */
 #cmakedefine HAVE_FCHMOD 1
 
+/* Define to 1 if you have the `fnmatch' function. */
+#cmakedefine HAVE_FNMATCH 1
+
 /* Define to 1 if you have the `basename' function. */
 #cmakedefine HAVE_BASENAME 1
 
@@ -161,6 +179,10 @@
 /* Define to 1 if you have the clock_gettime function and monotonic timer. */
 #cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC 1
 
+/* Define to 1 if you have the clock_gettime function and raw monotonic timer.
+   */
+#cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC_RAW 1
+
 /* Define to 1 if you have the `closesocket' function. */
 #cmakedefine HAVE_CLOSESOCKET 1
 
@@ -176,6 +198,12 @@
 /* Define to 1 if you have the freeaddrinfo function. */
 #cmakedefine HAVE_FREEADDRINFO 1
 
+/* Define to 1 if you have the fseeko function. */
+#cmakedefine HAVE_FSEEKO 1
+
+/* Define to 1 if you have the _fseeki64 function. */
+#cmakedefine HAVE__FSEEKI64 1
+
 /* Define to 1 if you have the ftruncate function. */
 #cmakedefine HAVE_FTRUNCATE 1
 
@@ -212,9 +240,6 @@
 /* Define to 1 if you have the `getpass_r' function. */
 #cmakedefine HAVE_GETPASS_R 1
 
-/* Define to 1 if you have the `getppid' function. */
-#cmakedefine HAVE_GETPPID 1
-
 /* Define to 1 if you have the `getpeername' function. */
 #cmakedefine HAVE_GETPEERNAME 1
 
@@ -353,6 +378,9 @@
 #  define HAVE_LONGLONG 1
 #endif
 
+/* Define to 1 if you have the 'suseconds_t' data type. */
+#cmakedefine HAVE_SUSECONDS_T 1
+
 /* Define to 1 if you have the MSG_NOSIGNAL flag. */
 #cmakedefine HAVE_MSG_NOSIGNAL 1
 
@@ -365,6 +393,9 @@
 /* Define to 1 if you have the <netinet/tcp.h> header file. */
 #cmakedefine HAVE_NETINET_TCP_H 1
 
+/* Define to 1 if you have the <netinet/udp.h> header file. */
+#cmakedefine HAVE_NETINET_UDP_H 1
+
 /* Define to 1 if you have the <linux/tcp.h> header file. */
 #cmakedefine HAVE_LINUX_TCP_H 1
 
@@ -392,8 +423,8 @@
 /* Define to 1 if you have the <pwd.h> header file. */
 #cmakedefine HAVE_PWD_H 1
 
-/* Define to 1 if you have the `RAND_egd' function. */
-#cmakedefine HAVE_RAND_EGD 1
+/* Define to 1 if OpenSSL has the `SSL_set0_wbio` function. */
+#cmakedefine HAVE_SSL_SET0_WBIO 1
 
 /* Define to 1 if you have the recv function. */
 #cmakedefine HAVE_RECV 1
@@ -401,9 +432,15 @@
 /* Define to 1 if you have the select function. */
 #cmakedefine HAVE_SELECT 1
 
+/* Define to 1 if you have the sched_yield function. */
+#cmakedefine HAVE_SCHED_YIELD 1
+
 /* Define to 1 if you have the send function. */
 #cmakedefine HAVE_SEND 1
 
+/* Define to 1 if you have the sendmsg function. */
+#cmakedefine HAVE_SENDMSG 1
+
 /* Define to 1 if you have the 'fsetxattr' function. */
 #cmakedefine HAVE_FSETXATTR 1
 
@@ -413,9 +450,6 @@
 /* fsetxattr() takes 6 args */
 #cmakedefine HAVE_FSETXATTR_6 1
 
-/* Define to 1 if you have the <setjmp.h> header file. */
-#cmakedefine HAVE_SETJMP_H 1
-
 /* Define to 1 if you have the `setlocale' function. */
 #cmakedefine HAVE_SETLOCALE 1
 
@@ -437,12 +471,12 @@
 /* Define to 1 if you have the signal function. */
 #cmakedefine HAVE_SIGNAL 1
 
-/* Define to 1 if you have the <signal.h> header file. */
-#cmakedefine HAVE_SIGNAL_H 1
-
 /* Define to 1 if you have the sigsetjmp function or macro. */
 #cmakedefine HAVE_SIGSETJMP 1
 
+/* Define to 1 if you have the `snprintf' function. */
+#cmakedefine HAVE_SNPRINTF 1
+
 /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
 #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
 
@@ -452,9 +486,6 @@
 /* Define to 1 if you have the socketpair function. */
 #cmakedefine HAVE_SOCKETPAIR 1
 
-/* Define to 1 if you have the <ssl.h> header file. */
-#cmakedefine HAVE_SSL_H 1
-
 /* Define to 1 if you have the <stdatomic.h> header file. */
 #cmakedefine HAVE_STDATOMIC_H 1
 
@@ -464,9 +495,6 @@
 /* Define to 1 if you have the <stdint.h> header file. */
 #cmakedefine HAVE_STDINT_H 1
 
-/* Define to 1 if you have the <stdlib.h> header file. */
-#cmakedefine HAVE_STDLIB_H 1
-
 /* Define to 1 if you have the strcasecmp function. */
 #cmakedefine HAVE_STRCASECMP 1
 
@@ -485,9 +513,6 @@
 /* Define to 1 if you have the <strings.h> header file. */
 #cmakedefine HAVE_STRINGS_H 1
 
-/* Define to 1 if you have the <string.h> header file. */
-#cmakedefine HAVE_STRING_H 1
-
 /* Define to 1 if you have the <stropts.h> header file. */
 #cmakedefine HAVE_STROPTS_H 1
 
@@ -497,6 +522,9 @@
 /* Define to 1 if you have the strtoll function. */
 #cmakedefine HAVE_STRTOLL 1
 
+/* Define to 1 if you have the memrchr function. */
+#cmakedefine HAVE_MEMRCHR 1
+
 /* if struct sockaddr_storage is defined */
 #cmakedefine HAVE_STRUCT_SOCKADDR_STORAGE 1
 
@@ -506,6 +534,9 @@
 /* Define to 1 if you have the <sys/filio.h> header file. */
 #cmakedefine HAVE_SYS_FILIO_H 1
 
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#cmakedefine HAVE_SYS_WAIT_H 1
+
 /* Define to 1 if you have the <sys/ioctl.h> header file. */
 #cmakedefine HAVE_SYS_IOCTL_H 1
 
@@ -548,9 +579,6 @@
 /* Define to 1 if you have the <termio.h> header file. */
 #cmakedefine HAVE_TERMIO_H 1
 
-/* Define to 1 if you have the <time.h> header file. */
-#cmakedefine HAVE_TIME_H 1
-
 /* Define to 1 if you have the <unistd.h> header file. */
 #cmakedefine HAVE_UNISTD_H 1
 
@@ -572,9 +600,6 @@
 /* Define to 1 if you have the windows.h header file. */
 #cmakedefine HAVE_WINDOWS_H 1
 
-/* Define to 1 if you have the winldap.h header file. */
-#cmakedefine HAVE_WINLDAP_H 1
-
 /* Define to 1 if you have the winsock2.h header file. */
 #cmakedefine HAVE_WINSOCK2_H 1
 
@@ -643,12 +668,18 @@
 #  define SIZEOF___INT64 KWIML_ABI_SIZEOF___INT64
 #endif
 
+/* The size of `long long', as computed by sizeof. */
+${SIZEOF_LONG_LONG_CODE}
+
 /* The size of `off_t', as computed by sizeof. */
 ${SIZEOF_OFF_T_CODE}
 
 /* The size of `curl_off_t', as computed by sizeof. */
 ${SIZEOF_CURL_OFF_T_CODE}
 
+/* The size of `curl_socket_t', as computed by sizeof. */
+${SIZEOF_CURL_SOCKET_T_CODE}
+
 /* The size of `size_t', as computed by sizeof. */
 ${SIZEOF_SIZE_T_CODE}
 
@@ -661,9 +692,6 @@
 /* Define to 1 if you have the ANSI C header files. */
 #cmakedefine STDC_HEADERS 1
 
-/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
-#cmakedefine TIME_WITH_SYS_TIME 1
-
 /* Define if you want to enable c-ares support */
 #cmakedefine USE_ARES 1
 
@@ -700,12 +728,6 @@
 /* If you want to build curl with the built-in manual */
 #cmakedefine USE_MANUAL 1
 
-/* if NSS is enabled */
-#cmakedefine USE_NSS 1
-
-/* if you have the PK11_CreateManagedGenericObject function */
-#cmakedefine HAVE_PK11_CREATEMANAGEDGENERICOBJECT 1
-
 /* if you want to use OpenLDAP code instead of legacy ldap implementation */
 #cmakedefine USE_OPENLDAP 1
 
@@ -735,7 +757,7 @@
 #cmakedefine USE_MSH3 1
 
 /* if Unix domain sockets are enabled  */
-#cmakedefine USE_UNIX_SOCKETS
+#cmakedefine USE_UNIX_SOCKETS 1
 
 /* to enable SSPI support */
 #cmakedefine USE_WINDOWS_SSPI 1
@@ -799,3 +821,12 @@
 
 /* Define to 1 to enable websocket support. */
 #cmakedefine USE_WEBSOCKETS 1
+
+/* Define to 1 if OpenSSL has the SSL_CTX_set_srp_username function. */
+#cmakedefine HAVE_OPENSSL_SRP 1
+
+/* Define to 1 if GnuTLS has the gnutls_srp_verifier function. */
+#cmakedefine HAVE_GNUTLS_SRP 1
+
+/* Define to 1 to enable TLS-SRP support. */
+#cmakedefine USE_TLS_SRP 1
diff --git a/Utilities/cmcurl/lib/curl_ctype.h b/Utilities/cmcurl/lib/curl_ctype.h
index 1d1d60c..7f0d0cc 100644
--- a/Utilities/cmcurl/lib/curl_ctype.h
+++ b/Utilities/cmcurl/lib/curl_ctype.h
@@ -43,5 +43,9 @@
 #define ISDIGIT(x)  (((x) >= '0') && ((x) <= '9'))
 #define ISBLANK(x)  (((x) == ' ') || ((x) == '\t'))
 #define ISSPACE(x)  (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d)))
+#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \
+                        ((x) == '~'))
+#define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x))
+
 
 #endif /* HEADER_CURL_CTYPE_H */
diff --git a/Utilities/cmcurl/lib/curl_des.c b/Utilities/cmcurl/lib/curl_des.c
index 5c623b3..b77763f 100644
--- a/Utilities/cmcurl/lib/curl_des.c
+++ b/Utilities/cmcurl/lib/curl_des.c
@@ -24,12 +24,11 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \
-    (defined(USE_GNUTLS) || \
-     defined(USE_NSS) || \
-     defined(USE_SECTRANSP) || \
-     defined(USE_OS400CRYPTO) || \
-     defined(USE_WIN32_CRYPTO))
+#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) &&     \
+  (defined(USE_GNUTLS) ||                                       \
+   defined(USE_SECTRANSP) ||                                    \
+   defined(USE_OS400CRYPTO) ||                                  \
+   defined(USE_WIN32_CRYPTO))
 
 #include "curl_des.h"
 
diff --git a/Utilities/cmcurl/lib/curl_des.h b/Utilities/cmcurl/lib/curl_des.h
index 6ec450a..66525ab 100644
--- a/Utilities/cmcurl/lib/curl_des.h
+++ b/Utilities/cmcurl/lib/curl_des.h
@@ -26,12 +26,11 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) && \
-    (defined(USE_GNUTLS) || \
-     defined(USE_NSS) || \
-     defined(USE_SECTRANSP) || \
-     defined(USE_OS400CRYPTO) || \
-     defined(USE_WIN32_CRYPTO))
+#if defined(USE_CURL_NTLM_CORE) && !defined(USE_WOLFSSL) &&     \
+  (defined(USE_GNUTLS) ||                                       \
+   defined(USE_SECTRANSP) ||                                    \
+   defined(USE_OS400CRYPTO) ||                                  \
+   defined(USE_WIN32_CRYPTO))
 
 /* Applies odd parity to the given byte array */
 void Curl_des_set_odd_parity(unsigned char *bytes, size_t length);
diff --git a/Utilities/cmcurl/lib/curl_hmac.h b/Utilities/cmcurl/lib/curl_hmac.h
index 11625c0..2ea03dd 100644
--- a/Utilities/cmcurl/lib/curl_hmac.h
+++ b/Utilities/cmcurl/lib/curl_hmac.h
@@ -24,7 +24,8 @@
  *
  ***************************************************************************/
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI))         \
+  || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include <curl/curl.h>
 
diff --git a/Utilities/cmcurl/lib/curl_log.c b/Utilities/cmcurl/lib/curl_log.c
deleted file mode 100644
index 71024cf..0000000
--- a/Utilities/cmcurl/lib/curl_log.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "curl_log.h"
-#include "urldata.h"
-#include "easyif.h"
-#include "cfilters.h"
-#include "timeval.h"
-#include "multiif.h"
-#include "strcase.h"
-
-#include "cf-socket.h"
-#include "connect.h"
-#include "http2.h"
-#include "http_proxy.h"
-#include "cf-h1-proxy.h"
-#include "cf-h2-proxy.h"
-#include "cf-haproxy.h"
-#include "cf-https-connect.h"
-#include "socks.h"
-#include "strtok.h"
-#include "vtls/vtls.h"
-#include "vquic/vquic.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
-                char *ptr, size_t size)
-{
-  if(data->set.verbose) {
-    static const char s_infotype[CURLINFO_END][3] = {
-      "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
-    if(data->set.fdebug) {
-      bool inCallback = Curl_is_in_callback(data);
-      Curl_set_in_callback(data, true);
-      (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
-      Curl_set_in_callback(data, inCallback);
-    }
-    else {
-      switch(type) {
-      case CURLINFO_TEXT:
-      case CURLINFO_HEADER_OUT:
-      case CURLINFO_HEADER_IN:
-        fwrite(s_infotype[type], 2, 1, data->set.err);
-        fwrite(ptr, size, 1, data->set.err);
-        break;
-      default: /* nada */
-        break;
-      }
-    }
-  }
-}
-
-
-/* Curl_failf() is for messages stating why we failed.
- * The message SHALL NOT include any LF or CR.
- */
-void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
-{
-  DEBUGASSERT(!strchr(fmt, '\n'));
-  if(data->set.verbose || data->set.errorbuffer) {
-    va_list ap;
-    int len;
-    char error[CURL_ERROR_SIZE + 2];
-    va_start(ap, fmt);
-    len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
-
-    if(data->set.errorbuffer && !data->state.errorbuf) {
-      strcpy(data->set.errorbuffer, error);
-      data->state.errorbuf = TRUE; /* wrote error string */
-    }
-    error[len++] = '\n';
-    error[len] = '\0';
-    Curl_debug(data, CURLINFO_TEXT, error, len);
-    va_end(ap);
-  }
-}
-
-/* Curl_infof() is for info message along the way */
-#define MAXINFO 2048
-
-void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
-{
-  DEBUGASSERT(!strchr(fmt, '\n'));
-  if(data && data->set.verbose) {
-    va_list ap;
-    int len;
-    char buffer[MAXINFO + 2];
-    va_start(ap, fmt);
-    len = mvsnprintf(buffer, MAXINFO, fmt, ap);
-    va_end(ap);
-    buffer[len++] = '\n';
-    buffer[len] = '\0';
-    Curl_debug(data, CURLINFO_TEXT, buffer, len);
-  }
-}
-
-#ifdef DEBUGBUILD
-
-void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
-                       const char *fmt, ...)
-{
-  DEBUGASSERT(cf);
-  if(data && Curl_log_cf_is_debug(cf)) {
-    va_list ap;
-    int len;
-    char buffer[MAXINFO + 2];
-    len = msnprintf(buffer, MAXINFO, "[CONN-%ld%s-%s] ",
-                    cf->conn->connection_id, cf->sockindex? "/2" : "",
-                    cf->cft->name);
-    va_start(ap, fmt);
-    len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
-    va_end(ap);
-    buffer[len++] = '\n';
-    buffer[len] = '\0';
-    Curl_debug(data, CURLINFO_TEXT, buffer, len);
-  }
-}
-
-
-static struct Curl_cftype *cf_types[] = {
-  &Curl_cft_tcp,
-  &Curl_cft_udp,
-  &Curl_cft_unix,
-  &Curl_cft_tcp_accept,
-  &Curl_cft_happy_eyeballs,
-  &Curl_cft_setup,
-#ifdef USE_NGHTTP2
-  &Curl_cft_nghttp2,
-#endif
-#ifdef USE_SSL
-  &Curl_cft_ssl,
-  &Curl_cft_ssl_proxy,
-#endif
-#if !defined(CURL_DISABLE_PROXY)
-#if !defined(CURL_DISABLE_HTTP)
-  &Curl_cft_h1_proxy,
-#ifdef USE_NGHTTP2
-  &Curl_cft_h2_proxy,
-#endif
-  &Curl_cft_http_proxy,
-#endif /* !CURL_DISABLE_HTTP */
-  &Curl_cft_haproxy,
-  &Curl_cft_socks_proxy,
-#endif /* !CURL_DISABLE_PROXY */
-#ifdef ENABLE_QUIC
-  &Curl_cft_http3,
-#endif
-#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
-  &Curl_cft_http_connect,
-#endif
-  NULL,
-};
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
-CURLcode Curl_log_init(void)
-{
-  const char *setting = getenv("CURL_DEBUG");
-  if(setting) {
-    char *token, *tok_buf, *tmp;
-    size_t i;
-
-    tmp = strdup(setting);
-    if(!tmp)
-      return CURLE_OUT_OF_MEMORY;
-
-    token = strtok_r(tmp, ", ", &tok_buf);
-    while(token) {
-      for(i = 0; cf_types[i]; ++i) {
-        if(strcasecompare(token, cf_types[i]->name)) {
-          cf_types[i]->log_level = CURL_LOG_DEBUG;
-          break;
-        }
-      }
-      token = strtok_r(NULL, ", ", &tok_buf);
-    }
-    free(tmp);
-  }
-  return CURLE_OK;
-}
-#else /* DEBUGBUILD */
-
-CURLcode Curl_log_init(void)
-{
-  return CURLE_OK;
-}
-
-#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
-void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
-                       const char *fmt, ...)
-{
-  (void)data;
-  (void)cf;
-  (void)fmt;
-}
-#endif
-
-#endif /* !DEBUGBUILD */
diff --git a/Utilities/cmcurl/lib/curl_log.h b/Utilities/cmcurl/lib/curl_log.h
deleted file mode 100644
index ad6143f..0000000
--- a/Utilities/cmcurl/lib/curl_log.h
+++ /dev/null
@@ -1,138 +0,0 @@
-#ifndef HEADER_CURL_LOG_H
-#define HEADER_CURL_LOG_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-struct Curl_easy;
-struct Curl_cfilter;
-
-/**
- * Init logging, return != 0 on failure.
- */
-CURLcode Curl_log_init(void);
-
-
-void Curl_infof(struct Curl_easy *, const char *fmt, ...);
-void Curl_failf(struct Curl_easy *, const char *fmt, ...);
-
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
-
-#if defined(HAVE_VARIADIC_MACROS_C99)
-#define infof(...)  Curl_nop_stmt
-#elif defined(HAVE_VARIADIC_MACROS_GCC)
-#define infof(x...)  Curl_nop_stmt
-#else
-#error "missing VARIADIC macro define, fix and rebuild!"
-#endif
-
-#else /* CURL_DISABLE_VERBOSE_STRINGS */
-
-#define infof Curl_infof
-
-#endif /* CURL_DISABLE_VERBOSE_STRINGS */
-
-#define failf Curl_failf
-
-
-#define CURL_LOG_DEFAULT  0
-#define CURL_LOG_DEBUG    1
-#define CURL_LOG_TRACE    2
-
-
-/* the function used to output verbose information */
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
-                char *ptr, size_t size);
-
-#ifdef DEBUGBUILD
-
-/* explainer: we have some mix configuration and werror settings
- * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced
- * on gnuc and some other compiler. Need to treat carefully.
- */
-#if defined(HAVE_VARIADIC_MACROS_C99) && \
-    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-
-#define LOG_CF(data, cf, ...) \
-  do { if(Curl_log_cf_is_debug(cf)) \
-         Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0)
-#else
-#define LOG_CF Curl_log_cf_debug
-#endif
-
-void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
-#if defined(__GNUC__) && !defined(printf) &&                    \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-  !defined(__MINGW32__)
-                       const char *fmt, ...)
-                       __attribute__((format(printf, 3, 4)));
-#else
-                       const char *fmt, ...);
-#endif
-
-#define Curl_log_cf_is_debug(cf) \
-    ((cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG)
-
-#else /* !DEBUGBUILD */
-
-#if defined(HAVE_VARIADIC_MACROS_C99) && \
-    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-#define LOG_CF(...)               Curl_nop_stmt
-#define Curl_log_cf_debug(...)    Curl_nop_stmt
-#elif defined(HAVE_VARIADIC_MACROS_GCC) && \
-    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-#define LOG_CF(x...)              Curl_nop_stmt
-#define Curl_log_cf_debug(x...)   Curl_nop_stmt
-#else
-#define LOG_CF                    Curl_log_cf_debug
-/* without c99, we seem unable to completely define away this function. */
-void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
-                       const char *fmt, ...);
-#endif
-
-#define Curl_log_cf_is_debug(x)   ((void)(x), FALSE)
-
-#endif  /* !DEBUGBUILD */
-
-#define LOG_CF_IS_DEBUG(x)        Curl_log_cf_is_debug(x)
-
-/* Macros intended for DEBUGF logging, use like:
- * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
- * and it will output:
- * [CONN-1-0][CF-SSL] this filter very much rocks
- * on connection #1 with sockindex 0 for filter of type "SSL". */
-#define DMSG(d,msg)  \
-  "[CONN-%ld] "msg, (d)->conn->connection_id
-#define DMSGI(d,i,msg)  \
-  "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
-#define CMSG(c,msg)  \
-  "[CONN-%ld] "msg, (c)->connection_id
-#define CMSGI(c,i,msg)  \
-  "[CONN-%ld-%d] "msg, (c)->connection_id, (i)
-#define CFMSG(cf,msg)  \
-  "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
-  (cf)->sockindex, (cf)->cft->name
-
-
-
-#endif /* HEADER_CURL_LOG_H */
diff --git a/Utilities/cmcurl/lib/curl_md4.h b/Utilities/cmcurl/lib/curl_md4.h
index 03567b9..4706e49 100644
--- a/Utilities/cmcurl/lib/curl_md4.h
+++ b/Utilities/cmcurl/lib/curl_md4.h
@@ -25,14 +25,15 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
+#include <curl/curl.h>
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if defined(USE_CURL_NTLM_CORE)
 
 #define MD4_DIGEST_LENGTH 16
 
-void Curl_md4it(unsigned char *output, const unsigned char *input,
-                const size_t len);
+CURLcode Curl_md4it(unsigned char *output, const unsigned char *input,
+                    const size_t len);
 
-#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */
+#endif /* defined(USE_CURL_NTLM_CORE) */
 
 #endif /* HEADER_CURL_MD4_H */
diff --git a/Utilities/cmcurl/lib/curl_md5.h b/Utilities/cmcurl/lib/curl_md5.h
index ec2512f..61671c3 100644
--- a/Utilities/cmcurl/lib/curl_md5.h
+++ b/Utilities/cmcurl/lib/curl_md5.h
@@ -24,7 +24,9 @@
  *
  ***************************************************************************/
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \
+    || !defined(CURL_DISABLE_DIGEST_AUTH)
+
 #include "curl_hmac.h"
 
 #define MD5_DIGEST_LEN  16
diff --git a/Utilities/cmcurl/lib/curl_memory.h b/Utilities/cmcurl/lib/curl_memory.h
index 1a21c5a..b8c46d7 100644
--- a/Utilities/cmcurl/lib/curl_memory.h
+++ b/Utilities/cmcurl/lib/curl_memory.h
@@ -55,9 +55,65 @@
  */
 
 #ifdef HEADER_CURL_MEMDEBUG_H
-#error "Header memdebug.h shall not be included before curl_memory.h"
+/* cleanup after memdebug.h */
+
+#ifdef MEMDEBUG_NODEFINES
+#ifdef CURLDEBUG
+
+#undef strdup
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#undef send
+#undef recv
+
+#ifdef WIN32
+#  ifdef UNICODE
+#    undef wcsdup
+#    undef _wcsdup
+#    undef _tcsdup
+#  else
+#    undef _tcsdup
+#  endif
 #endif
 
+#undef socket
+#undef accept
+#ifdef HAVE_SOCKETPAIR
+#undef socketpair
+#endif
+
+#ifdef HAVE_GETADDRINFO
+#if defined(getaddrinfo) && defined(__osf__)
+#undef ogetaddrinfo
+#else
+#undef getaddrinfo
+#endif
+#endif /* HAVE_GETADDRINFO */
+
+#ifdef HAVE_FREEADDRINFO
+#undef freeaddrinfo
+#endif /* HAVE_FREEADDRINFO */
+
+/* sclose is probably already defined, redefine it! */
+#undef sclose
+#undef fopen
+#undef fdopen
+#undef fclose
+
+#endif /* MEMDEBUG_NODEFINES */
+#endif /* CURLDEBUG */
+
+#undef HEADER_CURL_MEMDEBUG_H
+#endif /* HEADER_CURL_MEMDEBUG_H */
+
+/*
+** Following section applies even when CURLDEBUG is not defined.
+*/
+
+#undef fake_sclose
+
 #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */
 /*
  * The following memory function replacement typedef's are COPIED from
diff --git a/Utilities/cmcurl/lib/curl_memrchr.h b/Utilities/cmcurl/lib/curl_memrchr.h
index a1a4ba0..45bb38c 100644
--- a/Utilities/cmcurl/lib/curl_memrchr.h
+++ b/Utilities/cmcurl/lib/curl_memrchr.h
@@ -28,9 +28,7 @@
 
 #ifdef HAVE_MEMRCHR
 
-#ifdef HAVE_STRING_H
-#  include <string.h>
-#endif
+#include <string.h>
 #ifdef HAVE_STRINGS_H
 #  include <strings.h>
 #endif
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.c b/Utilities/cmcurl/lib/curl_ntlm_core.c
index ba8457d..cc0ed91 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.c
@@ -38,7 +38,7 @@
    1. USE_OPENSSL
    2. USE_WOLFSSL
    3. USE_GNUTLS
-   4. USE_NSS
+   4. -
    5. USE_MBEDTLS
    6. USE_SECTRANSP
    7. USE_OS400CRYPTO
@@ -47,7 +47,7 @@
    This ensures that:
    - the same SSL branch gets activated throughout this source
      file even if multiple backends are enabled at the same time.
-   - OpenSSL and NSS have higher priority than Windows Crypt, due
+   - OpenSSL has higher priority than Windows Crypt, due
      to issues with the latter supporting NTLM2Session responses
      in NTLM type-3 messages.
  */
@@ -96,12 +96,6 @@
 
 #  include <nettle/des.h>
 
-#elif defined(USE_NSS)
-
-#  include <nss.h>
-#  include <pk11pub.h>
-#  include <hasht.h>
-
 #elif defined(USE_MBEDTLS)
 
 #  include <mbedtls/des.h>
@@ -188,70 +182,6 @@
   des_set_key(des, (const uint8_t *) key);
 }
 
-#elif defined(USE_NSS)
-
-/*
- * encrypt_des() expands a 56 bit key KEY_56 to 64 bit and encrypts 64 bit of
- * data, using the expanded key. IN should point to 64 bits of source data,
- * OUT to a 64 bit output buffer.
- */
-static bool encrypt_des(const unsigned char *in, unsigned char *out,
-                        const unsigned char *key_56)
-{
-  const CK_MECHANISM_TYPE mech = CKM_DES_ECB; /* DES cipher in ECB mode */
-  char key[8];                                /* expanded 64 bit key */
-  SECItem key_item;
-  PK11SymKey *symkey = NULL;
-  SECItem *param = NULL;
-  PK11Context *ctx = NULL;
-  int out_len;                                /* not used, required by NSS */
-  bool rv = FALSE;
-
-  /* use internal slot for DES encryption (requires NSS to be initialized) */
-  PK11SlotInfo *slot = PK11_GetInternalKeySlot();
-  if(!slot)
-    return FALSE;
-
-  /* Expand the 56-bit key to 64-bits */
-  extend_key_56_to_64(key_56, key);
-
-  /* Set the key parity to odd */
-  Curl_des_set_odd_parity((unsigned char *) key, sizeof(key));
-
-  /* Import the key */
-  key_item.data = (unsigned char *)key;
-  key_item.len = sizeof(key);
-  symkey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap, CKA_ENCRYPT,
-                             &key_item, NULL);
-  if(!symkey)
-    goto fail;
-
-  /* Create the DES encryption context */
-  param = PK11_ParamFromIV(mech, /* no IV in ECB mode */ NULL);
-  if(!param)
-    goto fail;
-  ctx = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symkey, param);
-  if(!ctx)
-    goto fail;
-
-  /* Perform the encryption */
-  if(SECSuccess == PK11_CipherOp(ctx, out, &out_len, /* outbuflen */ 8,
-                                 (unsigned char *)in, /* inbuflen */ 8)
-      && SECSuccess == PK11_Finalize(ctx))
-    rv = /* all OK */ TRUE;
-
-fail:
-  /* cleanup */
-  if(ctx)
-    PK11_DestroyContext(ctx, PR_TRUE);
-  if(symkey)
-    PK11_FreeSymKey(symkey);
-  if(param)
-    SECITEM_FreeItem(param, PR_TRUE);
-  PK11_FreeSlot(slot);
-  return rv;
-}
-
 #elif defined(USE_MBEDTLS)
 
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
@@ -402,7 +332,7 @@
   des_encrypt(&des, 8, results + 8, plaintext);
   setup_des_key(keys + 14, &des);
   des_encrypt(&des, 8, results + 16, plaintext);
-#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \
+#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP)            \
   || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO)
   encrypt_des(plaintext, results, keys);
   encrypt_des(plaintext, results + 8, keys + 7);
@@ -444,7 +374,7 @@
     des_encrypt(&des, 8, lmbuffer, magic);
     setup_des_key(pw + 7, &des);
     des_encrypt(&des, 8, lmbuffer + 8, magic);
-#elif defined(USE_NSS) || defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \
+#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP)            \
   || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO)
     encrypt_des(magic, lmbuffer, pw);
     encrypt_des(magic, lmbuffer + 8, pw + 7);
@@ -489,6 +419,7 @@
 {
   size_t len = strlen(password);
   unsigned char *pw;
+  CURLcode result;
   if(len > SIZE_T_MAX/2) /* avoid integer overflow */
     return CURLE_OUT_OF_MEMORY;
   pw = len ? malloc(len * 2) : (unsigned char *)strdup("");
@@ -498,12 +429,13 @@
   ascii_to_unicode_le(pw, password, len);
 
   /* Create NT hashed password. */
-  Curl_md4it(ntbuffer, pw, 2 * len);
-  memset(ntbuffer + 16, 0, 21 - 16);
+  result = Curl_md4it(ntbuffer, pw, 2 * len);
+  if(!result)
+    memset(ntbuffer + 16, 0, 21 - 16);
 
   free(pw);
 
-  return CURLE_OK;
+  return result;
 }
 
 #if !defined(USE_WINDOWS_SSPI)
diff --git a/Utilities/cmcurl/lib/curl_ntlm_core.h b/Utilities/cmcurl/lib/curl_ntlm_core.h
index 33b651f..0c62ee0 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_core.h
+++ b/Utilities/cmcurl/lib/curl_ntlm_core.h
@@ -28,15 +28,6 @@
 
 #if defined(USE_CURL_NTLM_CORE)
 
-/* If NSS is the first available SSL backend (see order in curl_ntlm_core.c)
-   then it must be initialized to be used by NTLM. */
-#if !defined(USE_OPENSSL) && \
-    !defined(USE_WOLFSSL) && \
-    !defined(USE_GNUTLS) && \
-    defined(USE_NSS)
-#define NTLM_NEEDS_NSS_INIT
-#endif
-
 #if defined(USE_OPENSSL)
 #  include <openssl/ssl.h>
 #elif defined(USE_WOLFSSL)
diff --git a/Utilities/cmcurl/lib/curl_ntlm_wb.c b/Utilities/cmcurl/lib/curl_ntlm_wb.c
index a10e2a1..aa7bea7 100644
--- a/Utilities/cmcurl/lib/curl_ntlm_wb.c
+++ b/Utilities/cmcurl/lib/curl_ntlm_wb.c
@@ -39,9 +39,7 @@
 #ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
 #endif
-#ifdef HAVE_SIGNAL_H
 #include <signal.h>
-#endif
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
diff --git a/Utilities/cmcurl/lib/curl_printf.h b/Utilities/cmcurl/lib/curl_printf.h
index 6d3d492..46ef344 100644
--- a/Utilities/cmcurl/lib/curl_printf.h
+++ b/Utilities/cmcurl/lib/curl_printf.h
@@ -37,6 +37,7 @@
 # undef vprintf
 # undef vfprintf
 # undef vsnprintf
+# undef mvsnprintf
 # undef aprintf
 # undef vaprintf
 # define printf curl_mprintf
diff --git a/Utilities/cmcurl/lib/curl_sasl.c b/Utilities/cmcurl/lib/curl_sasl.c
index 119fb9b..91ddf10 100644
--- a/Utilities/cmcurl/lib/curl_sasl.c
+++ b/Utilities/cmcurl/lib/curl_sasl.c
@@ -221,12 +221,12 @@
 }
 
 /*
- * state()
+ * sasl_state()
  *
  * This is the ONLY way to change SASL state!
  */
-static void state(struct SASL *sasl, struct Curl_easy *data,
-                  saslstate newstate)
+static void sasl_state(struct SASL *sasl, struct Curl_easy *data,
+                       saslstate newstate)
 {
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
@@ -420,7 +420,7 @@
     }
     else
 #endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
     if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
        Curl_auth_is_digest_supported()) {
       mech = SASL_MECH_STRING_DIGEST_MD5;
@@ -508,7 +508,7 @@
 
     if(!result) {
       *progress = SASL_INPROGRESS;
-      state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
+      sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
     }
   }
 
@@ -530,8 +530,8 @@
   struct bufref resp;
   const char *hostname, *disp_hostname;
   int port;
-#if !defined(CURL_DISABLE_CRYPTO_AUTH) || defined(USE_KERBEROS5) ||     \
-  defined(USE_NTLM)
+#if defined(USE_KERBEROS5) || defined(USE_NTLM) \
+    || !defined(CURL_DISABLE_DIGEST_AUTH)
   const char *service = data->set.str[STRING_SERVICE_NAME] ?
     data->set.str[STRING_SERVICE_NAME] :
     sasl->params->service;
@@ -548,14 +548,14 @@
     if(code != sasl->params->finalcode)
       result = CURLE_LOGIN_DENIED;
     *progress = SASL_DONE;
-    state(sasl, data, SASL_STOP);
+    sasl_state(sasl, data, SASL_STOP);
     return result;
   }
 
   if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
      code != sasl->params->contcode) {
     *progress = SASL_DONE;
-    state(sasl, data, SASL_STOP);
+    sasl_state(sasl, data, SASL_STOP);
     return CURLE_LOGIN_DENIED;
   }
 
@@ -577,7 +577,6 @@
   case SASL_EXTERNAL:
     result = Curl_auth_create_external_message(conn->user, &resp);
     break;
-#ifndef CURL_DISABLE_CRYPTO_AUTH
 #ifdef USE_GSASL
   case SASL_GSASL:
     result = get_server_message(sasl, data, &serverdata);
@@ -587,6 +586,7 @@
       newstate = SASL_GSASL;
     break;
 #endif
+#ifndef CURL_DISABLE_DIGEST_AUTH
   case SASL_CRAMMD5:
     result = get_server_message(sasl, data, &serverdata);
     if(!result)
@@ -698,7 +698,7 @@
     if(code == sasl->params->finalcode) {
       /* Final response was received so we are done */
       *progress = SASL_DONE;
-      state(sasl, data, SASL_STOP);
+      sasl_state(sasl, data, SASL_STOP);
       return result;
     }
     else if(code == sasl->params->contcode) {
@@ -708,7 +708,7 @@
     }
     else {
       *progress = SASL_DONE;
-      state(sasl, data, SASL_STOP);
+      sasl_state(sasl, data, SASL_STOP);
       return CURLE_LOGIN_DENIED;
     }
 
@@ -745,7 +745,7 @@
 
   Curl_bufref_free(&resp);
 
-  state(sasl, data, newstate);
+  sasl_state(sasl, data, newstate);
 
   return result;
 }
diff --git a/Utilities/cmcurl/lib/curl_setup.h b/Utilities/cmcurl/lib/curl_setup.h
index 9043d97..8557cf4 100644
--- a/Utilities/cmcurl/lib/curl_setup.h
+++ b/Utilities/cmcurl/lib/curl_setup.h
@@ -28,11 +28,6 @@
 #define CURL_NO_OLDIES
 #endif
 
-/* define mingw version macros, eg __MINGW{32,64}_{MINOR,MAJOR}_VERSION */
-#ifdef __MINGW32__
-#include <_mingw.h>
-#endif
-
 /*
  * Disable Visual Studio warnings:
  * 4127 "conditional expression is constant"
@@ -262,8 +257,9 @@
 #if defined(__APPLE__) && !defined(USE_ARES)
 #include <TargetConditionals.h>
 #define USE_RESOLVE_ON_IPS 1
-#  if defined(TARGET_OS_OSX) && TARGET_OS_OSX
-#    define CURL_OSX_CALL_COPYPROXIES 1
+#  if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \
+     defined(ENABLE_IPV6)
+#    define CURL_MACOS_CALL_COPYPROXIES 1
 #  endif
 #endif
 
@@ -302,6 +298,7 @@
 #  if defined(HAVE_PROTO_BSDSOCKET_H) && \
     (!defined(__amigaos4__) || defined(USE_AMISSL))
      /* use bsdsocket.library directly, instead of libc networking functions */
+#    define _SYS_MBUF_H /* m_len define clashes with curl */
 #    include <proto/bsdsocket.h>
 #    ifdef __amigaos4__
        int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
@@ -658,32 +655,30 @@
 
 #define LIBIDN_REQUIRED_VERSION "0.4.1"
 
-#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \
-    defined(USE_MBEDTLS) || \
-    defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
-    defined(USE_SECTRANSP) || defined(USE_GSKIT) || \
-    defined(USE_BEARSSL) || defined(USE_RUSTLS)
+#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
+  defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
+  defined(USE_BEARSSL) || defined(USE_RUSTLS)
 #define USE_SSL    /* SSL support has been enabled */
 #endif
 
 /* Single point where USE_SPNEGO definition might be defined */
-#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \
+#if !defined(CURL_DISABLE_NEGOTIATE_AUTH) && \
     (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
 #define USE_SPNEGO
 #endif
 
 /* Single point where USE_KERBEROS5 definition might be defined */
-#if !defined(CURL_DISABLE_CRYPTO_AUTH) && \
+#if !defined(CURL_DISABLE_KERBEROS_AUTH) && \
     (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI))
 #define USE_KERBEROS5
 #endif
 
 /* Single point where USE_NTLM definition might be defined */
-#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM)
-#  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                       \
-      defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) ||  \
-      defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
-      (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
+#if !defined(CURL_DISABLE_NTLM)
+#  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                   \
+  defined(USE_GNUTLS) || defined(USE_SECTRANSP) ||                      \
+  defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
+  (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
 #    define USE_CURL_NTLM_CORE
 #  endif
 #  if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
@@ -845,9 +840,6 @@
 #endif
 
 #if defined(USE_UNIX_SOCKETS) && defined(WIN32)
-#  if defined(__MINGW32__) && !defined(LUP_SECURE)
-     typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */
-#  endif
 #  if !defined(UNIX_PATH_MAX)
      /* Replicating logic present in afunix.h
         (distributed with newer Windows 10 SDK versions only) */
diff --git a/Utilities/cmcurl/lib/curl_setup_once.h b/Utilities/cmcurl/lib/curl_setup_once.h
index dde7229..c1ed059 100644
--- a/Utilities/cmcurl/lib/curl_setup_once.h
+++ b/Utilities/cmcurl/lib/curl_setup_once.h
@@ -77,6 +77,12 @@
 #  endif
 #endif
 
+#ifdef USE_SCHANNEL
+/* Must set this before <schannel.h> is included directly or indirectly by
+   another Windows header. */
+#  define SCHANNEL_USE_BLACKLISTS 1
+#endif
+
 #ifdef __hpux
 #  if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
 #    ifdef _APP32_64BIT_OFF_T
diff --git a/Utilities/cmcurl/lib/curl_sha256.h b/Utilities/cmcurl/lib/curl_sha256.h
index c5e157b..d99f958 100644
--- a/Utilities/cmcurl/lib/curl_sha256.h
+++ b/Utilities/cmcurl/lib/curl_sha256.h
@@ -25,7 +25,9 @@
  *
  ***************************************************************************/
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
+    || defined(USE_LIBSSH2)
+
 #include <curl/curl.h>
 #include "curl_hmac.h"
 
diff --git a/Utilities/cmcurl/lib/curl_sspi.h b/Utilities/cmcurl/lib/curl_sspi.h
index 9816d59..5af7c24 100644
--- a/Utilities/cmcurl/lib/curl_sspi.h
+++ b/Utilities/cmcurl/lib/curl_sspi.h
@@ -70,227 +70,6 @@
 #define ISC_REQ_USE_HTTP_STYLE                0x01000000
 #endif
 
-#ifndef ISC_RET_REPLAY_DETECT
-#define ISC_RET_REPLAY_DETECT                 0x00000004
-#endif
-
-#ifndef ISC_RET_SEQUENCE_DETECT
-#define ISC_RET_SEQUENCE_DETECT               0x00000008
-#endif
-
-#ifndef ISC_RET_CONFIDENTIALITY
-#define ISC_RET_CONFIDENTIALITY               0x00000010
-#endif
-
-#ifndef ISC_RET_ALLOCATED_MEMORY
-#define ISC_RET_ALLOCATED_MEMORY              0x00000100
-#endif
-
-#ifndef ISC_RET_STREAM
-#define ISC_RET_STREAM                        0x00008000
-#endif
-
-#ifndef SEC_E_INSUFFICIENT_MEMORY
-# define SEC_E_INSUFFICIENT_MEMORY            ((HRESULT)0x80090300L)
-#endif
-#ifndef SEC_E_INVALID_HANDLE
-# define SEC_E_INVALID_HANDLE                 ((HRESULT)0x80090301L)
-#endif
-#ifndef SEC_E_UNSUPPORTED_FUNCTION
-# define SEC_E_UNSUPPORTED_FUNCTION           ((HRESULT)0x80090302L)
-#endif
-#ifndef SEC_E_TARGET_UNKNOWN
-# define SEC_E_TARGET_UNKNOWN                 ((HRESULT)0x80090303L)
-#endif
-#ifndef SEC_E_INTERNAL_ERROR
-# define SEC_E_INTERNAL_ERROR                 ((HRESULT)0x80090304L)
-#endif
-#ifndef SEC_E_SECPKG_NOT_FOUND
-# define SEC_E_SECPKG_NOT_FOUND               ((HRESULT)0x80090305L)
-#endif
-#ifndef SEC_E_NOT_OWNER
-# define SEC_E_NOT_OWNER                      ((HRESULT)0x80090306L)
-#endif
-#ifndef SEC_E_CANNOT_INSTALL
-# define SEC_E_CANNOT_INSTALL                 ((HRESULT)0x80090307L)
-#endif
-#ifndef SEC_E_INVALID_TOKEN
-# define SEC_E_INVALID_TOKEN                  ((HRESULT)0x80090308L)
-#endif
-#ifndef SEC_E_CANNOT_PACK
-# define SEC_E_CANNOT_PACK                    ((HRESULT)0x80090309L)
-#endif
-#ifndef SEC_E_QOP_NOT_SUPPORTED
-# define SEC_E_QOP_NOT_SUPPORTED              ((HRESULT)0x8009030AL)
-#endif
-#ifndef SEC_E_NO_IMPERSONATION
-# define SEC_E_NO_IMPERSONATION               ((HRESULT)0x8009030BL)
-#endif
-#ifndef SEC_E_LOGON_DENIED
-# define SEC_E_LOGON_DENIED                   ((HRESULT)0x8009030CL)
-#endif
-#ifndef SEC_E_UNKNOWN_CREDENTIALS
-# define SEC_E_UNKNOWN_CREDENTIALS            ((HRESULT)0x8009030DL)
-#endif
-#ifndef SEC_E_NO_CREDENTIALS
-# define SEC_E_NO_CREDENTIALS                 ((HRESULT)0x8009030EL)
-#endif
-#ifndef SEC_E_MESSAGE_ALTERED
-# define SEC_E_MESSAGE_ALTERED                ((HRESULT)0x8009030FL)
-#endif
-#ifndef SEC_E_OUT_OF_SEQUENCE
-# define SEC_E_OUT_OF_SEQUENCE                ((HRESULT)0x80090310L)
-#endif
-#ifndef SEC_E_NO_AUTHENTICATING_AUTHORITY
-# define SEC_E_NO_AUTHENTICATING_AUTHORITY    ((HRESULT)0x80090311L)
-#endif
-#ifndef SEC_E_BAD_PKGID
-# define SEC_E_BAD_PKGID                      ((HRESULT)0x80090316L)
-#endif
-#ifndef SEC_E_CONTEXT_EXPIRED
-# define SEC_E_CONTEXT_EXPIRED                ((HRESULT)0x80090317L)
-#endif
-#ifndef SEC_E_INCOMPLETE_MESSAGE
-# define SEC_E_INCOMPLETE_MESSAGE             ((HRESULT)0x80090318L)
-#endif
-#ifndef SEC_E_INCOMPLETE_CREDENTIALS
-# define SEC_E_INCOMPLETE_CREDENTIALS         ((HRESULT)0x80090320L)
-#endif
-#ifndef SEC_E_BUFFER_TOO_SMALL
-# define SEC_E_BUFFER_TOO_SMALL               ((HRESULT)0x80090321L)
-#endif
-#ifndef SEC_E_WRONG_PRINCIPAL
-# define SEC_E_WRONG_PRINCIPAL                ((HRESULT)0x80090322L)
-#endif
-#ifndef SEC_E_TIME_SKEW
-# define SEC_E_TIME_SKEW                      ((HRESULT)0x80090324L)
-#endif
-#ifndef SEC_E_UNTRUSTED_ROOT
-# define SEC_E_UNTRUSTED_ROOT                 ((HRESULT)0x80090325L)
-#endif
-#ifndef SEC_E_ILLEGAL_MESSAGE
-# define SEC_E_ILLEGAL_MESSAGE                ((HRESULT)0x80090326L)
-#endif
-#ifndef SEC_E_CERT_UNKNOWN
-# define SEC_E_CERT_UNKNOWN                   ((HRESULT)0x80090327L)
-#endif
-#ifndef SEC_E_CERT_EXPIRED
-# define SEC_E_CERT_EXPIRED                   ((HRESULT)0x80090328L)
-#endif
-#ifndef SEC_E_ENCRYPT_FAILURE
-# define SEC_E_ENCRYPT_FAILURE                ((HRESULT)0x80090329L)
-#endif
-#ifndef SEC_E_DECRYPT_FAILURE
-# define SEC_E_DECRYPT_FAILURE                ((HRESULT)0x80090330L)
-#endif
-#ifndef SEC_E_ALGORITHM_MISMATCH
-# define SEC_E_ALGORITHM_MISMATCH             ((HRESULT)0x80090331L)
-#endif
-#ifndef SEC_E_SECURITY_QOS_FAILED
-# define SEC_E_SECURITY_QOS_FAILED            ((HRESULT)0x80090332L)
-#endif
-#ifndef SEC_E_UNFINISHED_CONTEXT_DELETED
-# define SEC_E_UNFINISHED_CONTEXT_DELETED     ((HRESULT)0x80090333L)
-#endif
-#ifndef SEC_E_NO_TGT_REPLY
-# define SEC_E_NO_TGT_REPLY                   ((HRESULT)0x80090334L)
-#endif
-#ifndef SEC_E_NO_IP_ADDRESSES
-# define SEC_E_NO_IP_ADDRESSES                ((HRESULT)0x80090335L)
-#endif
-#ifndef SEC_E_WRONG_CREDENTIAL_HANDLE
-# define SEC_E_WRONG_CREDENTIAL_HANDLE        ((HRESULT)0x80090336L)
-#endif
-#ifndef SEC_E_CRYPTO_SYSTEM_INVALID
-# define SEC_E_CRYPTO_SYSTEM_INVALID          ((HRESULT)0x80090337L)
-#endif
-#ifndef SEC_E_MAX_REFERRALS_EXCEEDED
-# define SEC_E_MAX_REFERRALS_EXCEEDED         ((HRESULT)0x80090338L)
-#endif
-#ifndef SEC_E_MUST_BE_KDC
-# define SEC_E_MUST_BE_KDC                    ((HRESULT)0x80090339L)
-#endif
-#ifndef SEC_E_STRONG_CRYPTO_NOT_SUPPORTED
-# define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED    ((HRESULT)0x8009033AL)
-#endif
-#ifndef SEC_E_TOO_MANY_PRINCIPALS
-# define SEC_E_TOO_MANY_PRINCIPALS            ((HRESULT)0x8009033BL)
-#endif
-#ifndef SEC_E_NO_PA_DATA
-# define SEC_E_NO_PA_DATA                     ((HRESULT)0x8009033CL)
-#endif
-#ifndef SEC_E_PKINIT_NAME_MISMATCH
-# define SEC_E_PKINIT_NAME_MISMATCH           ((HRESULT)0x8009033DL)
-#endif
-#ifndef SEC_E_SMARTCARD_LOGON_REQUIRED
-# define SEC_E_SMARTCARD_LOGON_REQUIRED       ((HRESULT)0x8009033EL)
-#endif
-#ifndef SEC_E_SHUTDOWN_IN_PROGRESS
-# define SEC_E_SHUTDOWN_IN_PROGRESS           ((HRESULT)0x8009033FL)
-#endif
-#ifndef SEC_E_KDC_INVALID_REQUEST
-# define SEC_E_KDC_INVALID_REQUEST            ((HRESULT)0x80090340L)
-#endif
-#ifndef SEC_E_KDC_UNABLE_TO_REFER
-# define SEC_E_KDC_UNABLE_TO_REFER            ((HRESULT)0x80090341L)
-#endif
-#ifndef SEC_E_KDC_UNKNOWN_ETYPE
-# define SEC_E_KDC_UNKNOWN_ETYPE              ((HRESULT)0x80090342L)
-#endif
-#ifndef SEC_E_UNSUPPORTED_PREAUTH
-# define SEC_E_UNSUPPORTED_PREAUTH            ((HRESULT)0x80090343L)
-#endif
-#ifndef SEC_E_DELEGATION_REQUIRED
-# define SEC_E_DELEGATION_REQUIRED            ((HRESULT)0x80090345L)
-#endif
-#ifndef SEC_E_BAD_BINDINGS
-# define SEC_E_BAD_BINDINGS                   ((HRESULT)0x80090346L)
-#endif
-#ifndef SEC_E_MULTIPLE_ACCOUNTS
-# define SEC_E_MULTIPLE_ACCOUNTS              ((HRESULT)0x80090347L)
-#endif
-#ifndef SEC_E_NO_KERB_KEY
-# define SEC_E_NO_KERB_KEY                    ((HRESULT)0x80090348L)
-#endif
-#ifndef SEC_E_CERT_WRONG_USAGE
-# define SEC_E_CERT_WRONG_USAGE               ((HRESULT)0x80090349L)
-#endif
-#ifndef SEC_E_DOWNGRADE_DETECTED
-# define SEC_E_DOWNGRADE_DETECTED             ((HRESULT)0x80090350L)
-#endif
-#ifndef SEC_E_SMARTCARD_CERT_REVOKED
-# define SEC_E_SMARTCARD_CERT_REVOKED         ((HRESULT)0x80090351L)
-#endif
-#ifndef SEC_E_ISSUING_CA_UNTRUSTED
-# define SEC_E_ISSUING_CA_UNTRUSTED           ((HRESULT)0x80090352L)
-#endif
-#ifndef SEC_E_REVOCATION_OFFLINE_C
-# define SEC_E_REVOCATION_OFFLINE_C           ((HRESULT)0x80090353L)
-#endif
-#ifndef SEC_E_PKINIT_CLIENT_FAILURE
-# define SEC_E_PKINIT_CLIENT_FAILURE          ((HRESULT)0x80090354L)
-#endif
-#ifndef SEC_E_SMARTCARD_CERT_EXPIRED
-# define SEC_E_SMARTCARD_CERT_EXPIRED         ((HRESULT)0x80090355L)
-#endif
-#ifndef SEC_E_NO_S4U_PROT_SUPPORT
-# define SEC_E_NO_S4U_PROT_SUPPORT            ((HRESULT)0x80090356L)
-#endif
-#ifndef SEC_E_CROSSREALM_DELEGATION_FAILURE
-# define SEC_E_CROSSREALM_DELEGATION_FAILURE  ((HRESULT)0x80090357L)
-#endif
-#ifndef SEC_E_REVOCATION_OFFLINE_KDC
-# define SEC_E_REVOCATION_OFFLINE_KDC         ((HRESULT)0x80090358L)
-#endif
-#ifndef SEC_E_ISSUING_CA_UNTRUSTED_KDC
-# define SEC_E_ISSUING_CA_UNTRUSTED_KDC       ((HRESULT)0x80090359L)
-#endif
-#ifndef SEC_E_KDC_CERT_EXPIRED
-# define SEC_E_KDC_CERT_EXPIRED               ((HRESULT)0x8009035AL)
-#endif
-#ifndef SEC_E_KDC_CERT_REVOKED
-# define SEC_E_KDC_CERT_REVOKED               ((HRESULT)0x8009035BL)
-#endif
 #ifndef SEC_E_INVALID_PARAMETER
 # define SEC_E_INVALID_PARAMETER              ((HRESULT)0x8009035DL)
 #endif
@@ -301,30 +80,6 @@
 # define SEC_E_POLICY_NLTM_ONLY               ((HRESULT)0x8009035FL)
 #endif
 
-#ifndef SEC_I_CONTINUE_NEEDED
-# define SEC_I_CONTINUE_NEEDED                ((HRESULT)0x00090312L)
-#endif
-#ifndef SEC_I_COMPLETE_NEEDED
-# define SEC_I_COMPLETE_NEEDED                ((HRESULT)0x00090313L)
-#endif
-#ifndef SEC_I_COMPLETE_AND_CONTINUE
-# define SEC_I_COMPLETE_AND_CONTINUE          ((HRESULT)0x00090314L)
-#endif
-#ifndef SEC_I_LOCAL_LOGON
-# define SEC_I_LOCAL_LOGON                    ((HRESULT)0x00090315L)
-#endif
-#ifndef SEC_I_CONTEXT_EXPIRED
-# define SEC_I_CONTEXT_EXPIRED                ((HRESULT)0x00090317L)
-#endif
-#ifndef SEC_I_INCOMPLETE_CREDENTIALS
-# define SEC_I_INCOMPLETE_CREDENTIALS         ((HRESULT)0x00090320L)
-#endif
-#ifndef SEC_I_RENEGOTIATE
-# define SEC_I_RENEGOTIATE                    ((HRESULT)0x00090321L)
-#endif
-#ifndef SEC_I_NO_LSA_CONTEXT
-# define SEC_I_NO_LSA_CONTEXT                 ((HRESULT)0x00090323L)
-#endif
 #ifndef SEC_I_SIGNATURE_NEEDED
 # define SEC_I_SIGNATURE_NEEDED               ((HRESULT)0x0009035CL)
 #endif
diff --git a/Utilities/cmcurl/lib/curl_threads.c b/Utilities/cmcurl/lib/curl_threads.c
index e13e294..222d936 100644
--- a/Utilities/cmcurl/lib/curl_threads.c
+++ b/Utilities/cmcurl/lib/curl_threads.c
@@ -106,8 +106,6 @@
 {
 #ifdef _WIN32_WCE
   typedef HANDLE curl_win_thread_handle_t;
-#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
-  typedef unsigned long curl_win_thread_handle_t;
 #else
   typedef uintptr_t curl_win_thread_handle_t;
 #endif
diff --git a/Utilities/cmcurl/lib/curl_threads.h b/Utilities/cmcurl/lib/curl_threads.h
index facbc73..27a478d 100644
--- a/Utilities/cmcurl/lib/curl_threads.h
+++ b/Utilities/cmcurl/lib/curl_threads.h
@@ -40,8 +40,7 @@
 #  define curl_thread_t          HANDLE
 #  define curl_thread_t_null     (HANDLE)0
 #  if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
-      (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \
-      (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
+      (_WIN32_WINNT < _WIN32_WINNT_VISTA)
 #    define Curl_mutex_init(m)   InitializeCriticalSection(m)
 #  else
 #    define Curl_mutex_init(m)   InitializeCriticalSectionEx(m, 0, 1)
diff --git a/Utilities/cmcurl/lib/curl_trc.c b/Utilities/cmcurl/lib/curl_trc.c
new file mode 100644
index 0000000..e53b305
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_trc.c
@@ -0,0 +1,255 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "curl_trc.h"
+#include "urldata.h"
+#include "easyif.h"
+#include "cfilters.h"
+#include "timeval.h"
+#include "multiif.h"
+#include "strcase.h"
+
+#include "cf-socket.h"
+#include "connect.h"
+#include "http2.h"
+#include "http_proxy.h"
+#include "cf-h1-proxy.h"
+#include "cf-h2-proxy.h"
+#include "cf-haproxy.h"
+#include "cf-https-connect.h"
+#include "socks.h"
+#include "strtok.h"
+#include "vtls/vtls.h"
+#include "vquic/vquic.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+                char *ptr, size_t size)
+{
+  if(data->set.verbose) {
+    static const char s_infotype[CURLINFO_END][3] = {
+      "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+    if(data->set.fdebug) {
+      bool inCallback = Curl_is_in_callback(data);
+      /* CURLOPT_DEBUGFUNCTION doc says the user may set CURLOPT_PRIVATE to
+         distinguish their handle from internal handles. */
+      if(data->internal)
+        DEBUGASSERT(!data->set.private_data);
+      Curl_set_in_callback(data, true);
+      (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
+      Curl_set_in_callback(data, inCallback);
+    }
+    else {
+      switch(type) {
+      case CURLINFO_TEXT:
+      case CURLINFO_HEADER_OUT:
+      case CURLINFO_HEADER_IN:
+        fwrite(s_infotype[type], 2, 1, data->set.err);
+        fwrite(ptr, size, 1, data->set.err);
+        break;
+      default: /* nada */
+        break;
+      }
+    }
+  }
+}
+
+
+/* Curl_failf() is for messages stating why we failed.
+ * The message SHALL NOT include any LF or CR.
+ */
+void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(data->set.verbose || data->set.errorbuffer) {
+    va_list ap;
+    int len;
+    char error[CURL_ERROR_SIZE + 2];
+    va_start(ap, fmt);
+    len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
+
+    if(data->set.errorbuffer && !data->state.errorbuf) {
+      strcpy(data->set.errorbuffer, error);
+      data->state.errorbuf = TRUE; /* wrote error string */
+    }
+    error[len++] = '\n';
+    error[len] = '\0';
+    Curl_debug(data, CURLINFO_TEXT, error, len);
+    va_end(ap);
+  }
+}
+
+/* Curl_infof() is for info message along the way */
+#define MAXINFO 2048
+
+void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(data && data->set.verbose) {
+    va_list ap;
+    int len;
+    char buffer[MAXINFO + 2];
+    va_start(ap, fmt);
+    len = mvsnprintf(buffer, MAXINFO, fmt, ap);
+    va_end(ap);
+    buffer[len++] = '\n';
+    buffer[len] = '\0';
+    Curl_debug(data, CURLINFO_TEXT, buffer, len);
+  }
+}
+
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+
+void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+                       const char *fmt, ...)
+{
+  DEBUGASSERT(cf);
+  if(data && Curl_trc_cf_is_verbose(cf, data)) {
+    va_list ap;
+    int len;
+    char buffer[MAXINFO + 2];
+    len = msnprintf(buffer, MAXINFO, "[%s] ", cf->cft->name);
+    va_start(ap, fmt);
+    len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+    va_end(ap);
+    buffer[len++] = '\n';
+    buffer[len] = '\0';
+    Curl_debug(data, CURLINFO_TEXT, buffer, len);
+  }
+}
+
+
+static struct Curl_cftype *cf_types[] = {
+  &Curl_cft_tcp,
+  &Curl_cft_udp,
+  &Curl_cft_unix,
+  &Curl_cft_tcp_accept,
+  &Curl_cft_happy_eyeballs,
+  &Curl_cft_setup,
+#ifdef USE_NGHTTP2
+  &Curl_cft_nghttp2,
+#endif
+#ifdef USE_SSL
+  &Curl_cft_ssl,
+  &Curl_cft_ssl_proxy,
+#endif
+#if !defined(CURL_DISABLE_PROXY)
+#if !defined(CURL_DISABLE_HTTP)
+  &Curl_cft_h1_proxy,
+#ifdef USE_NGHTTP2
+  &Curl_cft_h2_proxy,
+#endif
+  &Curl_cft_http_proxy,
+#endif /* !CURL_DISABLE_HTTP */
+  &Curl_cft_haproxy,
+  &Curl_cft_socks_proxy,
+#endif /* !CURL_DISABLE_PROXY */
+#ifdef ENABLE_QUIC
+  &Curl_cft_http3,
+#endif
+#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+  &Curl_cft_http_connect,
+#endif
+  NULL,
+};
+
+CURLcode Curl_trc_opt(const char *config)
+{
+  char *token, *tok_buf, *tmp;
+  size_t i;
+  int lvl;
+
+  tmp = strdup(config);
+  if(!tmp)
+    return CURLE_OUT_OF_MEMORY;
+
+  token = strtok_r(tmp, ", ", &tok_buf);
+  while(token) {
+    switch(*token) {
+      case '-':
+        lvl = CURL_LOG_LVL_NONE;
+        ++token;
+        break;
+      case '+':
+        lvl = CURL_LOG_LVL_INFO;
+        ++token;
+        break;
+      default:
+        lvl = CURL_LOG_LVL_INFO;
+        break;
+    }
+    for(i = 0; cf_types[i]; ++i) {
+      if(strcasecompare(token, "all")) {
+        cf_types[i]->log_level = lvl;
+      }
+      else if(strcasecompare(token, cf_types[i]->name)) {
+        cf_types[i]->log_level = lvl;
+        break;
+      }
+    }
+    token = strtok_r(NULL, ", ", &tok_buf);
+  }
+  free(tmp);
+  return CURLE_OK;
+}
+
+CURLcode Curl_trc_init(void)
+{
+#ifdef DEBUGBUILD
+  /* WIP: we use the auto-init from an env var only in DEBUG builds for
+   * convenience. */
+  const char *config = getenv("CURL_DEBUG");
+  if(config) {
+    return Curl_trc_opt(config);
+  }
+#endif
+  return CURLE_OK;
+}
+#else /* !CURL_DISABLE_VERBOSE_STRINGS) */
+
+CURLcode Curl_trc_init(void)
+{
+  return CURLE_OK;
+}
+
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+                       const char *fmt, ...)
+{
+  (void)data;
+  (void)cf;
+  (void)fmt;
+}
+#endif
+
+#endif /* !DEBUGBUILD */
diff --git a/Utilities/cmcurl/lib/curl_trc.h b/Utilities/cmcurl/lib/curl_trc.h
new file mode 100644
index 0000000..84b5471
--- /dev/null
+++ b/Utilities/cmcurl/lib/curl_trc.h
@@ -0,0 +1,150 @@
+#ifndef HEADER_CURL_TRC_H
+#define HEADER_CURL_TRC_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+struct Curl_easy;
+struct Curl_cfilter;
+
+/**
+ * Init logging, return != 0 on failure.
+ */
+CURLcode Curl_trc_init(void);
+
+/**
+ * Configure tracing. May be called several times during global
+ * initialization. Later calls may not take effect.
+ *
+ * Configuration format supported:
+ * - comma-separated list of component names to enable logging on.
+ *   E.g. 'http/2,ssl'. Unknown names are ignored. Names are compared
+ *   case-insensitive.
+ * - component 'all' applies to all known log components
+ * - prefixing a component with '+' or '-' will en-/disable logging for
+ *   that component
+ * Example: 'all,-ssl' would enable logging for all components but the
+ * SSL filters.
+ *
+ * @param config configuration string
+ */
+CURLcode Curl_trc_opt(const char *config);
+
+/* the function used to output verbose information */
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+                char *ptr, size_t size);
+
+/**
+ * Output an informational message when transfer's verbose logging is enabled.
+ */
+void Curl_infof(struct Curl_easy *data,
+#if defined(__GNUC__) && !defined(printf) &&                    \
+  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+  !defined(__MINGW32__)
+                       const char *fmt, ...)
+                       __attribute__((format(printf, 2, 3)));
+#else
+                       const char *fmt, ...);
+#endif
+
+/**
+ * Output a failure message on registered callbacks for transfer.
+ */
+void Curl_failf(struct Curl_easy *data,
+#if defined(__GNUC__) && !defined(printf) &&                    \
+  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+  !defined(__MINGW32__)
+                       const char *fmt, ...)
+                       __attribute__((format(printf, 2, 3)));
+#else
+                       const char *fmt, ...);
+#endif
+
+#define failf Curl_failf
+
+/**
+ * Output an informational message when both transfer's verbose logging
+ * and connection filters verbose logging are enabled.
+ */
+void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+#if defined(__GNUC__) && !defined(printf) &&                    \
+  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
+  !defined(__MINGW32__)
+                       const char *fmt, ...)
+                       __attribute__((format(printf, 3, 4)));
+#else
+                       const char *fmt, ...);
+#endif
+
+#define CURL_LOG_LVL_NONE  0
+#define CURL_LOG_LVL_INFO  1
+
+
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+/* informational messages enabled */
+
+#define Curl_trc_is_verbose(data)    ((data) && (data)->set.verbose)
+#define Curl_trc_cf_is_verbose(cf, data) \
+                            ((data) && (data)->set.verbose && \
+                            (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
+
+/* explainer: we have some mix configuration and werror settings
+ * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced
+ * on gnuc and some other compiler. Need to treat carefully.
+ */
+#if defined(HAVE_VARIADIC_MACROS_C99) && \
+    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+
+#define infof(data, ...) \
+  do { if(Curl_trc_is_verbose(data)) \
+         Curl_infof(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_CF(data, cf, ...) \
+  do { if(Curl_trc_cf_is_verbose(cf, data)) \
+         Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
+
+#else /* no variadic macro args */
+#define infof Curl_infof
+#define CURL_TRC_CF Curl_trc_cf_infof
+#endif /* variadic macro args */
+
+#else /* !CURL_DISABLE_VERBOSE_STRINGS */
+/* All informational messages are not compiled in for size savings */
+
+#define Curl_trc_is_verbose(d)        ((void)(d), FALSE)
+#define Curl_trc_cf_is_verbose(x,y)   ((void)(x), (void)(y), FALSE)
+
+#if defined(HAVE_VARIADIC_MACROS_C99)
+#define infof(...)  Curl_nop_stmt
+#define CURL_TRC_CF(...)  Curl_nop_stmt
+#define Curl_trc_cf_infof(...)  Curl_nop_stmt
+#elif defined(HAVE_VARIADIC_MACROS_GCC)
+#define infof(x...)  Curl_nop_stmt
+#define CURL_TRC_CF(x...)  Curl_nop_stmt
+#define Curl_trc_cf_infof(x...)  Curl_nop_stmt
+#else
+#error "missing VARIADIC macro define, fix and rebuild!"
+#endif
+
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+
+#endif /* HEADER_CURL_TRC_H */
diff --git a/Utilities/cmcurl/lib/doh.c b/Utilities/cmcurl/lib/doh.c
index 7a38eab..bb0c89e 100644
--- a/Utilities/cmcurl/lib/doh.c
+++ b/Utilities/cmcurl/lib/doh.c
@@ -242,6 +242,7 @@
     /* pass in the struct pointer via a local variable to please coverity and
        the gcc typecheck helpers */
     struct dynbuf *resp = &p->serverdoh;
+    doh->internal = true;
     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
@@ -307,6 +308,10 @@
       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
     if(data->set.ssl.fsslctxp)
       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
+    if(data->set.fdebug)
+      ERROR_CHECK_SETOPT(CURLOPT_DEBUGFUNCTION, data->set.fdebug);
+    if(data->set.debugdata)
+      ERROR_CHECK_SETOPT(CURLOPT_DEBUGDATA, data->set.debugdata);
     if(data->set.str[STRING_SSL_EC_CURVES]) {
       ERROR_CHECK_SETOPT(CURLOPT_SSL_EC_CURVES,
                          data->set.str[STRING_SSL_EC_CURVES]);
diff --git a/Utilities/cmcurl/lib/dynbuf.h b/Utilities/cmcurl/lib/dynbuf.h
index 57ad62b..31a9130 100644
--- a/Utilities/cmcurl/lib/dynbuf.h
+++ b/Utilities/cmcurl/lib/dynbuf.h
@@ -81,8 +81,6 @@
 #define DYN_PAUSE_BUFFER    (64 * 1024 * 1024)
 #define DYN_HAXPROXY        2048
 #define DYN_HTTP_REQUEST    (1024*1024)
-#define DYN_H2_HEADERS      (128*1024)
-#define DYN_H2_TRAILERS     (128*1024)
 #define DYN_APRINTF         8000000
 #define DYN_RTSP_REQ_HEADER (64*1024)
 #define DYN_TRAILERS        (64*1024)
@@ -91,4 +89,5 @@
 #define DYN_H1_TRAILER      4096
 #define DYN_PINGPPONG_CMD   (64*1024)
 #define DYN_IMAP_CMD        (64*1024)
+#define DYN_MQTT_RECV       (64*1024)
 #endif
diff --git a/Utilities/cmcurl/lib/dynhds.c b/Utilities/cmcurl/lib/dynhds.c
index 007dfc5..979b3e8 100644
--- a/Utilities/cmcurl/lib/dynhds.c
+++ b/Utilities/cmcurl/lib/dynhds.c
@@ -344,6 +344,8 @@
   return Curl_dynhds_remove(dynhds, name, strlen(name));
 }
 
+#endif
+
 CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
 {
   CURLcode result = CURLE_OK;
@@ -363,4 +365,3 @@
   return result;
 }
 
-#endif
diff --git a/Utilities/cmcurl/lib/easy.c b/Utilities/cmcurl/lib/easy.c
index d36cc03..6b4fb8e 100644
--- a/Utilities/cmcurl/lib/easy.c
+++ b/Utilities/cmcurl/lib/easy.c
@@ -63,6 +63,7 @@
 #include "slist.h"
 #include "mime.h"
 #include "amigaos.h"
+#include "macos.h"
 #include "warnless.h"
 #include "sigpipe.h"
 #include "vssh/ssh.h"
@@ -83,7 +84,7 @@
 
 /* true globals -- for curl_global_init() and curl_global_cleanup() */
 static unsigned int  initialized;
-static long          init_flags;
+static long          easy_init_flags;
 
 #ifdef GLOBAL_INIT_IS_THREADSAFE
 
@@ -157,8 +158,8 @@
 #endif
   }
 
-  if(Curl_log_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_log_init failed\n"));
+  if(Curl_trc_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n"));
     goto fail;
   }
 
@@ -167,19 +168,20 @@
     goto fail;
   }
 
-#ifdef WIN32
   if(Curl_win32_init(flags)) {
     DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
     goto fail;
   }
-#endif
 
-#ifdef __AMIGA__
   if(Curl_amiga_init()) {
     DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
     goto fail;
   }
-#endif
+
+  if(Curl_macos_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n"));
+    goto fail;
+  }
 
   if(Curl_resolver_global_init()) {
     DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
@@ -199,7 +201,7 @@
   }
 #endif
 
-  init_flags = flags;
+  easy_init_flags = flags;
 
 #ifdef DEBUGBUILD
   if(getenv("CURL_GLOBAL_INIT"))
@@ -274,7 +276,7 @@
 
 /**
  * curl_global_cleanup() globally cleanups curl, uses the value of
- * "init_flags" to determine what needs to be cleaned up and what doesn't.
+ * "easy_init_flags" to determine what needs to be cleaned up and what doesn't.
  */
 void curl_global_cleanup(void)
 {
@@ -294,25 +296,42 @@
   Curl_resolver_global_cleanup();
 
 #ifdef WIN32
-  Curl_win32_cleanup(init_flags);
+  Curl_win32_cleanup(easy_init_flags);
 #endif
 
   Curl_amiga_cleanup();
 
   Curl_ssh_cleanup();
 
-#ifdef USE_WOLFSSH
-  (void)wolfSSH_Cleanup();
-#endif
 #ifdef DEBUGBUILD
   free(leakpointer);
 #endif
 
-  init_flags  = 0;
+  easy_init_flags = 0;
 
   global_init_unlock();
 }
 
+/**
+ * curl_global_trace() globally initializes curl logging.
+ */
+CURLcode curl_global_trace(const char *config)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  CURLcode result;
+  global_init_lock();
+
+  result = Curl_trc_opt(config);
+
+  global_init_unlock();
+
+  return result;
+#else
+  (void)config;
+  return CURLE_OK;
+#endif
+}
+
 /*
  * curl_global_sslset() globally initializes the SSL backend to use.
  */
@@ -693,7 +712,7 @@
  *
  * REALITY: it can't just create and destroy the multi handle that easily. It
  * needs to keep it around since if this easy handle is used again by this
- * function, the same multi handle must be re-used so that the same pools and
+ * function, the same multi handle must be reused so that the same pools and
  * caches can be used.
  *
  * DEBUG: if 'events' is set TRUE, this function will use a replacement engine
@@ -893,6 +912,8 @@
   /* the connection cache is setup on demand */
   outcurl->state.conn_cache = NULL;
   outcurl->state.lastconnect_id = -1;
+  outcurl->state.recent_conn_id = -1;
+  outcurl->id = -1;
 
   outcurl->progress.flags    = data->progress.flags;
   outcurl->progress.callback = data->progress.callback;
@@ -901,9 +922,7 @@
   if(data->cookies) {
     /* If cookies are enabled in the parent handle, we enable them
        in the clone as well! */
-    outcurl->cookies = Curl_cookie_init(data,
-                                        data->cookies->filename,
-                                        outcurl->cookies,
+    outcurl->cookies = Curl_cookie_init(data, NULL, outcurl->cookies,
                                         data->set.cookiesession);
     if(!outcurl->cookies)
       goto fail;
@@ -1040,7 +1059,7 @@
   memset(&data->state.authhost, 0, sizeof(struct auth));
   memset(&data->state.authproxy, 0, sizeof(struct auth));
 
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
   Curl_http_auth_cleanup_digest(data);
 #endif
 }
@@ -1064,11 +1083,14 @@
   CURLcode result = CURLE_OK;
   int oldstate;
   int newstate;
+  bool recursive = FALSE;
 
   if(!GOOD_EASY_HANDLE(data) || !data->conn)
     /* crazy input, don't continue */
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
+  if(Curl_is_in_callback(data))
+    recursive = TRUE;
   k = &data->req;
   oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
 
@@ -1096,34 +1118,9 @@
 
   if(!(newstate & KEEP_RECV_PAUSE)) {
     Curl_conn_ev_data_pause(data, FALSE);
-
-    if(data->state.tempcount) {
-      /* there are buffers for sending that can be delivered as the receive
-         pausing is lifted! */
-      unsigned int i;
-      unsigned int count = data->state.tempcount;
-      struct tempbuf writebuf[3]; /* there can only be three */
-
-      /* copy the structs to allow for immediate re-pausing */
-      for(i = 0; i < data->state.tempcount; i++) {
-        writebuf[i] = data->state.tempwrite[i];
-        Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
-      }
-      data->state.tempcount = 0;
-
-      for(i = 0; i < count; i++) {
-        /* even if one function returns error, this loops through and frees
-           all buffers */
-        if(!result)
-          result = Curl_client_write(data, writebuf[i].type,
-                                     Curl_dyn_ptr(&writebuf[i].b),
-                                     Curl_dyn_len(&writebuf[i].b));
-        Curl_dyn_free(&writebuf[i].b);
-      }
-
-      if(result)
-        return result;
-    }
+    result = Curl_client_unpause(data);
+    if(result)
+      return result;
   }
 
 #ifdef USE_HYPER
@@ -1160,6 +1157,11 @@
        corresponding socket callback, if used */
     result = Curl_updatesocket(data);
 
+  if(recursive)
+    /* this might have called a callback recursively which might have set this
+       to false again on exit */
+    Curl_set_in_callback(data, TRUE);
+
   return result;
 }
 
diff --git a/Utilities/cmcurl/lib/easy_lock.h b/Utilities/cmcurl/lib/easy_lock.h
index 5fa9477..d3fffd0 100644
--- a/Utilities/cmcurl/lib/easy_lock.h
+++ b/Utilities/cmcurl/lib/easy_lock.h
@@ -1,3 +1,5 @@
+#ifndef HEADER_CURL_EASY_LOCK_H
+#define HEADER_CURL_EASY_LOCK_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -29,13 +31,6 @@
 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
 
 #ifdef __MINGW32__
-#ifndef __MINGW64_VERSION_MAJOR
-#if (__MINGW32_MAJOR_VERSION < 5) || \
-    (__MINGW32_MAJOR_VERSION == 5 && __MINGW32_MINOR_VERSION == 0)
-/* mingw >= 5.0.1 defines SRWLOCK, and slightly different from MS define */
-typedef PVOID SRWLOCK, *PSRWLOCK;
-#endif
-#endif
 #ifndef SRWLOCK_INIT
 #define SRWLOCK_INIT NULL
 #endif
@@ -103,3 +98,5 @@
 #undef  GLOBAL_INIT_IS_THREADSAFE
 
 #endif
+
+#endif /* HEADER_CURL_EASY_LOCK_H */
diff --git a/Utilities/cmcurl/lib/easyoptions.c b/Utilities/cmcurl/lib/easyoptions.c
index a9c1efd..e69c658 100644
--- a/Utilities/cmcurl/lib/easyoptions.c
+++ b/Utilities/cmcurl/lib/easyoptions.c
@@ -120,6 +120,7 @@
   {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
    CURLOT_LONG, 0},
   {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0},
+  {"HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0},
   {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0},
   {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0},
   {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0},
@@ -164,7 +165,9 @@
   {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0},
   {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0},
   {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0},
-  {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOT_LONG, 0},
+  {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS,
+   CURLOT_LONG, CURLOT_FLAG_ALIAS},
+  {"MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0},
   {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0},
   {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0},
   {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0},
@@ -370,6 +373,6 @@
  */
 int Curl_easyopts_check(void)
 {
-  return ((CURLOPT_LASTENTRY%10000) != (322 + 1));
+  return ((CURLOPT_LASTENTRY%10000) != (323 + 1));
 }
 #endif
diff --git a/Utilities/cmcurl/lib/escape.c b/Utilities/cmcurl/lib/escape.c
index 56aa2b3..5af00c3 100644
--- a/Utilities/cmcurl/lib/escape.c
+++ b/Utilities/cmcurl/lib/escape.c
@@ -38,33 +38,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/* Portable character check (remember EBCDIC). Do not use isalnum() because
-   its behavior is altered by the current locale.
-   See https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
-*/
-bool Curl_isunreserved(unsigned char in)
-{
-  switch(in) {
-    case '0': case '1': case '2': case '3': case '4':
-    case '5': case '6': case '7': case '8': case '9':
-    case 'a': case 'b': case 'c': case 'd': case 'e':
-    case 'f': case 'g': case 'h': case 'i': case 'j':
-    case 'k': case 'l': case 'm': case 'n': case 'o':
-    case 'p': case 'q': case 'r': case 's': case 't':
-    case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
-    case 'A': case 'B': case 'C': case 'D': case 'E':
-    case 'F': case 'G': case 'H': case 'I': case 'J':
-    case 'K': case 'L': case 'M': case 'N': case 'O':
-    case 'P': case 'Q': case 'R': case 'S': case 'T':
-    case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
-    case '-': case '.': case '_': case '~':
-      return TRUE;
-    default:
-      break;
-  }
-  return FALSE;
-}
-
 /* for ABI-compatibility with previous versions */
 char *curl_escape(const char *string, int inlength)
 {
@@ -99,7 +72,7 @@
   while(length--) {
     unsigned char in = *string++; /* treat the characters unsigned */
 
-    if(Curl_isunreserved(in)) {
+    if(ISUNRESERVED(in)) {
       /* append this */
       if(Curl_dyn_addn(&d, &in, 1))
         return NULL;
@@ -233,3 +206,29 @@
 {
   free(p);
 }
+
+/*
+ * Curl_hexencode()
+ *
+ * Converts binary input to lowercase hex-encoded ASCII output.
+ * Null-terminated.
+ */
+void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
+                    unsigned char *out, size_t olen) /* output buffer size */
+{
+  const char *hex = "0123456789abcdef";
+  DEBUGASSERT(src && len && (olen >= 3));
+  if(src && len && (olen >= 3)) {
+    while(len-- && (olen >= 3)) {
+      /* clang-tidy warns on this line without this comment: */
+      /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
+      *out++ = hex[(*src & 0xF0)>>4];
+      *out++ = hex[*src & 0x0F];
+      ++src;
+      olen -= 2;
+    }
+    *out = 0;
+  }
+  else if(olen)
+    *out = 0;
+}
diff --git a/Utilities/cmcurl/lib/escape.h b/Utilities/cmcurl/lib/escape.h
index cdbb712..690e417 100644
--- a/Utilities/cmcurl/lib/escape.h
+++ b/Utilities/cmcurl/lib/escape.h
@@ -26,7 +26,7 @@
 /* Escape and unescape URL encoding in strings. The functions return a new
  * allocated string or NULL if an error occurred.  */
 
-bool Curl_isunreserved(unsigned char in);
+#include "curl_ctype.h"
 
 enum urlreject {
   REJECT_NADA = 2,
@@ -38,4 +38,7 @@
                         char **ostring, size_t *olen,
                         enum urlreject ctrl);
 
+void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
+                    unsigned char *out, size_t olen); /* output buffer size */
+
 #endif /* HEADER_CURL_ESCAPE_H */
diff --git a/Utilities/cmcurl/lib/file.c b/Utilities/cmcurl/lib/file.c
index c751e88..ffa9fb7 100644
--- a/Utilities/cmcurl/lib/file.c
+++ b/Utilities/cmcurl/lib/file.c
@@ -571,7 +571,9 @@
     if(result)
       return result;
 
-    Curl_pgrsSetDownloadCounter(data, bytecount);
+    result = Curl_pgrsSetDownloadCounter(data, bytecount);
+    if(result)
+      return result;
 
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
diff --git a/Utilities/cmcurl/lib/fopen.c b/Utilities/cmcurl/lib/fopen.c
index f710dbf..75b8a7a 100644
--- a/Utilities/cmcurl/lib/fopen.c
+++ b/Utilities/cmcurl/lib/fopen.c
@@ -56,15 +56,15 @@
   int fd = -1;
   *tempname = NULL;
 
-  if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-    /* a non-regular file, fallback to direct fopen() */
-    *fh = fopen(filename, FOPEN_WRITETEXT);
-    if(*fh)
-      return CURLE_OK;
+  *fh = fopen(filename, FOPEN_WRITETEXT);
+  if(!*fh)
     goto fail;
-  }
+  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode))
+    return CURLE_OK;
+  fclose(*fh);
+  *fh = NULL;
 
-  result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix));
+  result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix));
   if(result)
     goto fail;
 
@@ -85,7 +85,7 @@
     if((fstat(fd, &nsb) != -1) &&
        (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) {
       /* if the user and group are the same, clone the original mode */
-      if(fchmod(fd, sb.st_mode) == -1)
+      if(fchmod(fd, (mode_t)sb.st_mode) == -1)
         goto fail;
     }
   }
diff --git a/Utilities/cmcurl/lib/formdata.c b/Utilities/cmcurl/lib/formdata.c
index 2bdb9f2..e40c4bc 100644
--- a/Utilities/cmcurl/lib/formdata.c
+++ b/Utilities/cmcurl/lib/formdata.c
@@ -27,7 +27,7 @@
 #include <curl/curl.h>
 
 #include "formdata.h"
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
 
 #if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
 #include <libgen.h>
@@ -789,6 +789,20 @@
   return res;
 }
 
+/* wrap call to fseeko so it matches the calling convention of callback */
+static int fseeko_wrapper(void *stream, curl_off_t offset, int whence)
+{
+#if defined(HAVE_FSEEKO)
+  return fseeko(stream, (off_t)offset, whence);
+#elif defined(HAVE__FSEEKI64)
+  return _fseeki64(stream, (__int64)offset, whence);
+#else
+  if(offset > LONG_MAX)
+    return -1;
+  return fseek(stream, (long)offset, whence);
+#endif
+}
+
 /*
  * Curl_getformdata() converts a linked list of "meta data" into a mime
  * structure. The input list is in 'post', while the output is stored in
@@ -874,8 +888,7 @@
                compatibility: use of "-" pseudo file name should be avoided. */
             result = curl_mime_data_cb(part, (curl_off_t) -1,
                                        (curl_read_callback) fread,
-                                       CURLX_FUNCTION_CAST(curl_seek_callback,
-                                                           fseek),
+                                       fseeko_wrapper,
                                        NULL, (void *) stdin);
           }
           else
@@ -941,7 +954,7 @@
 void curl_formfree(struct curl_httppost *form)
 {
   (void)form;
-  /* does nothing HTTP is disabled */
+  /* Nothing to do. */
 }
 
 #endif  /* if disabled */
diff --git a/Utilities/cmcurl/lib/formdata.h b/Utilities/cmcurl/lib/formdata.h
index caabb63..af46624 100644
--- a/Utilities/cmcurl/lib/formdata.h
+++ b/Utilities/cmcurl/lib/formdata.h
@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 
-#ifndef CURL_DISABLE_MIME
+#ifndef CURL_DISABLE_FORM_API
 
 /* used by FormAdd for temporary storage */
 struct FormInfo {
@@ -53,10 +53,7 @@
                           curl_mimepart *,
                           struct curl_httppost *post,
                           curl_read_callback fread_func);
-#else
-/* disabled */
-#define Curl_getformdata(a,b,c,d) CURLE_NOT_BUILT_IN
-#endif
+#endif /* CURL_DISABLE_FORM_API */
 
 
 #endif /* HEADER_CURL_FORMDATA_H */
diff --git a/Utilities/cmcurl/lib/ftp.c b/Utilities/cmcurl/lib/ftp.c
index 4f50cb4..6e7fda0 100644
--- a/Utilities/cmcurl/lib/ftp.c
+++ b/Utilities/cmcurl/lib/ftp.c
@@ -32,9 +32,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -93,14 +90,14 @@
 
 /* Local API functions */
 #ifndef DEBUGBUILD
-static void _state(struct Curl_easy *data,
-                   ftpstate newstate);
-#define state(x,y) _state(x,y)
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate);
+#define ftp_state(x,y) _ftp_state(x,y)
 #else
-static void _state(struct Curl_easy *data,
-                   ftpstate newstate,
-                   int lineno);
-#define state(x,y) _state(x,y,__LINE__)
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate,
+                       int lineno);
+#define ftp_state(x,y) _ftp_state(x,y,__LINE__)
 #endif
 
 static CURLcode ftp_sendquote(struct Curl_easy *data,
@@ -463,7 +460,7 @@
   }
 
   conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
-  state(data, FTP_STOP);
+  ftp_state(data, FTP_STOP);
 
   return CURLE_OK;
 }
@@ -591,7 +588,7 @@
      * generically is a good idea.
      */
     infof(data, "We got a 421 - timeout");
-    state(data, FTP_STOP);
+    ftp_state(data, FTP_STOP);
     return CURLE_OPERATION_TIMEDOUT;
   }
 
@@ -750,10 +747,10 @@
 #endif
 
 /* This is the ONLY way to change FTP state! */
-static void _state(struct Curl_easy *data,
-                   ftpstate newstate
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate
 #ifdef DEBUGBUILD
-                   , int lineno
+                       , int lineno
 #endif
   )
 {
@@ -784,7 +781,7 @@
   if(!result) {
     struct ftp_conn *ftpc = &conn->proto.ftpc;
     ftpc->ftp_trying_alternative = FALSE;
-    state(data, FTP_USER);
+    ftp_state(data, FTP_USER);
   }
   return result;
 }
@@ -794,7 +791,7 @@
 {
   CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
   if(!result)
-    state(data, FTP_PWD);
+    ftp_state(data, FTP_PWD);
 
   return result;
 }
@@ -865,14 +862,14 @@
     if(conn->bits.reuse && ftpc->entrypath &&
        /* no need to go to entrypath when we have an absolute path */
        !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
-      /* This is a re-used connection. Since we change directory to where the
+      /* This is a reused connection. Since we change directory to where the
          transfer is taking place, we must first get back to the original dir
          where we ended up after login: */
       ftpc->cwdcount = 0; /* we count this as the first path, then we add one
                              for all upcoming ones in the ftp->dirs[] array */
       result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
       if(!result)
-        state(data, FTP_CWD);
+        ftp_state(data, FTP_CWD);
     }
     else {
       if(ftpc->dirdepth) {
@@ -882,7 +879,7 @@
         result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                ftpc->dirs[ftpc->cwdcount -1]);
         if(!result)
-          state(data, FTP_CWD);
+          ftp_state(data, FTP_CWD);
       }
       else {
         /* No CWD necessary */
@@ -975,7 +972,7 @@
         if(ip_end) {
           /* either ipv6 or (ipv4|domain|interface):port(-range) */
 #ifdef ENABLE_IPV6
-          if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
+          if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
             /* ipv6 */
             port_min = port_max = 0;
             strcpy(addr, string_ftpport);
@@ -1261,11 +1258,11 @@
   if(result)
     goto out;
   portsock = CURL_SOCKET_BAD; /* now held in filter */
-  state(data, FTP_PORT);
+  ftp_state(data, FTP_PORT);
 
 out:
   if(result) {
-    state(data, FTP_STOP);
+    ftp_state(data, FTP_STOP);
   }
   if(portsock != CURL_SOCKET_BAD)
     Curl_socket_close(data, conn, portsock);
@@ -1307,7 +1304,7 @@
   result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
   if(!result) {
     ftpc->count1 = modeoff;
-    state(data, FTP_PASV);
+    ftp_state(data, FTP_PASV);
     infof(data, "Connect data stream passively");
   }
   return result;
@@ -1330,7 +1327,7 @@
     /* doesn't transfer any data */
 
     /* still possibly do PRE QUOTE jobs */
-    state(data, FTP_RETR_PREQUOTE);
+    ftp_state(data, FTP_RETR_PREQUOTE);
     result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
   }
   else if(data->set.ftp_use_port) {
@@ -1355,7 +1352,7 @@
         result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
                                conn->proto.ftpc.file);
       if(!result)
-        state(data, FTP_PRET);
+        ftp_state(data, FTP_PRET);
     }
     else
       result = ftp_state_use_pasv(data, conn);
@@ -1377,7 +1374,7 @@
        whether it supports range */
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
     if(!result)
-      state(data, FTP_REST);
+      ftp_state(data, FTP_REST);
   }
   else
     result = ftp_state_prepare_transfer(data);
@@ -1398,7 +1395,7 @@
     /* we know ftpc->file is a valid pointer to a file name */
     result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
     if(!result)
-      state(data, FTP_SIZE);
+      ftp_state(data, FTP_SIZE);
   }
   else
     result = ftp_state_rest(data, conn);
@@ -1466,7 +1463,7 @@
   free(cmd);
 
   if(!result)
-    state(data, FTP_LIST);
+    ftp_state(data, FTP_LIST);
 
   return result;
 }
@@ -1530,7 +1527,7 @@
     result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
 
     if(!result)
-      state(data, FTP_MDTM);
+      ftp_state(data, FTP_MDTM);
   }
   else
     result = ftp_state_type(data);
@@ -1569,7 +1566,7 @@
       /* Got no given size to start from, figure it out */
       result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
       if(!result)
-        state(data, FTP_STOR_SIZE);
+        ftp_state(data, FTP_STOR_SIZE);
       return result;
     }
 
@@ -1624,7 +1621,7 @@
          * ftp_done() because we didn't transfer anything! */
         ftp->transfer = PPTRANSFER_NONE;
 
-        state(data, FTP_STOP);
+        ftp_state(data, FTP_STOP);
         return CURLE_OK;
       }
     }
@@ -1634,7 +1631,7 @@
   result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s",
                          ftpc->file);
   if(!result)
-    state(data, FTP_STOR);
+    ftp_state(data, FTP_STOR);
 
   return result;
 }
@@ -1695,7 +1692,7 @@
       result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
       if(result)
         return result;
-      state(data, instate);
+      ftp_state(data, instate);
       quote = TRUE;
     }
   }
@@ -1709,7 +1706,7 @@
       break;
     case FTP_RETR_PREQUOTE:
       if(ftp->transfer != PPTRANSFER_BODY)
-        state(data, FTP_STOP);
+        ftp_state(data, FTP_STOP);
       else {
         if(ftpc->known_filesize != -1) {
           Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
@@ -1731,12 +1728,12 @@
             */
             result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
             if(!result)
-              state(data, FTP_RETR);
+              ftp_state(data, FTP_RETR);
           }
           else {
             result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
             if(!result)
-              state(data, FTP_RETR_SIZE);
+              ftp_state(data, FTP_RETR_SIZE);
           }
         }
       }
@@ -1780,7 +1777,7 @@
   if(!result) {
     conn->proto.ftpc.count1++;
     /* remain in/go to the FTP_PASV state */
-    state(data, FTP_PASV);
+    ftp_state(data, FTP_PASV);
   }
   return result;
 }
@@ -1900,7 +1897,7 @@
     if(data->set.ftp_skip_ip) {
       /* told to ignore the remotely given IP but instead use the host we used
          for the control connection */
-      infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead",
+      infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
             ip[0], ip[1], ip[2], ip[3],
             conn->host.name);
       ftpc->newhost = strdup(control_address(conn));
@@ -2005,7 +2002,7 @@
     return CURLE_OUT_OF_MEMORY;
 
   conn->bits.do_more = TRUE;
-  state(data, FTP_STOP); /* this phase is completed */
+  ftp_state(data, FTP_STOP); /* this phase is completed */
 
   return result;
 }
@@ -2039,7 +2036,7 @@
   }
   else {
     infof(data, "Connect data stream actively");
-    state(data, FTP_STOP); /* end of DO phase */
+    ftp_state(data, FTP_STOP); /* end of DO phase */
     result = ftp_dophase_done(data, FALSE);
   }
 
@@ -2070,6 +2067,31 @@
   return TRUE;
 }
 
+static CURLcode client_write_header(struct Curl_easy *data,
+                                    char *buf, size_t blen)
+{
+  /* Some replies from an FTP server are written to the client
+   * as CLIENTWRITE_HEADER, formatted as if they came from a
+   * HTTP conversation.
+   * In all protocols, CLIENTWRITE_HEADER data is only passed to
+   * the body write callback when data->set.include_header is set
+   * via CURLOPT_HEADER.
+   * For historic reasons, FTP never played this game and expects
+   * all its HEADERs to do that always. Set that flag during the
+   * call to Curl_client_write() so it does the right thing.
+   *
+   * Notice that we cannot enable this flag for FTP in general,
+   * as an FTP transfer might involve a HTTP proxy connection and
+   * headers from CONNECT should not automatically be part of the
+   * output. */
+  CURLcode result;
+  int save = data->set.include_header;
+  data->set.include_header = TRUE;
+  result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen);
+  data->set.include_header = save? TRUE:FALSE;
+  return result;
+}
+
 static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
                                     int ftpcode)
 {
@@ -2123,8 +2145,7 @@
                   tm->tm_hour,
                   tm->tm_min,
                   tm->tm_sec);
-        result = Curl_client_write(data, CLIENTWRITE_BOTH, headerbuf,
-                                   headerbuflen);
+        result = client_write_header(data, headerbuf, headerbuflen);
         if(result)
           return result;
       } /* end of a ridiculous amount of conditionals */
@@ -2151,7 +2172,7 @@
           infof(data, "The requested document is not new enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
-          state(data, FTP_STOP);
+          ftp_state(data, FTP_STOP);
           return CURLE_OK;
         }
         break;
@@ -2160,7 +2181,7 @@
           infof(data, "The requested document is not old enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
-          state(data, FTP_STOP);
+          ftp_state(data, FTP_STOP);
           return CURLE_OK;
         }
         break;
@@ -2268,7 +2289,7 @@
       /* Set ->transfer so that we won't get any error in ftp_done()
        * because we didn't transfer the any file */
       ftp->transfer = PPTRANSFER_NONE;
-      state(data, FTP_STOP);
+      ftp_state(data, FTP_STOP);
       return CURLE_OK;
     }
 
@@ -2279,13 +2300,13 @@
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
                            data->state.resume_from);
     if(!result)
-      state(data, FTP_RETR_REST);
+      ftp_state(data, FTP_RETR_REST);
   }
   else {
     /* no resume */
     result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
     if(!result)
-      state(data, FTP_RETR);
+      ftp_state(data, FTP_RETR);
   }
 
   return result;
@@ -2334,7 +2355,7 @@
       char clbuf[128];
       int clbuflen = msnprintf(clbuf, sizeof(clbuf),
                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
-      result = Curl_client_write(data, CLIENTWRITE_BOTH, clbuf, clbuflen);
+      result = client_write_header(data, clbuf, clbuflen);
       if(result)
         return result;
     }
@@ -2368,8 +2389,7 @@
 #ifdef CURL_FTP_HTTPSTYLE_HEAD
     if(ftpcode == 350) {
       char buffer[24]= { "Accept-ranges: bytes\r\n" };
-      result = Curl_client_write(data, CLIENTWRITE_BOTH, buffer,
-                                 strlen(buffer));
+      result = client_write_header(data, buffer, strlen(buffer));
       if(result)
         return result;
     }
@@ -2385,7 +2405,7 @@
     else {
       result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
       if(!result)
-        state(data, FTP_RETR);
+        ftp_state(data, FTP_RETR);
     }
     break;
   }
@@ -2401,7 +2421,7 @@
 
   if(ftpcode >= 400) {
     failf(data, "Failed FTP upload: %0d", ftpcode);
-    state(data, FTP_STOP);
+    ftp_state(data, FTP_STOP);
     /* oops, we never close the sockets! */
     return CURLE_UPLOAD_FAILED;
   }
@@ -2412,7 +2432,7 @@
   if(data->set.ftp_use_port) {
     bool connected;
 
-    state(data, FTP_STOP); /* no longer in STOR state */
+    ftp_state(data, FTP_STOP); /* no longer in STOR state */
 
     result = AllowServerConnect(data, &connected);
     if(result)
@@ -2535,7 +2555,7 @@
       if(!connected) {
         struct ftp_conn *ftpc = &conn->proto.ftpc;
         infof(data, "Data conn was not available immediately");
-        state(data, FTP_STOP);
+        ftp_state(data, FTP_STOP);
         ftpc->wait_data_conn = TRUE;
       }
     }
@@ -2546,7 +2566,7 @@
     if((instate == FTP_LIST) && (ftpcode == 450)) {
       /* simply no matching files in the dir listing */
       ftp->transfer = PPTRANSFER_NONE; /* don't download anything */
-      state(data, FTP_STOP); /* this phase is over */
+      ftp_state(data, FTP_STOP); /* this phase is over */
     }
     else {
       failf(data, "RETR response: %03d", ftpcode);
@@ -2582,7 +2602,7 @@
     */
     result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
     if(!result)
-      state(data, FTP_PBSZ);
+      ftp_state(data, FTP_PBSZ);
   }
   else {
     result = ftp_state_pwd(data, conn);
@@ -2605,7 +2625,7 @@
     result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
                            conn->passwd?conn->passwd:"");
     if(!result)
-      state(data, FTP_PASS);
+      ftp_state(data, FTP_PASS);
   }
   else if(ftpcode/100 == 2) {
     /* 230 User ... logged in.
@@ -2617,7 +2637,7 @@
       result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
                              data->set.str[STRING_FTP_ACCOUNT]);
       if(!result)
-        state(data, FTP_ACCT);
+        ftp_state(data, FTP_ACCT);
     }
     else {
       failf(data, "ACCT requested but none available");
@@ -2638,7 +2658,7 @@
                       data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
       if(!result) {
         ftpc->ftp_trying_alternative = TRUE;
-        state(data, FTP_USER);
+        ftp_state(data, FTP_USER);
       }
     }
     else {
@@ -2741,7 +2761,7 @@
         result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
                                ftpauth[ftpc->count1]);
         if(!result)
-          state(data, FTP_AUTH);
+          ftp_state(data, FTP_AUTH);
       }
       else
         result = ftp_state_user(data, conn);
@@ -2808,7 +2828,7 @@
         Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
                       data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
       if(!result)
-        state(data, FTP_PROT);
+        ftp_state(data, FTP_PROT);
       break;
 
     case FTP_PROT:
@@ -2827,7 +2847,7 @@
          */
         result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
         if(!result)
-          state(data, FTP_CCC);
+          ftp_state(data, FTP_CCC);
       }
       else
         result = ftp_state_pwd(data, conn);
@@ -2919,7 +2939,7 @@
             infof(data, "Entry path is '%s'", ftpc->entrypath);
             /* also save it where getinfo can access it: */
             data->state.most_recent_ftp_entrypath = ftpc->entrypath;
-            state(data, FTP_SYST);
+            ftp_state(data, FTP_SYST);
             break;
           }
 
@@ -2935,7 +2955,7 @@
           infof(data, "Failed to figure out path");
         }
       }
-      state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
@@ -2970,7 +2990,7 @@
           /* remember target server OS */
           Curl_safefree(ftpc->server_os);
           ftpc->server_os = os;
-          state(data, FTP_NAMEFMT);
+          ftp_state(data, FTP_NAMEFMT);
           break;
         }
         /* Nothing special for the target server. */
@@ -2982,7 +3002,7 @@
         /* Cannot identify server OS. Continue anyway and cross fingers. */
       }
 
-      state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
@@ -2993,7 +3013,7 @@
         break;
       }
 
-      state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
@@ -3026,7 +3046,7 @@
           result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
                                  ftpc->dirs[ftpc->cwdcount - 1]);
           if(!result)
-            state(data, FTP_MKD);
+            ftp_state(data, FTP_MKD);
         }
         else {
           /* return failure */
@@ -3055,7 +3075,7 @@
         result = CURLE_REMOTE_ACCESS_DENIED;
       }
       else {
-        state(data, FTP_CWD);
+        ftp_state(data, FTP_CWD);
         /* send CWD */
         result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                ftpc->dirs[ftpc->cwdcount - 1]);
@@ -3114,7 +3134,7 @@
       /* fallthrough, just stop! */
     default:
       /* internal error */
-      state(data, FTP_STOP);
+      ftp_state(data, FTP_STOP);
       break;
     }
   } /* if(ftpcode) */
@@ -3191,7 +3211,7 @@
 
   /* When we connect, we start in the state where we await the 220
      response */
-  state(data, FTP_WAIT220);
+  ftp_state(data, FTP_WAIT220);
 
   result = ftp_multi_statemach(data, done);
 
@@ -3460,7 +3480,7 @@
       /* if a command starts with an asterisk, which a legal FTP command never
          can, the command will be allowed to fail without it causing any
          aborts or cancels etc. It will cause libcurl to act as if the command
-         is successful, whatever the server reponds. */
+         is successful, whatever the server responds. */
 
       if(cmd[0] == '*') {
         cmd++;
@@ -3516,13 +3536,13 @@
   char want = (char)(ascii?'A':'I');
 
   if(ftpc->transfertype == want) {
-    state(data, newstate);
+    ftp_state(data, newstate);
     return ftp_state_type_resp(data, 200, newstate);
   }
 
   result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
   if(!result) {
-    state(data, newstate);
+    ftp_state(data, newstate);
 
     /* keep track of our current transfer type */
     ftpc->transfertype = want;
@@ -4040,11 +4060,11 @@
             curl_easy_strerror(result));
       conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
       connclose(conn, "QUIT command failed"); /* mark for connection closure */
-      state(data, FTP_STOP);
+      ftp_state(data, FTP_STOP);
       return result;
     }
 
-    state(data, FTP_QUIT);
+    ftp_state(data, FTP_QUIT);
 
     result = ftp_block_statemach(data, conn);
   }
diff --git a/Utilities/cmcurl/lib/ftplistparser.c b/Utilities/cmcurl/lib/ftplistparser.c
index 226d9bc..2a7ca5b 100644
--- a/Utilities/cmcurl/lib/ftplistparser.c
+++ b/Utilities/cmcurl/lib/ftplistparser.c
@@ -379,7 +379,7 @@
     /* scenario:
      * 1. call => OK..
      * 2. call => OUT_OF_MEMORY (or other error)
-     * 3. (last) call => is skipped RIGHT HERE and the error is hadled later
+     * 3. (last) call => is skipped RIGHT HERE and the error is handled later
      *    in wc_statemach()
      */
     goto fail;
diff --git a/Utilities/cmcurl/lib/getinfo.c b/Utilities/cmcurl/lib/getinfo.c
index 826ffd0..f1574e0 100644
--- a/Utilities/cmcurl/lib/getinfo.c
+++ b/Utilities/cmcurl/lib/getinfo.c
@@ -415,6 +415,13 @@
   case CURLINFO_RETRY_AFTER:
     *param_offt = data->info.retry_after;
     break;
+  case CURLINFO_XFER_ID:
+    *param_offt = data->id;
+    break;
+  case CURLINFO_CONN_ID:
+    *param_offt = data->conn?
+                  data->conn->connection_id : data->state.recent_conn_id;
+    break;
   default:
     return CURLE_UNKNOWN_OPTION;
   }
diff --git a/Utilities/cmcurl/lib/gopher.c b/Utilities/cmcurl/lib/gopher.c
index 4a11d93..61e41b7 100644
--- a/Utilities/cmcurl/lib/gopher.c
+++ b/Utilities/cmcurl/lib/gopher.c
@@ -185,7 +185,7 @@
     if(strlen(sel) < 1)
       break;
 
-    result = Curl_write(data, sockfd, sel, k, &amount);
+    result = Curl_nwrite(data, FIRSTSOCKET, sel, k, &amount);
     if(!result) { /* Which may not have written it all! */
       result = Curl_client_write(data, CLIENTWRITE_HEADER, sel, amount);
       if(result)
@@ -227,7 +227,7 @@
   free(sel_org);
 
   if(!result)
-    result = Curl_write(data, sockfd, "\r\n", 2, &amount);
+    result = Curl_nwrite(data, FIRSTSOCKET, "\r\n", 2, &amount);
   if(result) {
     failf(data, "Failed sending Gopher request");
     return result;
diff --git a/Utilities/cmcurl/lib/headers.c b/Utilities/cmcurl/lib/headers.c
index 4367ce7..3ff4d5e 100644
--- a/Utilities/cmcurl/lib/headers.c
+++ b/Utilities/cmcurl/lib/headers.c
@@ -300,9 +300,16 @@
     if(data->state.prevhead)
       /* line folding, append value to the previous header's value */
       return unfold_value(data, header, hlen);
-    else
-      /* can't unfold without a previous header */
-      return CURLE_BAD_FUNCTION_ARGUMENT;
+    else {
+      /* Can't unfold without a previous header. Instead of erroring, just
+         pass the leading blanks. */
+      while(hlen && ISBLANK(*header)) {
+        header++;
+        hlen--;
+      }
+      if(!hlen)
+        return CURLE_WEIRD_SERVER_REPLY;
+    }
   }
 
   hs = calloc(1, sizeof(*hs) + hlen);
diff --git a/Utilities/cmcurl/lib/hmac.c b/Utilities/cmcurl/lib/hmac.c
index 8d8de17..4019b67 100644
--- a/Utilities/cmcurl/lib/hmac.c
+++ b/Utilities/cmcurl/lib/hmac.c
@@ -26,7 +26,8 @@
 
 #include "curl_setup.h"
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI))         \
+  || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include <curl/curl.h>
 
@@ -169,4 +170,4 @@
   return CURLE_OK;
 }
 
-#endif /* CURL_DISABLE_CRYPTO_AUTH */
+#endif /* Using NTLM (without SSPI) or AWS */
diff --git a/Utilities/cmcurl/lib/hostip.c b/Utilities/cmcurl/lib/hostip.c
index d721403..3cd9a65 100644
--- a/Utilities/cmcurl/lib/hostip.c
+++ b/Utilities/cmcurl/lib/hostip.c
@@ -41,12 +41,8 @@
 #include <inet.h>
 #endif
 
-#ifdef HAVE_SETJMP_H
 #include <setjmp.h>
-#endif
-#ifdef HAVE_SIGNAL_H
 #include <signal.h>
-#endif
 
 #include "urldata.h"
 #include "sendf.h"
@@ -67,10 +63,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
-#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
-#endif
-
 #if defined(CURLRES_SYNCH) &&                   \
   defined(HAVE_ALARM) &&                        \
   defined(SIGALRM) &&                           \
@@ -126,19 +118,6 @@
 static void freednsentry(void *freethis);
 
 /*
- * Return # of addresses in a Curl_addrinfo struct
- */
-static int num_addresses(const struct Curl_addrinfo *addr)
-{
-  int i = 0;
-  while(addr) {
-    addr = addr->ai_next;
-    i++;
-  }
-  return i;
-}
-
-/*
  * Curl_printable_address() stores a printable version of the 1st address
  * given in the 'ai' argument. The result will be stored in the buf that is
  * bufsize bytes big.
@@ -392,6 +371,19 @@
 }
 
 #ifndef CURL_DISABLE_SHUFFLE_DNS
+/*
+ * Return # of addresses in a Curl_addrinfo struct
+ */
+static int num_addresses(const struct Curl_addrinfo *addr)
+{
+  int i = 0;
+  while(addr) {
+    addr = addr->ai_next;
+    i++;
+  }
+  return i;
+}
+
 UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
                                     struct Curl_addrinfo **addr);
 /*
@@ -561,6 +553,7 @@
 static struct Curl_addrinfo *get_localhost(int port, const char *name)
 {
   struct Curl_addrinfo *ca;
+  struct Curl_addrinfo *ca6;
   const size_t ss_size = sizeof(struct sockaddr_in);
   const size_t hostlen = strlen(name);
   struct sockaddr_in sa;
@@ -587,8 +580,12 @@
   memcpy(ca->ai_addr, &sa, ss_size);
   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
   strcpy(ca->ai_canonname, name);
-  ca->ai_next = get_localhost6(port, name);
-  return ca;
+
+  ca6 = get_localhost6(port, name);
+  if(!ca6)
+    return ca;
+  ca6->ai_next = ca;
+  return ca6;
 }
 
 #ifdef ENABLE_IPV6
@@ -600,7 +597,7 @@
   if(data) {
     /* the nature of most system is that IPv6 status doesn't come and go
        during a program's lifetime so we only probe the first time and then we
-       have the info kept for fast re-use */
+       have the info kept for fast reuse */
     DEBUGASSERT(data);
     DEBUGASSERT(data->multi);
     if(data->multi->ipv6_up == IPV6_UNKNOWN) {
@@ -743,23 +740,6 @@
         return CURLRESOLV_ERROR;
     }
 
-#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
-    {
-      /*
-       * The automagic conversion from IPv4 literals to IPv6 literals only
-       * works if the SCDynamicStoreCopyProxies system function gets called
-       * first. As Curl currently doesn't support system-wide HTTP proxies, we
-       * therefore don't use any value this function might return.
-       *
-       * This function is only available on a macOS and is not needed for
-       * IPv4-only builds, hence the conditions above.
-       */
-      CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
-      if(dict)
-        CFRelease(dict);
-    }
-#endif
-
 #ifndef USE_RESOLVE_ON_IPS
     /* First check if this is an IPv4 address string */
     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
@@ -1256,7 +1236,7 @@
       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
 
       if(dns) {
-        infof(data, "RESOLVE %.*s:%d is - old addresses discarded",
+        infof(data, "RESOLVE %.*s:%d - old addresses discarded",
               (int)hlen, host_begin, port);
         /* delete old entry, there are two reasons for this
          1. old entry may have different addresses.
diff --git a/Utilities/cmcurl/lib/hostip.h b/Utilities/cmcurl/lib/hostip.h
index 06d0867..b68f539 100644
--- a/Utilities/cmcurl/lib/hostip.h
+++ b/Utilities/cmcurl/lib/hostip.h
@@ -30,9 +30,7 @@
 #include "timeval.h" /* for timediff_t */
 #include "asyn.h"
 
-#ifdef HAVE_SETJMP_H
 #include <setjmp.h>
-#endif
 
 /* Allocate enough memory to hold the full name information structs and
  * everything. OSF1 is known to require at least 8872 bytes. The buffer
diff --git a/Utilities/cmcurl/lib/hsts.c b/Utilities/cmcurl/lib/hsts.c
index 53c01fc..7ecf004 100644
--- a/Utilities/cmcurl/lib/hsts.c
+++ b/Utilities/cmcurl/lib/hsts.c
@@ -57,7 +57,7 @@
 /* to play well with debug builds, we can *set* a fixed time this will
    return */
 time_t deltatime; /* allow for "adjustments" for unit test purposes */
-static time_t debugtime(void *unused)
+static time_t hsts_debugtime(void *unused)
 {
   char *timestr = getenv("CURL_TIME");
   (void)unused;
@@ -70,7 +70,8 @@
   }
   return time(NULL);
 }
-#define time(x) debugtime(x)
+#undef time
+#define time(x) hsts_debugtime(x)
 #endif
 
 struct hsts *Curl_hsts_init(void)
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c
index 219dcc2..40ef70d 100644
--- a/Utilities/cmcurl/lib/http.c
+++ b/Utilities/cmcurl/lib/http.c
@@ -233,7 +233,6 @@
   if(!http)
     return CURLE_OUT_OF_MEMORY;
 
-  Curl_mime_initpart(&http->form);
   data->req.p.http = http;
   connkeep(conn, "HTTP default");
 
@@ -342,6 +341,8 @@
 }
 
 #ifndef CURL_DISABLE_HTTP_AUTH
+
+#ifndef CURL_DISABLE_BASIC_AUTH
 /*
  * http_output_basic() sets up an Authorization: header (or the proxy version)
  * for HTTP Basic authentication.
@@ -403,6 +404,9 @@
   return result;
 }
 
+#endif
+
+#ifndef CURL_DISABLE_BEARER_AUTH
 /*
  * http_output_bearer() sets up an Authorization: header
  * for HTTP Bearer authentication.
@@ -430,6 +434,8 @@
 
 #endif
 
+#endif
+
 /* pickoneauth() selects the most favourable authentication method from the
  * ones available and the ones we want.
  *
@@ -446,18 +452,26 @@
      of preference in case of the existence of multiple accepted types. */
   if(avail & CURLAUTH_NEGOTIATE)
     pick->picked = CURLAUTH_NEGOTIATE;
+#ifndef CURL_DISABLE_BEARER_AUTH
   else if(avail & CURLAUTH_BEARER)
     pick->picked = CURLAUTH_BEARER;
+#endif
+#ifndef CURL_DISABLE_DIGEST_AUTH
   else if(avail & CURLAUTH_DIGEST)
     pick->picked = CURLAUTH_DIGEST;
+#endif
   else if(avail & CURLAUTH_NTLM)
     pick->picked = CURLAUTH_NTLM;
   else if(avail & CURLAUTH_NTLM_WB)
     pick->picked = CURLAUTH_NTLM_WB;
+#ifndef CURL_DISABLE_BASIC_AUTH
   else if(avail & CURLAUTH_BASIC)
     pick->picked = CURLAUTH_BASIC;
+#endif
+#ifndef CURL_DISABLE_AWS
   else if(avail & CURLAUTH_AWS_SIGV4)
     pick->picked = CURLAUTH_AWS_SIGV4;
+#endif
   else {
     pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
     picked = FALSE;
@@ -723,11 +737,11 @@
   CURLcode result = CURLE_OK;
   (void)conn;
 
-#ifdef CURL_DISABLE_CRYPTO_AUTH
+#ifdef CURL_DISABLE_DIGEST_AUTH
   (void)request;
   (void)path;
 #endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_AWS
   if(authstatus->picked == CURLAUTH_AWS_SIGV4) {
     auth = "AWS_SIGV4";
     result = Curl_output_aws_sigv4(data, proxy);
@@ -763,7 +777,7 @@
   }
   else
 #endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
   if(authstatus->picked == CURLAUTH_DIGEST) {
     auth = "Digest";
     result = Curl_output_digest(data,
@@ -775,6 +789,7 @@
   }
   else
 #endif
+#ifndef CURL_DISABLE_BASIC_AUTH
   if(authstatus->picked == CURLAUTH_BASIC) {
     /* Basic */
     if(
@@ -794,6 +809,8 @@
        functions work that way */
     authstatus->done = TRUE;
   }
+#endif
+#ifndef CURL_DISABLE_BEARER_AUTH
   if(authstatus->picked == CURLAUTH_BEARER) {
     /* Bearer */
     if((!proxy && data->set.str[STRING_BEARER] &&
@@ -808,6 +825,7 @@
        functions work that way */
     authstatus->done = TRUE;
   }
+#endif
 
   if(auth) {
 #ifndef CURL_DISABLE_PROXY
@@ -866,7 +884,12 @@
 #ifndef CURL_DISABLE_PROXY
     (conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
 #endif
-     data->state.aptr.user || data->set.str[STRING_BEARER])
+     data->state.aptr.user ||
+#ifdef USE_SPNEGO
+     authhost->want & CURLAUTH_NEGOTIATE ||
+     authproxy->want & CURLAUTH_NEGOTIATE ||
+#endif
+     data->set.str[STRING_BEARER])
     /* continue please */;
   else {
     authhost->done = TRUE;
@@ -1064,7 +1087,7 @@
       }
       else
 #endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
         if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
           if((authp->avail & CURLAUTH_DIGEST) != 0)
             infof(data, "Ignoring duplicate digest auth header.");
@@ -1087,6 +1110,7 @@
         }
         else
 #endif
+#ifndef CURL_DISABLE_BASIC_AUTH
           if(checkprefix("Basic", auth) &&
              is_valid_auth_separator(auth[5])) {
             *availp |= CURLAUTH_BASIC;
@@ -1101,6 +1125,8 @@
             }
           }
           else
+#endif
+#ifndef CURL_DISABLE_BEARER_AUTH
             if(checkprefix("Bearer", auth) &&
                is_valid_auth_separator(auth[6])) {
               *availp |= CURLAUTH_BEARER;
@@ -1113,6 +1139,9 @@
                 data->state.authproblem = TRUE;
               }
             }
+#else
+           ;
+#endif
 
     /* there may be multiple methods on one line, so keep reading */
     while(*auth && *auth != ',') /* read up to the next comma */
@@ -1130,8 +1159,6 @@
  * http_should_fail() determines whether an HTTP response has gotten us
  * into an error state or not.
  *
- * @param conn all information about the current connection
- *
  * @retval FALSE communications should continue
  *
  * @retval TRUE communications should not continue
@@ -1277,7 +1304,7 @@
                           curl_off_t *bytes_written,
                           /* how much of the buffer contains body data */
                           curl_off_t included_body_bytes,
-                          int socketindex)
+                          int sockindex)
 {
   ssize_t amount;
   CURLcode result;
@@ -1285,12 +1312,9 @@
   size_t size;
   struct connectdata *conn = data->conn;
   size_t sendsize;
-  curl_socket_t sockfd;
   size_t headersize;
 
-  DEBUGASSERT(socketindex <= SECONDARYSOCKET);
-
-  sockfd = Curl_conn_get_socket(data, socketindex);
+  DEBUGASSERT(sockindex <= SECONDARYSOCKET && sockindex >= 0);
 
   /* The looping below is required since we use non-blocking sockets, but due
      to the circumstances we will just loop and try again and again etc */
@@ -1308,7 +1332,7 @@
       || IS_HTTPS_PROXY(conn->http_proxy.proxytype)
 #endif
        )
-     && conn->httpversion != 20) {
+     && conn->httpversion < 20) {
     /* Make sure this doesn't send more body bytes than what the max send
        speed says. The request bytes do not count to the max speed.
     */
@@ -1372,9 +1396,25 @@
       else
         sendsize = size;
     }
+
+    /* We currently cannot send more that this for http here:
+     * - if sending blocks, it return 0 as amount
+     * - we then whisk aside the `in` into the `http` struct
+     *   and install our own `data->state.fread_func` that
+     *   on subsequent calls reads `in` empty.
+     * - when the whisked away `in` is empty, the `fread_func`
+     *   is restored ot its original state.
+     * The problem is that `fread_func` can only return
+     * `upload_buffer_size` lengths. If the send we do here
+     * is larger and blocks, we do re-sending with smaller
+     * amounts of data and connection filters do not like
+     * that.
+     */
+    if(http && (sendsize > (size_t)data->set.upload_buffer_size))
+      sendsize = (size_t)data->set.upload_buffer_size;
   }
 
-  result = Curl_write(data, sockfd, ptr, sendsize, &amount);
+  result = Curl_nwrite(data, sockindex, ptr, sendsize, &amount);
 
   if(!result) {
     /*
@@ -1527,7 +1567,7 @@
   struct connectdata *conn = data->conn;
 
   /* We default to persistent connections. We set this already in this connect
-     function to make the re-use checks properly be able to check this bit. */
+     function to make the reuse checks properly be able to check this bit. */
   connkeep(conn, "HTTP default");
 
   return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done);
@@ -1562,8 +1602,6 @@
   data->state.authhost.multipass = FALSE;
   data->state.authproxy.multipass = FALSE;
 
-  Curl_unencode_cleanup(data);
-
   /* set the proper values (possibly modified on POST) */
   conn->seek_func = data->set.seek_func; /* restore */
   conn->seek_client = data->set.seek_client; /* restore */
@@ -1572,7 +1610,6 @@
     return CURLE_OK;
 
   Curl_dyn_free(&http->send_buffer);
-  Curl_mime_cleanpart(&http->form);
   Curl_dyn_reset(&data->state.headerb);
   Curl_hyper_done(data);
   Curl_ws_done(data);
@@ -2370,45 +2407,53 @@
 
   switch(httpreq) {
   case HTTPREQ_POST_MIME:
-    http->sendit = &data->set.mimepost;
+    data->state.mimepost = &data->set.mimepost;
     break;
+#ifndef CURL_DISABLE_FORM_API
   case HTTPREQ_POST_FORM:
-    /* Convert the form structure into a mime structure. */
-    Curl_mime_cleanpart(&http->form);
-    result = Curl_getformdata(data, &http->form, data->set.httppost,
-                              data->state.fread_func);
-    if(result)
-      return result;
-    http->sendit = &http->form;
+    /* Convert the form structure into a mime structure, then keep
+       the conversion */
+    if(!data->state.formp) {
+      data->state.formp = calloc(sizeof(curl_mimepart), 1);
+      if(!data->state.formp)
+        return CURLE_OUT_OF_MEMORY;
+      Curl_mime_cleanpart(data->state.formp);
+      result = Curl_getformdata(data, data->state.formp, data->set.httppost,
+                                data->state.fread_func);
+      if(result)
+        return result;
+      data->state.mimepost = data->state.formp;
+    }
     break;
+#endif
   default:
-    http->sendit = NULL;
+    data->state.mimepost = NULL;
   }
 
 #ifndef CURL_DISABLE_MIME
-  if(http->sendit) {
+  if(data->state.mimepost) {
     const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
 
     /* Read and seek body only. */
-    http->sendit->flags |= MIME_BODY_ONLY;
+    data->state.mimepost->flags |= MIME_BODY_ONLY;
 
     /* Prepare the mime structure headers & set content type. */
 
     if(cthdr)
       for(cthdr += 13; *cthdr == ' '; cthdr++)
         ;
-    else if(http->sendit->kind == MIMEKIND_MULTIPART)
+    else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
       cthdr = "multipart/form-data";
 
-    curl_mime_headers(http->sendit, data->set.headers, 0);
-    result = Curl_mime_prepare_headers(data, http->sendit, cthdr,
+    curl_mime_headers(data->state.mimepost, data->set.headers, 0);
+    result = Curl_mime_prepare_headers(data, data->state.mimepost, cthdr,
                                        NULL, MIMESTRATEGY_FORM);
-    curl_mime_headers(http->sendit, NULL, 0);
+    curl_mime_headers(data->state.mimepost, NULL, 0);
     if(!result)
-      result = Curl_mime_rewind(http->sendit);
+      result = Curl_mime_rewind(data->state.mimepost);
     if(result)
       return result;
-    http->postsize = Curl_mime_size(http->sendit);
+    http->postsize = Curl_mime_size(data->state.mimepost);
   }
 #endif
 
@@ -2564,7 +2609,7 @@
     {
       struct curl_slist *hdr;
 
-      for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
+      for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
         result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
         if(result)
           return result;
@@ -2599,7 +2644,7 @@
 
     /* Read from mime structure. */
     data->state.fread_func = (curl_read_callback) Curl_mime_read;
-    data->state.in = (void *) http->sendit;
+    data->state.in = (void *) data->state.mimepost;
     http->sending = HTTPSEND_BODY;
 
     /* this sends the buffer and frees all the buffer resources */
@@ -2667,11 +2712,7 @@
 #ifndef USE_HYPER
     /* With Hyper the body is always passed on separately */
     if(data->set.postfields) {
-
-      /* In HTTP2, we send request body in DATA frame regardless of
-         its size. */
-      if(conn->httpversion < 20 &&
-         !data->state.expect100header &&
+      if(!data->state.expect100header &&
          (http->postsize < MAX_INITIAL_POST_SIZE)) {
         /* if we don't use expect: 100  AND
            postsize is less than MAX_INITIAL_POST_SIZE
@@ -2687,7 +2728,7 @@
 
         if(!data->req.upload_chunky) {
           /* We're not sending it 'chunked', append it to the request
-             already now to reduce the number if send() calls */
+             already now to reduce the number of send() calls */
           result = Curl_dyn_addn(r, data->set.postfields,
                                  (size_t)http->postsize);
           included_body = http->postsize;
@@ -2832,16 +2873,18 @@
     }
     if(co) {
       struct Cookie *store = co;
+      size_t clen = 8; /* hold the size of the generated Cookie: header */
       /* now loop through all cookies that matched */
       while(co) {
         if(co->value) {
-          if(0 == count) {
+          size_t add;
+          if(!count) {
             result = Curl_dyn_addn(r, STRCONST("Cookie: "));
             if(result)
               break;
           }
-          if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >=
-             MAX_COOKIE_HEADER_LEN) {
+          add = strlen(co->name) + strlen(co->value) + 1;
+          if(clen + add >= MAX_COOKIE_HEADER_LEN) {
             infof(data, "Restricted outgoing cookies due to header size, "
                   "'%s' not sent", co->name);
             linecap = TRUE;
@@ -2851,6 +2894,7 @@
                                  co->name, co->value);
           if(result)
             break;
+          clen += add + (count ? 2 : 0);
           count++;
         }
         co = co->next; /* next cookie please */
@@ -3025,7 +3069,7 @@
       *done = TRUE;
       return CURLE_OK;
     }
-    /* We have a new url to load, but since we want to be able to re-use this
+    /* We have a new url to load, but since we want to be able to reuse this
        connection properly, we read the full response in "ignore more" */
     k->ignorebody = TRUE;
     infof(data, "Ignoring the response-body");
@@ -3065,7 +3109,7 @@
       data->info.httpcode = 304;
       infof(data, "Simulate an HTTP 304 response");
       /* we abort the transfer before it is completed == we ruin the
-         re-use ability. Close the connection */
+         reuse ability. Close the connection */
       streamclose(conn, "Simulated 304 handling");
       return CURLE_OK;
     }
@@ -3309,8 +3353,8 @@
                   altused ? altused : ""
       );
 
-  /* clear userpwd and proxyuserpwd to avoid re-using old credentials
-   * from re-used connections */
+  /* clear userpwd and proxyuserpwd to avoid reusing old credentials
+   * from reused connections */
   Curl_safefree(data->state.aptr.userpwd);
   Curl_safefree(data->state.aptr.proxyuserpwd);
   free(altused);
@@ -3381,6 +3425,9 @@
     }
   }
 
+  if(data->req.upload_done)
+    Curl_conn_ev_data_done_send(data);
+
   if((conn->httpversion >= 20) && data->req.upload_chunky)
     /* upload_chunky was set above to set up the request in a chunky fashion,
        but is disabled here again to avoid that the chunked encoded version is
@@ -3916,6 +3963,34 @@
   return CURLE_OK;
 }
 
+CURLcode Curl_bump_headersize(struct Curl_easy *data,
+                              size_t delta,
+                              bool connect_only)
+{
+  size_t bad = 0;
+  unsigned int max = MAX_HTTP_RESP_HEADER_SIZE;
+  if(delta < MAX_HTTP_RESP_HEADER_SIZE) {
+    data->info.header_size += (unsigned int)delta;
+    data->req.allheadercount += (unsigned int)delta;
+    if(!connect_only)
+      data->req.headerbytecount += (unsigned int)delta;
+    if(data->req.allheadercount > max)
+      bad = data->req.allheadercount;
+    else if(data->info.header_size > (max * 20)) {
+      bad = data->info.header_size;
+      max *= 20;
+    }
+  }
+  else
+    bad = data->req.allheadercount + delta;
+  if(bad) {
+    failf(data, "Too large response headers: %zu > %u", bad, max);
+    return CURLE_RECV_ERROR;
+  }
+  return CURLE_OK;
+}
+
+
 /*
  * Read any HTTP header lines from the server and pass them to the client app.
  */
@@ -4054,6 +4129,7 @@
           /* Switching Protocols */
           if(k->upgr101 == UPGR101_H2) {
             /* Switching to HTTP/2 */
+            DEBUGASSERT(conn->httpversion < 20);
             infof(data, "Received 101, Switching to HTTP/2");
             k->upgr101 = UPGR101_RECEIVED;
 
@@ -4096,6 +4172,11 @@
         }
       }
       else {
+        if(k->upgr101 == UPGR101_H2) {
+          /* A requested upgrade was denied, poke the multi handle to possibly
+             allow a pending pipewait to continue */
+          Curl_multi_connchanged(data->multi);
+        }
         k->header = FALSE; /* no more header to parse! */
 
         if((k->size == -1) && !k->chunk && !conn->bits.close &&
@@ -4153,7 +4234,6 @@
       /* now, only output this if the header AND body are requested:
        */
       writetype = CLIENTWRITE_HEADER |
-        (data->set.include_header ? CLIENTWRITE_BODY : 0) |
         ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
 
       headerlen = Curl_dyn_len(&data->state.headerb);
@@ -4163,8 +4243,9 @@
       if(result)
         return result;
 
-      data->info.header_size += (long)headerlen;
-      data->req.headerbytecount += (long)headerlen;
+      result = Curl_bump_headersize(data, headerlen, FALSE);
+      if(result)
+        return result;
 
       /*
        * When all the headers have been parsed, see if we should give
@@ -4227,7 +4308,18 @@
               if((k->httpcode == 417) && data->state.expect100header) {
                 /* 417 Expectation Failed - try again without the Expect
                    header */
-                infof(data, "Got 417 while waiting for a 100");
+                if(!k->writebytecount &&
+                   k->exp100 == EXP100_AWAITING_CONTINUE) {
+                  infof(data, "Got HTTP failure 417 while waiting for a 100");
+                }
+                else {
+                  infof(data, "Got HTTP failure 417 while sending data");
+                  streamclose(conn,
+                              "Stop sending data before everything sent");
+                  result = http_perhapsrewind(data, conn);
+                  if(result)
+                    return result;
+                }
                 data->state.disableexpect = TRUE;
                 DEBUGASSERT(!data->req.newurl);
                 data->req.newurl = strdup(data->state.url);
@@ -4473,8 +4565,6 @@
     /*
      * End of header-checks. Write them to the client.
      */
-    if(data->set.include_header)
-      writetype |= CLIENTWRITE_BODY;
     if(k->httpcode/100 == 1)
       writetype |= CLIENTWRITE_1XX;
 
@@ -4486,8 +4576,10 @@
     if(result)
       return result;
 
-    data->info.header_size += Curl_dyn_len(&data->state.headerb);
-    data->req.headerbytecount += Curl_dyn_len(&data->state.headerb);
+    result = Curl_bump_headersize(data, Curl_dyn_len(&data->state.headerb),
+                                  FALSE);
+    if(result)
+      return result;
 
     Curl_dyn_reset(&data->state.headerb);
   }
@@ -4569,8 +4661,8 @@
     if(!req->path)
       goto out;
   }
-  Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
@@ -4727,8 +4819,8 @@
   if(result)
     goto out;
 
-  Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
@@ -4858,8 +4950,8 @@
     if(!resp->description)
       goto out;
   }
-  Curl_dynhds_init(&resp->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&resp->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
diff --git a/Utilities/cmcurl/lib/http.h b/Utilities/cmcurl/lib/http.h
index df3b4e3..9ee3c65 100644
--- a/Utilities/cmcurl/lib/http.h
+++ b/Utilities/cmcurl/lib/http.h
@@ -64,6 +64,10 @@
 
 struct dynhds;
 
+CURLcode Curl_bump_headersize(struct Curl_easy *data,
+                              size_t delta,
+                              bool connect_only);
+
 /* Header specific functions */
 bool Curl_compareheader(const char *headerline,  /* line to check */
                         const char *header,   /* header keyword _with_ colon */
@@ -183,21 +187,19 @@
 #define EXPECT_100_THRESHOLD (1024*1024)
 #endif
 
+/* MAX_HTTP_RESP_HEADER_SIZE is the maximum size of all response headers
+   combined that libcurl allows for a single HTTP response, any HTTP
+   version. This count includes CONNECT response headers. */
+#define MAX_HTTP_RESP_HEADER_SIZE (300*1024)
+
 #endif /* CURL_DISABLE_HTTP */
 
 /****************************************************************************
  * HTTP unique setup
  ***************************************************************************/
 struct HTTP {
-  curl_mimepart *sendit;
   curl_off_t postsize; /* off_t to handle large file sizes */
   const char *postdata;
-
-  const char *p_pragma;      /* Pragma: string */
-
-  /* For FORM posting */
-  curl_mimepart form;
-
   struct back {
     curl_read_callback fread_func; /* backup storage for fread pointer */
     void *fread_in;           /* backup storage for fread_in pointer */
@@ -292,7 +294,7 @@
 
 /**
  * Create the list of HTTP/2 headers which represent the request,
- * using HTTP/2 pseudo headers preceeding the `req->headers`.
+ * using HTTP/2 pseudo headers preceding the `req->headers`.
  *
  * Applies the following transformations:
  * - if `authority` is set, any "Host" header is removed.
diff --git a/Utilities/cmcurl/lib/http1.c b/Utilities/cmcurl/lib/http1.c
index 46fe855..182234c 100644
--- a/Utilities/cmcurl/lib/http1.c
+++ b/Utilities/cmcurl/lib/http1.c
@@ -38,124 +38,97 @@
 #include "memdebug.h"
 
 
-#define MAX_URL_LEN   (4*1024)
+#define H1_MAX_URL_LEN   (8*1024)
 
 void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len)
 {
   memset(parser, 0, sizeof(*parser));
   parser->max_line_len = max_line_len;
-  Curl_bufq_init(&parser->scratch, max_line_len, 1);
+  Curl_dyn_init(&parser->scratch, max_line_len);
 }
 
 void Curl_h1_req_parse_free(struct h1_req_parser *parser)
 {
   if(parser) {
     Curl_http_req_free(parser->req);
-    Curl_bufq_free(&parser->scratch);
+    Curl_dyn_free(&parser->scratch);
     parser->req = NULL;
     parser->done = FALSE;
   }
 }
 
+static CURLcode trim_line(struct h1_req_parser *parser, int options)
+{
+  DEBUGASSERT(parser->line);
+  if(parser->line_len) {
+    if(parser->line[parser->line_len - 1] == '\n')
+      --parser->line_len;
+    if(parser->line_len) {
+      if(parser->line[parser->line_len - 1] == '\r')
+        --parser->line_len;
+      else if(options & H1_PARSE_OPT_STRICT)
+        return CURLE_URL_MALFORMAT;
+    }
+    else if(options & H1_PARSE_OPT_STRICT)
+      return CURLE_URL_MALFORMAT;
+  }
+  else if(options & H1_PARSE_OPT_STRICT)
+    return CURLE_URL_MALFORMAT;
+
+  if(parser->line_len > parser->max_line_len) {
+    return CURLE_URL_MALFORMAT;
+  }
+  return CURLE_OK;
+}
+
 static ssize_t detect_line(struct h1_req_parser *parser,
-                           const char *buf, const size_t buflen, int options,
+                           const char *buf, const size_t buflen,
                            CURLcode *err)
 {
   const char  *line_end;
-  size_t len;
 
   DEBUGASSERT(!parser->line);
   line_end = memchr(buf, '\n', buflen);
   if(!line_end) {
-    *err = (buflen > parser->max_line_len)? CURLE_URL_MALFORMAT : CURLE_AGAIN;
+    *err = CURLE_AGAIN;
     return -1;
   }
-  len = line_end - buf + 1;
-  if(len > parser->max_line_len) {
-    *err = CURLE_URL_MALFORMAT;
-    return -1;
-  }
-
-  if(options & H1_PARSE_OPT_STRICT) {
-    if((len == 1) || (buf[len - 2] != '\r')) {
-      *err = CURLE_URL_MALFORMAT;
-      return -1;
-    }
-    parser->line = buf;
-    parser->line_len = len - 2;
-  }
-  else {
-    parser->line = buf;
-    parser->line_len = len - (((len == 1) || (buf[len - 2] != '\r'))? 1 : 2);
-  }
+  parser->line = buf;
+  parser->line_len = line_end - buf + 1;
   *err = CURLE_OK;
-  return (ssize_t)len;
+  return (ssize_t)parser->line_len;
 }
 
 static ssize_t next_line(struct h1_req_parser *parser,
                          const char *buf, const size_t buflen, int options,
                          CURLcode *err)
 {
-  ssize_t nread = 0, n;
+  ssize_t nread = 0;
 
   if(parser->line) {
-    if(parser->scratch_skip) {
-      /* last line was from scratch. Remove it now, since we are done
-       * with it and look for the next one. */
-      Curl_bufq_skip_and_shift(&parser->scratch, parser->scratch_skip);
-      parser->scratch_skip = 0;
-    }
     parser->line = NULL;
     parser->line_len = 0;
+    Curl_dyn_reset(&parser->scratch);
   }
 
-  if(Curl_bufq_is_empty(&parser->scratch)) {
-    nread = detect_line(parser, buf, buflen, options, err);
-    if(nread < 0) {
-      if(*err != CURLE_AGAIN)
+  nread = detect_line(parser, buf, buflen, err);
+  if(nread >= 0) {
+    if(Curl_dyn_len(&parser->scratch)) {
+      /* append detected line to scratch to have the complete line */
+      *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len);
+      if(*err)
         return -1;
-      /* not a complete line, add to scratch for later revisit */
-      nread = Curl_bufq_write(&parser->scratch,
-                              (const unsigned char *)buf, buflen, err);
-      return nread;
+      parser->line = Curl_dyn_ptr(&parser->scratch);
+      parser->line_len = Curl_dyn_len(&parser->scratch);
     }
-    /* found one */
+    *err = trim_line(parser, options);
+    if(*err)
+      return -1;
   }
-  else {
-    const char *sbuf;
-    size_t sbuflen;
-
-    /* scratch contains bytes from last attempt, add more to it */
-    if(buflen) {
-      const char *line_end;
-      size_t add_len;
-      ssize_t pos;
-
-      line_end = memchr(buf, '\n', buflen);
-      pos = line_end? (line_end - buf + 1) : -1;
-      add_len = (pos >= 0)? (size_t)pos : buflen;
-      nread = Curl_bufq_write(&parser->scratch,
-                              (const unsigned char *)buf, add_len, err);
-      if(nread < 0) {
-        /* Unable to add anything to scratch is an error, since we should
-         * have seen a line there then before. */
-        if(*err == CURLE_AGAIN)
-          *err = CURLE_URL_MALFORMAT;
-        return -1;
-      }
-    }
-
-    if(Curl_bufq_peek(&parser->scratch,
-                      (const unsigned char **)&sbuf, &sbuflen)) {
-      n = detect_line(parser, sbuf, sbuflen, options, err);
-      if(n < 0 && *err != CURLE_AGAIN)
-        return -1;  /* real error */
-      parser->scratch_skip = (size_t)n;
-    }
-    else {
-      /* we SHOULD be able to peek at scratch data */
-      DEBUGASSERT(0);
-    }
+  else if(*err == CURLE_AGAIN) {
+    /* no line end in `buf`, add it to our scratch */
+    *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen);
+    nread = (*err)? -1 : (ssize_t)buflen;
   }
   return nread;
 }
@@ -190,7 +163,7 @@
       break;
     }
   }
-  /* no SPACE found or empty TARGET or empy HTTP_VERSION */
+  /* no SPACE found or empty TARGET or empty HTTP_VERSION */
   if(!target_len || !hv_len)
     goto out;
 
@@ -231,7 +204,7 @@
   else {
     /* origin-form OR absolute-form */
     CURLUcode uc;
-    char tmp[MAX_URL_LEN];
+    char tmp[H1_MAX_URL_LEN];
 
     /* default, unless we see an absolute URL */
     path = target;
@@ -328,7 +301,7 @@
         goto out;
       }
       parser->done = TRUE;
-      Curl_bufq_free(&parser->scratch);
+      Curl_dyn_reset(&parser->scratch);
       /* last chance adjustments */
     }
     else {
@@ -345,5 +318,29 @@
   return nread;
 }
 
+CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
+                                struct dynbuf *dbuf)
+{
+  CURLcode result;
+
+  result = Curl_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
+                         req->method,
+                         req->scheme? req->scheme : "",
+                         req->scheme? "://" : "",
+                         req->authority? req->authority : "",
+                         req->path? req->path : "",
+                         http_minor);
+  if(result)
+    goto out;
+
+  result = Curl_dynhds_h1_dprint(&req->headers, dbuf);
+  if(result)
+    goto out;
+
+  result = Curl_dyn_addn(dbuf, STRCONST("\r\n"));
+
+out:
+  return result;
+}
 
 #endif /* !CURL_DISABLE_HTTP */
diff --git a/Utilities/cmcurl/lib/http1.h b/Utilities/cmcurl/lib/http1.h
index 93111ef..2de302f 100644
--- a/Utilities/cmcurl/lib/http1.h
+++ b/Utilities/cmcurl/lib/http1.h
@@ -33,11 +33,11 @@
 #define H1_PARSE_OPT_NONE       (0)
 #define H1_PARSE_OPT_STRICT     (1 << 0)
 
-#define H1_PARSE_DEFAULT_MAX_LINE_LEN (8 * 1024)
+#define H1_PARSE_DEFAULT_MAX_LINE_LEN   DYN_HTTP_REQUEST
 
 struct h1_req_parser {
   struct httpreq *req;
-  struct bufq scratch;
+  struct dynbuf scratch;
   size_t scratch_skip;
   const char *line;
   size_t max_line_len;
@@ -56,6 +56,8 @@
 CURLcode Curl_h1_req_dprint(const struct httpreq *req,
                             struct dynbuf *dbuf);
 
+CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
+                                struct dynbuf *dbuf);
 
 #endif /* !CURL_DISABLE_HTTP */
 #endif /* HEADER_CURL_HTTP1_H */
diff --git a/Utilities/cmcurl/lib/http2.c b/Utilities/cmcurl/lib/http2.c
index 191d8cd..c8b0594 100644
--- a/Utilities/cmcurl/lib/http2.c
+++ b/Utilities/cmcurl/lib/http2.c
@@ -41,6 +41,7 @@
 #include "urlapi-int.h"
 #include "cfilters.h"
 #include "connect.h"
+#include "rand.h"
 #include "strtoofft.h"
 #include "strdup.h"
 #include "transfer.h"
@@ -69,7 +70,7 @@
 #define H2_CHUNK_SIZE           (16 * 1024)
 /* this is how much we want "in flight" for a stream */
 #define H2_STREAM_WINDOW_SIZE   (10 * 1024 * 1024)
-/* on receving from TLS, we prep for holding a full stream window */
+/* on receiving from TLS, we prep for holding a full stream window */
 #define H2_NW_RECV_CHUNKS       (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
 /* on send into TLS, we just want to accumulate small frames */
 #define H2_NW_SEND_CHUNKS       1
@@ -134,9 +135,11 @@
   BIT(conn_closed);
   BIT(goaway);
   BIT(enable_push);
+  BIT(nw_out_blocked);
 };
 
 /* How to access `call_data` from a cf_h2 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_h2_ctx *)(cf)->ctx)->call_data
 
@@ -173,8 +176,10 @@
   int32_t id; /* HTTP/2 protocol identifier for stream */
   struct bufq recvbuf; /* response buffer */
   struct bufq sendbuf; /* request buffer */
+  struct h1_req_parser h1; /* parsing the request */
   struct dynhds resp_trailers; /* response trailer fields */
   size_t resp_hds_len; /* amount of response header bytes in recvbuf */
+  size_t upload_blocked_len;
   curl_off_t upload_left; /* number of request bytes left to upload */
 
   char **push_headers;       /* allocated array */
@@ -183,6 +188,8 @@
 
   int status_code; /* HTTP response status code */
   uint32_t error; /* stream error code */
+  uint32_t local_window_size; /* the local recv window size */
+  bool resp_hds_complete; /* we have a complete, final response */
   bool closed; /* TRUE on stream close */
   bool reset;  /* TRUE on stream reset */
   bool close_handled; /* TRUE if stream closure is handled by libcurl */
@@ -209,9 +216,12 @@
 
   (void)cf;
   bits = CURL_CSELECT_IN;
-  if(!stream->send_closed && stream->upload_left)
+  if(!stream->send_closed &&
+     (stream->upload_left || stream->upload_blocked_len))
     bits |= CURL_CSELECT_OUT;
   if(data->state.dselect_bits != bits) {
+    CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x",
+                stream->id, bits);
     data->state.dselect_bits = bits;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
@@ -245,13 +255,15 @@
                   H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
                   H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_H2_TRAILERS);
+  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
+  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
   stream->resp_hds_len = 0;
   stream->bodystarted = FALSE;
   stream->status_code = -1;
   stream->closed = FALSE;
   stream->close_handled = FALSE;
   stream->error = NGHTTP2_NO_ERROR;
+  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
   stream->upload_left = 0;
 
   H2_STREAM_LCTX(data) = stream;
@@ -273,8 +285,8 @@
   if(ctx->h2) {
     if(!stream->closed && stream->id > 0) {
       /* RST_STREAM */
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] premature DATA_DONE, RST stream",
-                    stream->id));
+      CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream",
+                  stream->id);
       if(!nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
                                     stream->id, NGHTTP2_STREAM_CLOSED))
         (void)nghttp2_session_send(ctx->h2);
@@ -304,6 +316,7 @@
 
   Curl_bufq_free(&stream->sendbuf);
   Curl_bufq_free(&stream->recvbuf);
+  Curl_h1_req_parse_free(&stream->h1);
   Curl_dynhds_free(&stream->resp_trailers);
   if(stream->push_headers) {
     /* if they weren't used and then freed before */
@@ -356,8 +369,12 @@
 {
   struct Curl_cfilter *cf = writer_ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
+  ssize_t nwritten;
 
-  return Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
+  nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
+  if(nwritten > 0)
+    CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
+  return nwritten;
 }
 
 static ssize_t send_callback(nghttp2_session *h2,
@@ -365,6 +382,10 @@
                              void *userp);
 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
+                         void *userp);
+#endif
 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
                               int32_t stream_id,
                               const uint8_t *mem, size_t len, void *userp);
@@ -381,18 +402,6 @@
                           size_t len, void *userp);
 
 /*
- * multi_connchanged() is called to tell that there is a connection in
- * this multi handle that has changed state (multiplexing become possible, the
- * number of allowed streams changed or similar), and a subsequent use of this
- * multi handle should move CONNECT_PEND handles back to CONNECT to have them
- * retry.
- */
-static void multi_connchanged(struct Curl_multi *multi)
-{
-  multi->recheckstate = TRUE;
-}
-
-/*
  * Initialize the cfilter context
  */
 static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
@@ -419,6 +428,9 @@
 
   nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
   nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
+#endif
   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
     cbs, on_data_chunk_recv);
   nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
@@ -467,6 +479,7 @@
             stream->id);
       DEBUGASSERT(0);
     }
+    CURL_TRC_CF(data, cf, "created session via Upgrade");
   }
   else {
     nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
@@ -494,6 +507,8 @@
 
   /* all set, traffic will be send on connect */
   result = CURLE_OK;
+  CURL_TRC_CF(data, cf, "[0] created h2 session%s",
+              via_h1_upgrade? " (via h1 upgrade)" : "");
 
 out:
   if(cbs)
@@ -539,8 +554,8 @@
       break;
     }
     else {
-      DEBUGF(LOG_CF(data, cf, "process_pending_input: %zu bytes left "
-                    "in connection buffer", Curl_bufq_len(&ctx->inbufq)));
+      CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left "
+                  "in connection buffer", Curl_bufq_len(&ctx->inbufq));
     }
   }
 
@@ -580,11 +595,10 @@
     ssize_t nread = -1;
 
     *input_pending = FALSE;
-    Curl_attach_connection(data, cf->conn);
     nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
     if(nread != -1) {
-      DEBUGF(LOG_CF(data, cf, "%zd bytes stray data read before trying "
-                    "h2 connection", nread));
+      CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying "
+                  "h2 connection", nread);
       if(h2_process_pending_input(cf, data, &result) < 0)
         /* immediate error, considered dead */
         alive = FALSE;
@@ -592,11 +606,10 @@
         alive = !should_close_session(ctx);
       }
     }
-    else {
+    else if(result != CURLE_AGAIN) {
       /* the read failed so let's say this is dead anyway */
       alive = FALSE;
     }
-    Curl_detach_connection(data);
   }
 
   return alive;
@@ -644,13 +657,16 @@
   if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
 
-  DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes",
-                Curl_bufq_len(&ctx->outbufq)));
   nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
-  if(nwritten < 0 && result != CURLE_AGAIN) {
+  if(nwritten < 0) {
+    if(result == CURLE_AGAIN) {
+      CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
+                  Curl_bufq_len(&ctx->outbufq));
+      ctx->nw_out_blocked = 1;
+    }
     return result;
   }
-  return CURLE_OK;
+  return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
 }
 
 /*
@@ -676,15 +692,17 @@
                                   nw_out_writer, cf, &result);
   if(nwritten < 0) {
     if(result == CURLE_AGAIN) {
+      ctx->nw_out_blocked = 1;
       return NGHTTP2_ERR_WOULDBLOCK;
     }
     failf(data, "Failed sending HTTP2 data");
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
-  if(!nwritten)
+  if(!nwritten) {
+    ctx->nw_out_blocked = 1;
     return NGHTTP2_ERR_WOULDBLOCK;
-
+  }
   return nwritten;
 }
 
@@ -839,8 +857,8 @@
   struct cf_h2_ctx *ctx = cf->ctx;
   int rv; /* one of the CURL_PUSH_* defines */
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%d] PUSH_PROMISE received",
-                frame->promised_stream_id));
+  CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
+              frame->promised_stream_id);
   if(data->multi->push_cb) {
     struct stream_ctx *stream;
     struct stream_ctx *newstream;
@@ -859,7 +877,7 @@
     heads.data = data;
     heads.frame = frame;
     /* ask the application */
-    DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ask application"));
+    CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
 
     stream = H2_STREAM_CTX(data);
     if(!stream) {
@@ -931,7 +949,7 @@
     }
   }
   else {
-    DEBUGF(LOG_CF(data, cf, "Got PUSH_PROMISE, ignore it"));
+    CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
     rv = CURL_PUSH_DENY;
   }
 fail:
@@ -964,23 +982,23 @@
   struct stream_ctx *stream = H2_STREAM_CTX(data);
   int32_t stream_id = frame->hd.stream_id;
   CURLcode result;
+  size_t rbuflen;
   int rv;
 
   if(!stream) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No proto pointer", stream_id));
+    CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id);
     return CURLE_FAILED_INIT;
   }
 
   switch(frame->hd.type) {
   case NGHTTP2_DATA:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[DATA len=%zu pad=%zu], "
-                  "buffered=%zu, window=%d/%d",
-                  stream_id, frame->hd.length, frame->data.padlen,
-                  Curl_bufq_len(&stream->recvbuf),
-                  nghttp2_session_get_stream_effective_recv_data_length(
-                    ctx->h2, stream->id),
-                  nghttp2_session_get_stream_effective_local_window_size(
-                    ctx->h2, stream->id)));
+    rbuflen = Curl_bufq_len(&stream->recvbuf);
+    CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d",
+                stream_id, rbuflen,
+                nghttp2_session_get_stream_effective_recv_data_length(
+                  ctx->h2, stream->id),
+                nghttp2_session_get_stream_effective_local_window_size(
+                  ctx->h2, stream->id));
     /* If !body started on this stream, then receiving DATA is illegal. */
     if(!stream->bodystarted) {
       rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
@@ -993,9 +1011,22 @@
     if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
       drain_stream(cf, data, stream);
     }
+    else if(rbuflen > stream->local_window_size) {
+      int32_t wsize = nghttp2_session_get_stream_local_window_size(
+                        ctx->h2, stream->id);
+      if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) {
+        /* H2 flow control is not absolute, as the server might not have the
+         * same view, yet. When we receive more than we want, we enforce
+         * the local window size again to make nghttp2 send WINDOW_UPATEs
+         * accordingly. */
+        nghttp2_session_set_local_window_size(ctx->h2,
+                                              NGHTTP2_FLAG_NONE,
+                                              stream->id,
+                                              stream->local_window_size);
+      }
+    }
     break;
   case NGHTTP2_HEADERS:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[HEADERS]", stream_id));
     if(stream->bodystarted) {
       /* Only valid HEADERS after body started is trailer HEADERS.  We
          buffer them in on_header callback. */
@@ -1018,12 +1049,12 @@
     if(result)
       return result;
 
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] %zu header bytes",
-                  stream_id, Curl_bufq_len(&stream->recvbuf)));
+    if(stream->status_code / 100 != 1) {
+      stream->resp_hds_complete = TRUE;
+    }
     drain_stream(cf, data, stream);
     break;
   case NGHTTP2_PUSH_PROMISE:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[PUSH_PROMISE]", stream_id));
     rv = push_promise(cf, data, &frame->push_promise);
     if(rv) { /* deny! */
       DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
@@ -1033,38 +1064,127 @@
       if(nghttp2_is_fatal(rv))
         return CURLE_SEND_ERROR;
       else if(rv == CURL_PUSH_ERROROUT) {
-        DEBUGF(LOG_CF(data, cf, "[h2sid=%d] fail in PUSH_PROMISE received",
-                      stream_id));
+        CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received",
+                    stream_id);
         return CURLE_RECV_ERROR;
       }
     }
     break;
   case NGHTTP2_RST_STREAM:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[RST]", stream_id));
     stream->closed = TRUE;
-    stream->reset = TRUE;
+    if(frame->rst_stream.error_code) {
+      stream->reset = TRUE;
+    }
     stream->send_closed = TRUE;
     data->req.keepon &= ~KEEP_SEND_HOLD;
     drain_stream(cf, data, stream);
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[WINDOW_UPDATE]", stream_id));
     if((data->req.keepon & KEEP_SEND_HOLD) &&
        (data->req.keepon & KEEP_SEND)) {
       data->req.keepon &= ~KEEP_SEND_HOLD;
       drain_stream(cf, data, stream);
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after win update",
-                    stream_id));
+      CURL_TRC_CF(data, cf, "[%d] un-holding after win update",
+                  stream_id);
     }
     break;
   default:
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[%x]",
-                  stream_id, frame->hd.type));
     break;
   }
   return CURLE_OK;
 }
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
+{
+  switch(frame->hd.type) {
+    case NGHTTP2_DATA: {
+      return msnprintf(buffer, blen,
+                       "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
+                       (int)frame->hd.length,
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
+                       (int)frame->data.padlen);
+    }
+    case NGHTTP2_HEADERS: {
+      return msnprintf(buffer, blen,
+                       "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
+                       (int)frame->hd.length,
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+    }
+    case NGHTTP2_PRIORITY: {
+      return msnprintf(buffer, blen,
+                       "FRAME[PRIORITY, len=%d, flags=%d]",
+                       (int)frame->hd.length, frame->hd.flags);
+    }
+    case NGHTTP2_RST_STREAM: {
+      return msnprintf(buffer, blen,
+                       "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
+                       (int)frame->hd.length, frame->hd.flags,
+                       frame->rst_stream.error_code);
+    }
+    case NGHTTP2_SETTINGS: {
+      if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
+        return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
+      }
+      return msnprintf(buffer, blen,
+                       "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
+    }
+    case NGHTTP2_PUSH_PROMISE: {
+      return msnprintf(buffer, blen,
+                       "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
+                       (int)frame->hd.length,
+                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
+    }
+    case NGHTTP2_PING: {
+      return msnprintf(buffer, blen,
+                       "FRAME[PING, len=%d, ack=%d]",
+                       (int)frame->hd.length,
+                       frame->hd.flags&NGHTTP2_FLAG_ACK);
+    }
+    case NGHTTP2_GOAWAY: {
+      char scratch[128];
+      size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+        size_t len = (frame->goaway.opaque_data_len < s_len)?
+                      frame->goaway.opaque_data_len : s_len-1;
+        if(len)
+          memcpy(scratch, frame->goaway.opaque_data, len);
+        scratch[len] = '\0';
+        return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
+                         "last_stream=%d]", frame->goaway.error_code,
+                         scratch, frame->goaway.last_stream_id);
+    }
+    case NGHTTP2_WINDOW_UPDATE: {
+      return msnprintf(buffer, blen,
+                       "FRAME[WINDOW_UPDATE, incr=%d]",
+                       frame->window_update.window_size_increment);
+    }
+    default:
+      return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
+                       frame->hd.type, (int)frame->hd.length,
+                       frame->hd.flags);
+  }
+}
+
+static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
+                         void *userp)
+{
+  struct Curl_cfilter *cf = userp;
+  struct Curl_easy *data = CF_DATA_CURRENT(cf);
+
+  (void)session;
+  DEBUGASSERT(data);
+  if(data && Curl_trc_cf_is_verbose(cf, data)) {
+    char buffer[256];
+    int len;
+    len = fr_print(frame, buffer, sizeof(buffer)-1);
+    buffer[len] = 0;
+    CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
+  }
+  return 0;
+}
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp)
 {
@@ -1074,26 +1194,52 @@
   int32_t stream_id = frame->hd.stream_id;
 
   DEBUGASSERT(data);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  if(Curl_trc_cf_is_verbose(cf, data)) {
+    char buffer[256];
+    int len;
+    len = fr_print(frame, buffer, sizeof(buffer)-1);
+    buffer[len] = 0;
+    CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer);
+  }
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
   if(!stream_id) {
     /* stream ID zero is for connection-oriented stuff */
     DEBUGASSERT(data);
     switch(frame->hd.type) {
     case NGHTTP2_SETTINGS: {
-      uint32_t max_conn = ctx->max_concurrent_streams;
-      DEBUGF(LOG_CF(data, cf, "FRAME[SETTINGS]"));
-      ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
-          session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
-      ctx->enable_push = nghttp2_session_get_remote_settings(
-          session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
-      DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS == %d",
-                    ctx->max_concurrent_streams));
-      DEBUGF(LOG_CF(data, cf, "ENABLE_PUSH == %s",
-                    ctx->enable_push ? "TRUE" : "false"));
-      if(data && max_conn != ctx->max_concurrent_streams) {
-        /* only signal change if the value actually changed */
-        DEBUGF(LOG_CF(data, cf, "MAX_CONCURRENT_STREAMS now %u",
-                      ctx->max_concurrent_streams));
-        multi_connchanged(data->multi);
+      if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) {
+        uint32_t max_conn = ctx->max_concurrent_streams;
+        ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
+            session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
+        ctx->enable_push = nghttp2_session_get_remote_settings(
+            session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
+        CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d",
+                    ctx->max_concurrent_streams);
+        CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s",
+                    ctx->enable_push ? "TRUE" : "false");
+        if(data && max_conn != ctx->max_concurrent_streams) {
+          /* only signal change if the value actually changed */
+          CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u",
+                      ctx->max_concurrent_streams);
+          Curl_multi_connchanged(data->multi);
+        }
+        /* Since the initial stream window is 64K, a request might be on HOLD,
+         * due to exhaustion. The (initial) SETTINGS may announce a much larger
+         * window and *assume* that we treat this like a WINDOW_UPDATE. Some
+         * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
+         * To be safe, we UNHOLD a stream in order not to stall. */
+        if((data->req.keepon & KEEP_SEND_HOLD) &&
+           (data->req.keepon & KEEP_SEND)) {
+          struct stream_ctx *stream = H2_STREAM_CTX(data);
+          data->req.keepon &= ~KEEP_SEND_HOLD;
+          if(stream) {
+            drain_stream(cf, data, stream);
+            CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
+                        stream_id);
+          }
+        }
       }
       break;
     }
@@ -1102,26 +1248,20 @@
       ctx->goaway_error = frame->goaway.error_code;
       ctx->last_stream_id = frame->goaway.last_stream_id;
       if(data) {
-        DEBUGF(LOG_CF(data, cf, "FRAME[GOAWAY, error=%d, last_stream=%u]",
-                      ctx->goaway_error, ctx->last_stream_id));
         infof(data, "received GOAWAY, error=%d, last_stream=%u",
                     ctx->goaway_error, ctx->last_stream_id);
-        multi_connchanged(data->multi);
+        Curl_multi_connchanged(data->multi);
       }
       break;
-    case NGHTTP2_WINDOW_UPDATE:
-      DEBUGF(LOG_CF(data, cf, "FRAME[WINDOW_UPDATE]"));
-      break;
     default:
-      DEBUGF(LOG_CF(data, cf, "recv frame %x on 0", frame->hd.type));
+      break;
     }
     return 0;
   }
 
   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
   if(!data_s) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] No Curl_easy associated",
-                  stream_id));
+    CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id);
     return 0;
   }
 
@@ -1148,8 +1288,8 @@
     /* Receiving a Stream ID not in the hash should not happen - unless
        we have aborted a transfer artificially and there were more data
        in the pipeline. Silently ignore. */
-    DEBUGF(LOG_CF(CF_DATA_CURRENT(cf), cf, "[h2sid=%d] Data for unknown",
-                  stream_id));
+    CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown",
+                stream_id);
     /* consumed explicitly as no one will read it */
     nghttp2_session_consume(session, stream_id, len);
     return 0;
@@ -1191,8 +1331,6 @@
     return 0;
   }
   stream = H2_STREAM_CTX(data_s);
-  DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] on_stream_close(), %s (err %d)",
-                stream_id, nghttp2_http2_strerror(error_code), error_code));
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
@@ -1202,6 +1340,11 @@
     stream->reset = TRUE;
   data_s->req.keepon &= ~KEEP_SEND_HOLD;
 
+  if(stream->error)
+    CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
+              stream_id, nghttp2_http2_strerror(error_code), error_code);
+  else
+    CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
   drain_stream(cf, data_s, stream);
 
   /* remove `data_s` from the nghttp2 stream */
@@ -1211,7 +1354,6 @@
           stream_id);
     DEBUGASSERT(0);
   }
-  DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] closed now", stream_id));
   return 0;
 }
 
@@ -1228,8 +1370,6 @@
     return 0;
   }
 
-  DEBUGF(LOG_CF(data_s, cf, "on_begin_headers() was called"));
-
   if(frame->hd.type != NGHTTP2_HEADERS) {
     return 0;
   }
@@ -1335,10 +1475,8 @@
 
   if(stream->bodystarted) {
     /* This is a trailer */
-    DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] trailer: %.*s: %.*s",
-                  stream->id,
-                  (int)namelen, name,
-                  (int)valuelen, value));
+    CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
+                stream->id, (int)namelen, name, (int)valuelen, value);
     result = Curl_dynhds_add(&stream->resp_trailers,
                              (const char *)name, namelen,
                              (const char *)value, valuelen);
@@ -1375,8 +1513,8 @@
     if(CF_DATA_CURRENT(cf) != data_s)
       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
-    DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] status: HTTP/2 %03d",
-                  stream->id, stream->status_code));
+    CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
+                stream->id, stream->status_code);
     return 0;
   }
 
@@ -1399,10 +1537,8 @@
   if(CF_DATA_CURRENT(cf) != data_s)
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
-  DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] header: %.*s: %.*s",
-                stream->id,
-                (int)namelen, name,
-                (int)valuelen, value));
+  CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
+              stream->id, (int)namelen, name, (int)valuelen, value);
 
   return 0; /* 0 is successful */
 }
@@ -1448,9 +1584,9 @@
   if(nread > 0 && stream->upload_left != -1)
     stream->upload_left -= nread;
 
-  DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] req_body_read(len=%zu) left=%zd"
-                " -> %zd, %d",
-                stream_id, length, stream->upload_left, nread, result));
+  CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%"
+              CURL_FORMAT_CURL_OFF_T " -> %zd, %d",
+              stream_id, length, stream->upload_left, nread, result);
 
   if(stream->upload_left == 0)
     *data_flags = NGHTTP2_DATA_FLAG_EOF;
@@ -1523,7 +1659,7 @@
   if(!ctx || !ctx->h2 || !stream)
     goto out;
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%d] data done send", stream->id));
+  CURL_TRC_CF(data, cf, "[%d] data done send", stream->id);
   if(!stream->send_closed) {
     stream->send_closed = TRUE;
     if(stream->upload_left) {
@@ -1548,16 +1684,11 @@
   ssize_t rv = 0;
 
   if(stream->error == NGHTTP2_REFUSED_STREAM) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] REFUSED_STREAM, try again on a new "
-                  "connection", stream->id));
+    CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
+                "connection", stream->id);
     connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */
     data->state.refused_stream = TRUE;
-    *err = CURLE_SEND_ERROR; /* trigger Curl_retry_request() later */
-    return -1;
-  }
-  else if(stream->reset) {
-    failf(data, "HTTP/2 stream %u was reset", stream->id);
-    *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+    *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
     return -1;
   }
   else if(stream->error != NGHTTP2_NO_ERROR) {
@@ -1567,6 +1698,11 @@
     *err = CURLE_HTTP2_STREAM;
     return -1;
   }
+  else if(stream->reset) {
+    failf(data, "HTTP/2 stream %u was reset", stream->id);
+    *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+    return -1;
+  }
 
   if(!stream->bodystarted) {
     failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
@@ -1610,7 +1746,7 @@
   rv = 0;
 
 out:
-  DEBUGF(LOG_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err));
+  CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err);
   return rv;
 }
 
@@ -1659,15 +1795,15 @@
   struct stream_ctx *stream = H2_STREAM_CTX(data);
   int rv = 0;
 
-  if((sweight_wanted(data) != sweight_in_effect(data)) ||
-     (data->set.priority.exclusive != data->state.priority.exclusive) ||
-     (data->set.priority.parent != data->state.priority.parent) ) {
+  if(stream && stream->id > 0 &&
+     ((sweight_wanted(data) != sweight_in_effect(data)) ||
+      (data->set.priority.exclusive != data->state.priority.exclusive) ||
+      (data->set.priority.parent != data->state.priority.parent)) ) {
     /* send new weight and/or dependency */
     nghttp2_priority_spec pri_spec;
 
     h2_pri_spec(data, &pri_spec);
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] Queuing PRIORITY",
-                  stream->id));
+    CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
     DEBUGASSERT(stream->id != -1);
     rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
                                  stream->id, &pri_spec);
@@ -1675,31 +1811,30 @@
       goto out;
   }
 
-  while(!rv && nghttp2_session_want_write(ctx->h2))
+  ctx->nw_out_blocked = 0;
+  while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
     rv = nghttp2_session_send(ctx->h2);
 
 out:
   if(nghttp2_is_fatal(rv)) {
-    DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d",
-                  nghttp2_strerror(rv), rv));
+    CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d",
+                nghttp2_strerror(rv), rv);
     return CURLE_SEND_ERROR;
   }
   return nw_out_flush(cf, data);
 }
 
 static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           struct stream_ctx *stream,
                            char *buf, size_t len, CURLcode *err)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H2_STREAM_CTX(data);
   ssize_t nread = -1;
 
   *err = CURLE_AGAIN;
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "recvbuf read(len=%zu) -> %zd, %d",
-                  len, nread, *err));
     if(nread < 0)
       goto out;
     DEBUGASSERT(nread > 0);
@@ -1707,13 +1842,13 @@
 
   if(nread < 0) {
     if(stream->closed) {
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning CLOSE", stream->id));
+      CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
       nread = http2_handle_stream_close(cf, data, stream, err);
     }
     else if(stream->reset ||
             (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
             (ctx->goaway && ctx->last_stream_id < stream->id)) {
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] returning ERR", stream->id));
+      CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
       *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
       nread = -1;
     }
@@ -1724,8 +1859,9 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "stream_recv(len=%zu) -> %zd, %d",
-                len, nread, *err));
+  if(nread < 0 && *err != CURLE_AGAIN)
+    CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
+                stream->id, len, nread, *err);
   return nread;
 }
 
@@ -1739,8 +1875,8 @@
 
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
-    DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer",
-                  Curl_bufq_len(&ctx->inbufq)));
+    CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
+                Curl_bufq_len(&ctx->inbufq));
     if(h2_process_pending_input(cf, data, &result) < 0)
       return result;
   }
@@ -1760,8 +1896,6 @@
     }
 
     nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
-    /* DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d",
-                  Curl_bufq_len(&ctx->inbufq), nread, result)); */
     if(nread < 0) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
@@ -1771,9 +1905,14 @@
       break;
     }
     else if(nread == 0) {
+      CURL_TRC_CF(data, cf, "[0] ingress: connection closed");
       ctx->conn_closed = TRUE;
       break;
     }
+    else {
+      CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes",
+                  nread);
+    }
 
     if(h2_process_pending_input(cf, data, &result))
       return result;
@@ -1795,9 +1934,21 @@
   CURLcode result;
   struct cf_call_data save;
 
+  if(!stream) {
+    /* Abnormal call sequence: either this transfer has never opened a stream
+     * (unlikely) or the transfer has been done, cleaned up its resources, but
+     * a read() is called anyway. It is not clear what the calling sequence
+     * is for such a case. */
+    failf(data, "[%zd-%zd], http/2 recv on a transfer never opened "
+          "or already cleared", (ssize_t)data->id,
+          (ssize_t)cf->conn->connection_id);
+    *err = CURLE_HTTP2;
+    return -1;
+  }
+
   CF_DATA_SAVE(save, cf, data);
 
-  nread = stream_recv(cf, data, buf, len, err);
+  nread = stream_recv(cf, data, stream, buf, len, err);
   if(nread < 0 && *err != CURLE_AGAIN)
     goto out;
 
@@ -1806,7 +1957,7 @@
     if(*err)
       goto out;
 
-    nread = stream_recv(cf, data, buf, len, err);
+    nread = stream_recv(cf, data, stream, buf, len, err);
   }
 
   if(nread > 0) {
@@ -1828,28 +1979,34 @@
     }
 
     if(stream->closed) {
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] closed stream, set drain",
-                    stream->id));
+      CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
       drain_stream(cf, data, stream);
     }
   }
 
 out:
   result = h2_progress_egress(cf, data);
-  if(result) {
+  if(result == CURLE_AGAIN) {
+    /* pending data to send, need to be called again. Ideally, we'd
+     * monitor the socket for POLLOUT, but we might not be in SENDING
+     * transfer state any longer and are unable to make this happen.
+     */
+    drain_stream(cf, data, stream);
+  }
+  else if(result) {
     *err = result;
     nread = -1;
   }
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_recv(len=%zu) -> %zd %d, "
-                "buffered=%zu, window=%d/%d, connection %d/%d",
-                stream->id, len, nread, *err,
-                Curl_bufq_len(&stream->recvbuf),
-                nghttp2_session_get_stream_effective_recv_data_length(
-                  ctx->h2, stream->id),
-                nghttp2_session_get_stream_effective_local_window_size(
-                  ctx->h2, stream->id),
-                nghttp2_session_get_local_window_size(ctx->h2),
-                HTTP2_HUGE_WINDOW_SIZE));
+  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
+              "buffered=%zu, window=%d/%d, connection %d/%d",
+              stream->id, len, nread, *err,
+              Curl_bufq_len(&stream->recvbuf),
+              nghttp2_session_get_stream_effective_recv_data_length(
+                ctx->h2, stream->id),
+              nghttp2_session_get_stream_effective_local_window_size(
+                ctx->h2, stream->id),
+              nghttp2_session_get_local_window_size(ctx->h2),
+              HTTP2_HUGE_WINDOW_SIZE);
 
   CF_DATA_RESTORE(cf, save);
   return nread;
@@ -1861,16 +2018,15 @@
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = NULL;
-  struct h1_req_parser h1;
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
-  size_t nheader, i;
+  const void *body = NULL;
+  size_t nheader, bodylen, i;
   nghttp2_data_provider data_prd;
   int32_t stream_id;
   nghttp2_priority_spec pri_spec;
   ssize_t nwritten;
 
-  Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
 
   *err = http2_data_setup(cf, data, &stream);
@@ -1879,17 +2035,22 @@
     goto out;
   }
 
-  nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
   if(nwritten < 0)
     goto out;
-  DEBUGASSERT(h1.done);
-  DEBUGASSERT(h1.req);
+  if(!stream->h1.done) {
+    /* need more data */
+    goto out;
+  }
+  DEBUGASSERT(stream->h1.req);
 
-  *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
   if(*err) {
     nwritten = -1;
     goto out;
   }
+  /* no longer needed */
+  Curl_h1_req_parse_free(&stream->h1);
 
   nheader = Curl_dynhds_count(&h2_headers);
   nva = malloc(sizeof(nghttp2_nv) * nheader);
@@ -1908,29 +2069,9 @@
     nva[i].flags = NGHTTP2_NV_FLAG_NONE;
   }
 
-#define MAX_ACC 60000  /* <64KB to account for some overhead */
-  {
-    size_t acc = 0;
-
-    for(i = 0; i < nheader; ++i) {
-      acc += nva[i].namelen + nva[i].valuelen;
-
-      infof(data, "h2 [%.*s: %.*s]",
-            (int)nva[i].namelen, nva[i].name,
-            (int)nva[i].valuelen, nva[i].value);
-    }
-
-    if(acc > MAX_ACC) {
-      infof(data, "http_request: Warning: The cumulative length of all "
-            "headers exceeds %d bytes and that could cause the "
-            "stream to be rejected.", MAX_ACC);
-    }
-  }
-
   h2_pri_spec(data, &pri_spec);
-
-  DEBUGF(LOG_CF(data, cf, "send request allowed %d (easy handle %p)",
-                nghttp2_session_check_request_allowed(ctx->h2), (void *)data));
+  if(!nghttp2_session_check_request_allowed(ctx->h2))
+    CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
 
   switch(data->state.httpreq) {
   case HTTPREQ_POST:
@@ -1954,27 +2095,69 @@
                                        NULL, data);
   }
 
-  Curl_safefree(nva);
-
   if(stream_id < 0) {
-    DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
-                  nghttp2_strerror(stream_id), stream_id));
+    CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
+                nghttp2_strerror(stream_id), stream_id);
     *err = CURLE_SEND_ERROR;
     nwritten = -1;
     goto out;
   }
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) submit %s",
-                stream_id, len, data->state.url));
-  infof(data, "Using Stream ID: %u (easy handle %p)",
-        stream_id, (void *)data);
+#define MAX_ACC 60000  /* <64KB to account for some overhead */
+  if(Curl_trc_is_verbose(data)) {
+    size_t acc = 0;
+
+    infof(data, "[HTTP/2] [%d] OPENED stream for %s",
+          stream_id, data->state.url);
+    for(i = 0; i < nheader; ++i) {
+      acc += nva[i].namelen + nva[i].valuelen;
+
+      infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id,
+            (int)nva[i].namelen, nva[i].name,
+            (int)nva[i].valuelen, nva[i].value);
+    }
+
+    if(acc > MAX_ACC) {
+      infof(data, "[HTTP/2] Warning: The cumulative length of all "
+            "headers exceeds %d bytes and that could cause the "
+            "stream to be rejected.", MAX_ACC);
+    }
+  }
+
   stream->id = stream_id;
+  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
+  if(data->set.max_recv_speed) {
+    /* We are asked to only receive `max_recv_speed` bytes per second.
+     * Let's limit our stream window size around that, otherwise the server
+     * will send in large bursts only. We make the window 50% larger to
+     * allow for data in flight and avoid stalling. */
+    curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1);
+    n += CURLMAX((n/2), 1);
+    if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) &&
+       n < (UINT_MAX / H2_CHUNK_SIZE)) {
+      stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE;
+    }
+  }
+
+  body = (const char *)buf + nwritten;
+  bodylen = len - nwritten;
+
+  if(bodylen) {
+    /* We have request body to send in DATA frame */
+    ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err);
+    if(n < 0) {
+      *err = CURLE_SEND_ERROR;
+      nwritten = -1;
+      goto out;
+    }
+    nwritten += n;
+  }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%d] submit -> %zd, %d",
-         stream? stream->id : -1, nwritten, *err));
+  CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
+              stream? stream->id : -1, nwritten, *err);
+  Curl_safefree(nva);
   *pstream = stream;
-  Curl_h1_req_parse_free(&h1);
   Curl_dynhds_free(&h2_headers);
   return nwritten;
 }
@@ -1982,43 +2165,65 @@
 static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err)
 {
-  /*
-   * Currently, we send request in this function, but this function is also
-   * used to send request body. It would be nice to add dedicated function for
-   * request.
-   */
   struct cf_h2_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = H2_STREAM_CTX(data);
   struct cf_call_data save;
   int rv;
   ssize_t nwritten;
   CURLcode result;
+  int blocked = 0, was_blocked = 0;
 
   CF_DATA_SAVE(save, cf, data);
 
   if(stream && stream->id != -1) {
-    if(stream->close_handled) {
+    if(stream->upload_blocked_len) {
+      /* the data in `buf` has already been submitted or added to the
+       * buffers, but have been EAGAINed on the last invocation. */
+      /* TODO: this assertion triggers in OSSFuzz runs and it is not
+       * clear why. Disable for now to let OSSFuzz continue its tests. */
+      DEBUGASSERT(len >= stream->upload_blocked_len);
+      if(len < stream->upload_blocked_len) {
+        /* Did we get called again with a smaller `len`? This should not
+         * happen. We are not prepared to handle that. */
+        failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)",
+              len, stream->upload_blocked_len);
+        *err = CURLE_HTTP2;
+        nwritten = -1;
+        goto out;
+      }
+      nwritten = (ssize_t)stream->upload_blocked_len;
+      stream->upload_blocked_len = 0;
+      was_blocked = 1;
+    }
+    else if(stream->closed) {
+      if(stream->resp_hds_complete) {
+        /* Server decided to close the stream after having sent us a findl
+         * response. This is valid if it is not interested in the request
+         * body. This happens on 30x or 40x responses.
+         * We silently discard the data sent, since this is not a transport
+         * error situation. */
+        CURL_TRC_CF(data, cf, "[%d] discarding data"
+                    "on closed stream with response", stream->id);
+        *err = CURLE_OK;
+        nwritten = (ssize_t)len;
+        goto out;
+      }
       infof(data, "stream %u closed", stream->id);
-      *err = CURLE_HTTP2_STREAM;
+      *err = CURLE_SEND_ERROR;
       nwritten = -1;
       goto out;
     }
-    else if(stream->closed) {
-      nwritten = http2_handle_stream_close(cf, data, stream, err);
-      goto out;
-    }
-    /* If stream_id != -1, we have dispatched request HEADERS, and now
-       are going to send or sending request body in DATA frame */
-    nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
-    if(nwritten < 0) {
-      if(*err != CURLE_AGAIN)
+    else {
+      /* If stream_id != -1, we have dispatched request HEADERS and
+       * optionally request body, and now are going to send or sending
+       * more request body in DATA frame */
+      nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
+      if(nwritten < 0 && *err != CURLE_AGAIN)
         goto out;
-      nwritten = 0;
     }
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] bufq_write(len=%zu) -> %zd, %d",
-                  stream->id, len, nwritten, *err));
 
     if(!Curl_bufq_is_empty(&stream->sendbuf)) {
+      /* req body data is buffered, resume the potentially suspended stream */
       rv = nghttp2_session_resume_data(ctx->h2, stream->id);
       if(nghttp2_is_fatal(rv)) {
         *err = CURLE_SEND_ERROR;
@@ -2026,104 +2231,101 @@
         goto out;
       }
     }
-
-    result = h2_progress_ingress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
-    }
-
-    result = h2_progress_egress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
-    }
-
-    if(should_close_session(ctx)) {
-      if(stream->closed) {
-        nwritten = http2_handle_stream_close(cf, data, stream, err);
-      }
-      else {
-        DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
-        *err = CURLE_HTTP2;
-        nwritten = -1;
-      }
-      goto out;
-    }
-
-    if(!nwritten) {
-      size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
-                                                          stream->id);
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send: win %u/%zu",
-             stream->id,
-             nghttp2_session_get_remote_window_size(ctx->h2), rwin));
-      if(rwin == 0) {
-        /* We cannot upload more as the stream's remote window size
-         * is 0. We need to receive WIN_UPDATEs before we can continue.
-         */
-        data->req.keepon |= KEEP_SEND_HOLD;
-        DEBUGF(LOG_CF(data, cf, "[h2sid=%d] holding send as remote flow "
-               "window is exhausted", stream->id));
-      }
-      nwritten = -1;
-      *err = CURLE_AGAIN;
-    }
-    /* handled writing BODY for open stream. */
-    goto out;
   }
   else {
     nwritten = h2_submit(&stream, cf, data, buf, len, err);
     if(nwritten < 0) {
       goto out;
     }
+    DEBUGASSERT(stream);
+  }
 
-    result = h2_progress_ingress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
+  /* Call the nghttp2 send loop and flush to write ALL buffered data,
+   * headers and/or request body completely out to the network */
+  result = h2_progress_egress(cf, data);
+  /* if the stream has been closed in egress handling (nghttp2 does that
+   * when it does not like the headers, for example */
+  if(stream && stream->closed && !was_blocked) {
+    infof(data, "stream %u closed", stream->id);
+    *err = CURLE_SEND_ERROR;
+    nwritten = -1;
+    goto out;
+  }
+  else if(result == CURLE_AGAIN) {
+    blocked = 1;
+  }
+  else if(result) {
+    *err = result;
+    nwritten = -1;
+    goto out;
+  }
+  else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
+    /* although we wrote everything that nghttp2 wants to send now,
+     * there is data left in our stream send buffer unwritten. This may
+     * be due to the stream's HTTP/2 flow window being exhausted. */
+    blocked = 1;
+  }
+
+  if(stream && blocked && nwritten > 0) {
+    /* Unable to send all data, due to connection blocked or H2 window
+     * exhaustion. Data is left in our stream buffer, or nghttp2's internal
+     * frame buffer or our network out buffer. */
+    size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
+                                                                stream->id);
+    if(rwin == 0) {
+      /* H2 flow window exhaustion. We need to HOLD upload until we get
+       * a WINDOW_UPDATE from the server. */
+      data->req.keepon |= KEEP_SEND_HOLD;
+      CURL_TRC_CF(data, cf, "[%d] holding send as remote flow "
+                  "window is exhausted", stream->id);
     }
 
-    result = h2_progress_egress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
+    /* Whatever the cause, we need to return CURL_EAGAIN for this call.
+     * We have unwritten state that needs us being invoked again and EAGAIN
+     * is the only way to ensure that. */
+    stream->upload_blocked_len = nwritten;
+    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
+                "blocked_len=%zu",
+                stream->id, len,
+                nghttp2_session_get_remote_window_size(ctx->h2), rwin,
+                nwritten);
+    *err = CURLE_AGAIN;
+    nwritten = -1;
+    goto out;
+  }
+  else if(should_close_session(ctx)) {
+    /* nghttp2 thinks this session is done. If the stream has not been
+     * closed, this is an error state for out transfer */
+    if(stream->closed) {
+      nwritten = http2_handle_stream_close(cf, data, stream, err);
     }
-
-    if(should_close_session(ctx)) {
-      if(stream->closed) {
-        nwritten = http2_handle_stream_close(cf, data, stream, err);
-      }
-      else {
-        DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
-        *err = CURLE_HTTP2;
-        nwritten = -1;
-      }
-      goto out;
+    else {
+      CURL_TRC_CF(data, cf, "send: nothing to do in this session");
+      *err = CURLE_HTTP2;
+      nwritten = -1;
     }
   }
 
 out:
   if(stream) {
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, "
-                  "buffered=%zu, upload_left=%zu, stream-window=%d, "
-                  "connection-window=%d",
-                  stream->id, len, nwritten, *err,
-                  Curl_bufq_len(&stream->sendbuf),
-                  (ssize_t)stream->upload_left,
-                  nghttp2_session_get_stream_remote_window_size(
-                    ctx->h2, stream->id),
-                  nghttp2_session_get_remote_window_size(ctx->h2)));
-    drain_stream(cf, data, stream);
+    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
+                "upload_left=%" CURL_FORMAT_CURL_OFF_T ", "
+                "h2 windows %d-%d (stream-conn), "
+                "buffers %zu-%zu (stream-conn)",
+                stream->id, len, nwritten, *err,
+                stream->upload_left,
+                nghttp2_session_get_stream_remote_window_size(
+                  ctx->h2, stream->id),
+                nghttp2_session_get_remote_window_size(ctx->h2),
+                Curl_bufq_len(&stream->sendbuf),
+                Curl_bufq_len(&ctx->outbufq));
   }
   else {
-    DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
-                  "connection-window=%d",
-                  len, nwritten, *err,
-                  nghttp2_session_get_remote_window_size(ctx->h2)));
+    CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
+                "connection-window=%d, nw_send_buffer(%zu)",
+                len, nwritten, *err,
+                nghttp2_session_get_remote_window_size(ctx->h2),
+                Curl_bufq_len(&ctx->outbufq));
   }
   CF_DATA_RESTORE(cf, save);
   return nwritten;
@@ -2194,8 +2396,12 @@
   if(result)
     goto out;
 
+  /* Send out our SETTINGS and ACKs and such. If that blocks, we
+   * have it buffered and  can count this filter as being connected */
   result = h2_progress_egress(cf, data);
-  if(result)
+  if(result == CURLE_AGAIN)
+    result = CURLE_OK;
+  else if(result)
     goto out;
 
   *done = TRUE;
@@ -2203,6 +2409,7 @@
   result = CURLE_OK;
 
 out:
+  CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
   CF_DATA_RESTORE(cf, save);
   return result;
 }
@@ -2218,6 +2425,8 @@
     cf_h2_ctx_clear(ctx);
     CF_DATA_RESTORE(cf, save);
   }
+  if(cf->next)
+    cf->next->cft->do_close(cf->next, data);
 }
 
 static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -2241,8 +2450,7 @@
 
   DEBUGASSERT(data);
   if(ctx && ctx->h2 && stream) {
-    uint32_t window = !pause * H2_STREAM_WINDOW_SIZE;
-    CURLcode result;
+    uint32_t window = pause? 0 : stream->local_window_size;
 
     int rv = nghttp2_session_set_local_window_size(ctx->h2,
                                                    NGHTTP2_FLAG_NONE,
@@ -2257,10 +2465,8 @@
     if(!pause)
       drain_stream(cf, data, stream);
 
-    /* make sure the window update gets sent */
-    result = h2_progress_egress(cf, data);
-    if(result)
-      return result;
+    /* attempt to send the window update */
+    (void)h2_progress_egress(cf, data);
 
     if(!pause) {
       /* Unpausing a h2 transfer, requires it to be run again. The server
@@ -2343,8 +2549,8 @@
 
   CF_DATA_SAVE(save, cf, data);
   result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
-  DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d",
-         result, *input_pending));
+  CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
+              result, *input_pending);
   CF_DATA_RESTORE(cf, save);
   return result;
 }
@@ -2395,7 +2601,7 @@
 struct Curl_cftype Curl_cft_nghttp2 = {
   "HTTP/2",
   CF_TYPE_MULTIPLEX,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   cf_h2_destroy,
   cf_h2_connect,
   cf_h2_close,
@@ -2510,7 +2716,7 @@
   CURLcode result;
 
   DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
-  DEBUGF(infof(data, DMSGI(data, sockindex, "switching to HTTP/2")));
+  DEBUGF(infof(data, "switching to HTTP/2"));
 
   result = http2_cfilter_add(&cf, data, conn, sockindex);
   if(result)
@@ -2523,7 +2729,7 @@
   conn->httpversion = 20; /* we know we're on HTTP/2 now */
   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-  multi_connchanged(data->multi);
+  Curl_multi_connchanged(data->multi);
 
   if(cf->next) {
     bool done;
@@ -2551,7 +2757,7 @@
   cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-  multi_connchanged(data->multi);
+  Curl_multi_connchanged(data->multi);
 
   if(cf_h2->next) {
     bool done;
@@ -2569,7 +2775,7 @@
   CURLcode result;
 
   DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
-  DEBUGF(infof(data, DMSGI(data, sockindex, "upgrading to HTTP/2")));
+  DEBUGF(infof(data, "upgrading to HTTP/2"));
   DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
 
   result = http2_cfilter_add(&cf, data, conn, sockindex);
@@ -2608,7 +2814,7 @@
   conn->httpversion = 20; /* we know we're on HTTP/2 now */
   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
-  multi_connchanged(data->multi);
+  Curl_multi_connchanged(data->multi);
 
   if(cf->next) {
     bool done;
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index 8060162..901c22f 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS)
 
 #include "urldata.h"
 #include "strcase.h"
@@ -34,6 +34,7 @@
 #include "transfer.h"
 #include "parsedate.h"
 #include "sendf.h"
+#include "escape.h"
 
 #include <time.h>
 
@@ -44,16 +45,16 @@
 
 #include "slist.h"
 
-#define HMAC_SHA256(k, kl, d, dl, o)        \
-  do {                                      \
-    ret = Curl_hmacit(Curl_HMAC_SHA256,     \
-                      (unsigned char *)k,   \
-                      kl,                   \
-                      (unsigned char *)d,   \
-                      dl, o);               \
-    if(ret) {                               \
-      goto fail;                            \
-    }                                       \
+#define HMAC_SHA256(k, kl, d, dl, o)           \
+  do {                                         \
+    result = Curl_hmacit(Curl_HMAC_SHA256,     \
+                         (unsigned char *)k,   \
+                         kl,                   \
+                         (unsigned char *)d,   \
+                         dl, o);               \
+    if(result) {                               \
+      goto fail;                               \
+    }                                          \
   } while(0)
 
 #define TIMESTAMP_SIZE 17
@@ -63,11 +64,8 @@
 
 static void sha256_to_hex(char *dst, unsigned char *sha)
 {
-  int i;
-
-  for(i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
-    msnprintf(dst + (i * 2), SHA256_HEX_LENGTH - (i * 2), "%02x", sha[i]);
-  }
+  Curl_hexencode(sha, SHA256_DIGEST_LENGTH,
+                 (unsigned char *)dst, SHA256_HEX_LENGTH);
 }
 
 static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
@@ -199,10 +197,41 @@
     head = tmp_head;
   }
 
+  /* copy user headers to our header list. the logic is based on how http.c
+     handles user headers.
+
+     user headers in format 'name:' with no value are used to signal that an
+     internal header of that name should be removed. those user headers are not
+     added to this list.
+
+     user headers in format 'name;' with no value are used to signal that a
+     header of that name with no value should be sent. those user headers are
+     added to this list but in the format that they will be sent, ie the
+     semi-colon is changed to a colon for format 'name:'.
+
+     user headers with a value of whitespace only, or without a colon or
+     semi-colon, are not added to this list.
+     */
   for(l = data->set.headers; l; l = l->next) {
-    tmp_head = curl_slist_append(head, l->data);
-    if(!tmp_head)
+    char *dupdata, *ptr;
+    char *sep = strchr(l->data, ':');
+    if(!sep)
+      sep = strchr(l->data, ';');
+    if(!sep || (*sep == ':' && !*(sep + 1)))
+      continue;
+    for(ptr = sep + 1; ISSPACE(*ptr); ++ptr)
+      ;
+    if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
+      continue;
+    dupdata = strdup(l->data);
+    if(!dupdata)
       goto fail;
+    dupdata[sep - l->data] = ':';
+    tmp_head = Curl_slist_append_nodup(head, dupdata);
+    if(!tmp_head) {
+      free(dupdata);
+      goto fail;
+    }
     head = tmp_head;
   }
 
@@ -214,23 +243,22 @@
     if(!tmp_head)
       goto fail;
     head = tmp_head;
-    *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
+    *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp);
   }
   else {
     char *value;
 
-    *date_header = strdup(*date_header);
-    if(!*date_header)
-      goto fail;
-
     value = strchr(*date_header, ':');
-    if(!value)
+    if(!value) {
+      *date_header = NULL;
       goto fail;
+    }
     ++value;
     while(ISBLANK(*value))
       ++value;
     strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
     timestamp[TIMESTAMP_SIZE - 1] = 0;
+    *date_header = NULL;
   }
 
   /* alpha-sort in a case sensitive manner */
@@ -370,9 +398,117 @@
   return ret;
 }
 
+struct pair {
+  const char *p;
+  size_t len;
+};
+
+static int compare_func(const void *a, const void *b)
+{
+  const struct pair *aa = a;
+  const struct pair *bb = b;
+  /* If one element is empty, the other is always sorted higher */
+  if(aa->len == 0)
+    return -1;
+  if(bb->len == 0)
+    return 1;
+  return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
+}
+
+#define MAX_QUERYPAIRS 64
+
+static CURLcode canon_query(struct Curl_easy *data,
+                            const char *query, struct dynbuf *dq)
+{
+  CURLcode result = CURLE_OK;
+  int entry = 0;
+  int i;
+  const char *p = query;
+  struct pair array[MAX_QUERYPAIRS];
+  struct pair *ap = &array[0];
+  if(!query)
+    return result;
+
+  /* sort the name=value pairs first */
+  do {
+    char *amp;
+    entry++;
+    ap->p = p;
+    amp = strchr(p, '&');
+    if(amp)
+      ap->len = amp - p; /* excluding the ampersand */
+    else {
+      ap->len = strlen(p);
+      break;
+    }
+    ap++;
+    p = amp + 1;
+  } while(entry < MAX_QUERYPAIRS);
+  if(entry == MAX_QUERYPAIRS) {
+    /* too many query pairs for us */
+    failf(data, "aws-sigv4: too many query pairs in URL");
+    return CURLE_URL_MALFORMAT;
+  }
+
+  qsort(&array[0], entry, sizeof(struct pair), compare_func);
+
+  ap = &array[0];
+  for(i = 0; !result && (i < entry); i++, ap++) {
+    size_t len;
+    const char *q = ap->p;
+    if(!ap->len)
+      continue;
+    for(len = ap->len; len && !result; q++, len--) {
+      if(ISALNUM(*q))
+        result = Curl_dyn_addn(dq, q, 1);
+      else {
+        switch(*q) {
+        case '-':
+        case '.':
+        case '_':
+        case '~':
+        case '=':
+          /* allowed as-is */
+          result = Curl_dyn_addn(dq, q, 1);
+          break;
+        case '%':
+          /* uppercase the following if hexadecimal */
+          if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) {
+            char tmp[3]="%";
+            tmp[1] = Curl_raw_toupper(q[1]);
+            tmp[2] = Curl_raw_toupper(q[2]);
+            result = Curl_dyn_addn(dq, tmp, 3);
+            q += 2;
+            len -= 2;
+          }
+          else
+            /* '%' without a following two-digit hex, encode it */
+            result = Curl_dyn_addn(dq, "%25", 3);
+          break;
+        default: {
+          /* URL encode */
+          const char hex[] = "0123456789ABCDEF";
+          char out[3]={'%'};
+          out[1] = hex[((unsigned char)*q)>>4];
+          out[2] = hex[*q & 0xf];
+          result = Curl_dyn_addn(dq, out, 3);
+          break;
+        }
+        }
+      }
+    }
+    if(i < entry - 1) {
+      /* insert ampersands between query pairs */
+      result = Curl_dyn_addn(dq, "&", 1);
+    }
+  }
+  return result;
+}
+
+
 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
 {
-  CURLcode ret = CURLE_OUT_OF_MEMORY;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
   struct connectdata *conn = data->conn;
   size_t len;
   const char *arg;
@@ -388,6 +524,7 @@
   char date[9];
   struct dynbuf canonical_headers;
   struct dynbuf signed_headers;
+  struct dynbuf canonical_query;
   char *date_header = NULL;
   Curl_HttpReq httpreq;
   const char *method = NULL;
@@ -416,6 +553,7 @@
 
   /* we init those buffers here, so goto fail will free initialized dynbuf */
   Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+  Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
   Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
 
   /*
@@ -431,15 +569,15 @@
   /* provider1[:provider2[:region[:service]]]
 
      No string can be longer than N bytes of non-whitespace
-   */
+  */
   (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
                ":%" MAX_SIGV4_LEN_TXT "[^:]"
                ":%" MAX_SIGV4_LEN_TXT "[^:]"
                ":%" MAX_SIGV4_LEN_TXT "s",
                provider0, provider1, region, service);
   if(!provider0[0]) {
-    failf(data, "first provider can't be empty");
-    ret = CURLE_BAD_FUNCTION_ARGUMENT;
+    failf(data, "first aws-sigv4 provider can't be empty");
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
     goto fail;
   }
   else if(!provider1[0])
@@ -448,35 +586,38 @@
   if(!service[0]) {
     char *hostdot = strchr(hostname, '.');
     if(!hostdot) {
-      failf(data, "service missing in parameters and hostname");
-      ret = CURLE_URL_MALFORMAT;
+      failf(data, "aws-sigv4: service missing in parameters and hostname");
+      result = CURLE_URL_MALFORMAT;
       goto fail;
     }
     len = hostdot - hostname;
     if(len > MAX_SIGV4_LEN) {
-      failf(data, "service too long in hostname");
-      ret = CURLE_URL_MALFORMAT;
+      failf(data, "aws-sigv4: service too long in hostname");
+      result = CURLE_URL_MALFORMAT;
       goto fail;
     }
     strncpy(service, hostname, len);
     service[len] = '\0';
 
+    infof(data, "aws_sigv4: picked service %s from host", service);
+
     if(!region[0]) {
       const char *reg = hostdot + 1;
       const char *hostreg = strchr(reg, '.');
       if(!hostreg) {
-        failf(data, "region missing in parameters and hostname");
-        ret = CURLE_URL_MALFORMAT;
+        failf(data, "aws-sigv4: region missing in parameters and hostname");
+        result = CURLE_URL_MALFORMAT;
         goto fail;
       }
       len = hostreg - reg;
       if(len > MAX_SIGV4_LEN) {
-        failf(data, "region too long in hostname");
-        ret = CURLE_URL_MALFORMAT;
+        failf(data, "aws-sigv4: region too long in hostname");
+        result = CURLE_URL_MALFORMAT;
         goto fail;
       }
       strncpy(region, reg, len);
       region[len] = '\0';
+      infof(data, "aws_sigv4: picked region %s from host", region);
     }
   }
 
@@ -491,11 +632,11 @@
 
   if(!payload_hash) {
     if(sign_as_s3)
-      ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
-                                 sha_hex, content_sha256_hdr);
+      result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
+                                    sha_hex, content_sha256_hdr);
     else
-      ret = calc_payload_hash(data, sha_hash, sha_hex);
-    if(ret)
+      result = calc_payload_hash(data, sha_hash, sha_hex);
+    if(result)
       goto fail;
 
     payload_hash = sha_hex;
@@ -514,21 +655,20 @@
 #else
   time(&clock);
 #endif
-  ret = Curl_gmtime(clock, &tm);
-  if(ret) {
+  result = Curl_gmtime(clock, &tm);
+  if(result) {
     goto fail;
   }
   if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
-    ret = CURLE_OUT_OF_MEMORY;
+    result = CURLE_OUT_OF_MEMORY;
     goto fail;
   }
 
-  ret = make_headers(data, hostname, timestamp, provider1,
-                     &date_header, content_sha256_hdr,
-                     &canonical_headers, &signed_headers);
-  if(ret)
+  result = make_headers(data, hostname, timestamp, provider1,
+                        &date_header, content_sha256_hdr,
+                        &canonical_headers, &signed_headers);
+  if(result)
     goto fail;
-  ret = CURLE_OUT_OF_MEMORY;
 
   if(*content_sha256_hdr) {
     /* make_headers() needed this without the \r\n for canonicalization */
@@ -540,6 +680,11 @@
   memcpy(date, timestamp, sizeof(date));
   date[sizeof(date) - 1] = 0;
 
+  result = canon_query(data, data->state.up.query, &canonical_query);
+  if(result)
+    goto fail;
+  result = CURLE_OUT_OF_MEMORY;
+
   canonical_request =
     curl_maprintf("%s\n" /* HTTPRequestMethod */
                   "%s\n" /* CanonicalURI */
@@ -549,13 +694,16 @@
                   "%.*s",  /* HashedRequestPayload in hex */
                   method,
                   data->state.up.path,
-                  data->state.up.query ? data->state.up.query : "",
+                  Curl_dyn_ptr(&canonical_query) ?
+                  Curl_dyn_ptr(&canonical_query) : "",
                   Curl_dyn_ptr(&canonical_headers),
                   Curl_dyn_ptr(&signed_headers),
                   (int)payload_hash_len, payload_hash);
   if(!canonical_request)
     goto fail;
 
+  DEBUGF(infof(data, "Canonical request: %s", canonical_request));
+
   /* provider 0 lowercase */
   Curl_strntolower(provider0, provider0, strlen(provider0));
   request_type = curl_maprintf("%s4_request", provider0);
@@ -612,14 +760,19 @@
                                "Credential=%s/%s, "
                                "SignedHeaders=%s, "
                                "Signature=%s\r\n"
-                               "%s\r\n"
+                               /*
+                                * date_header is added here, only if it wasn't
+                                * user-specified (using CURLOPT_HTTPHEADER).
+                                * date_header includes \r\n
+                                */
+                               "%s"
                                "%s", /* optional sha256 header includes \r\n */
                                provider0,
                                user,
                                credential_scope,
                                Curl_dyn_ptr(&signed_headers),
                                sha_hex,
-                               date_header,
+                               date_header ? date_header : "",
                                content_sha256_hdr);
   if(!auth_headers) {
     goto fail;
@@ -628,9 +781,10 @@
   Curl_safefree(data->state.aptr.userpwd);
   data->state.aptr.userpwd = auth_headers;
   data->state.authhost.done = TRUE;
-  ret = CURLE_OK;
+  result = CURLE_OK;
 
 fail:
+  Curl_dyn_free(&canonical_query);
   Curl_dyn_free(&canonical_headers);
   Curl_dyn_free(&signed_headers);
   free(canonical_request);
@@ -639,7 +793,7 @@
   free(str_to_sign);
   free(secret);
   free(date_header);
-  return ret;
+  return result;
 }
 
-#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */
diff --git a/Utilities/cmcurl/lib/http_chunks.c b/Utilities/cmcurl/lib/http_chunks.c
index bda00d3..2a401d1 100644
--- a/Utilities/cmcurl/lib/http_chunks.c
+++ b/Utilities/cmcurl/lib/http_chunks.c
@@ -175,10 +175,7 @@
 
       /* Write the data portion available */
       if(!data->set.http_te_skip && !k->ignorebody) {
-        if(!data->set.http_ce_skip && k->writer_stack)
-          result = Curl_unencode_write(data, k->writer_stack, datap, piece);
-        else
-          result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece);
+        result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece);
 
         if(result) {
           *extrap = result;
diff --git a/Utilities/cmcurl/lib/http_digest.c b/Utilities/cmcurl/lib/http_digest.c
index 8daad99..2db3125 100644
--- a/Utilities/cmcurl/lib/http_digest.c
+++ b/Utilities/cmcurl/lib/http_digest.c
@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include "urldata.h"
 #include "strcase.h"
diff --git a/Utilities/cmcurl/lib/http_digest.h b/Utilities/cmcurl/lib/http_digest.h
index 7d5cfc1..5f79731 100644
--- a/Utilities/cmcurl/lib/http_digest.h
+++ b/Utilities/cmcurl/lib/http_digest.h
@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
 
 /* this is for digest header input */
 CURLcode Curl_input_digest(struct Curl_easy *data,
@@ -39,6 +39,6 @@
 
 void Curl_http_auth_cleanup_digest(struct Curl_easy *data);
 
-#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_CRYPTO_AUTH */
+#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_DIGEST_AUTH */
 
 #endif /* HEADER_CURL_HTTP_DIGEST_H */
diff --git a/Utilities/cmcurl/lib/http_proxy.c b/Utilities/cmcurl/lib/http_proxy.c
index add376b..a1d6da9 100644
--- a/Utilities/cmcurl/lib/http_proxy.c
+++ b/Utilities/cmcurl/lib/http_proxy.c
@@ -52,6 +52,113 @@
 #include "memdebug.h"
 
 
+CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
+                                         const char **phostname,
+                                         int *pport, bool *pipv6_ip)
+{
+  DEBUGASSERT(cf);
+  DEBUGASSERT(cf->conn);
+
+  if(cf->conn->bits.conn_to_host)
+    *phostname = cf->conn->conn_to_host.name;
+  else if(cf->sockindex == SECONDARYSOCKET)
+    *phostname = cf->conn->secondaryhostname;
+  else
+    *phostname = cf->conn->host.name;
+
+  if(cf->sockindex == SECONDARYSOCKET)
+    *pport = cf->conn->secondary_port;
+  else if(cf->conn->bits.conn_to_port)
+    *pport = cf->conn->conn_to_port;
+  else
+    *pport = cf->conn->remote_port;
+
+  if(*phostname != cf->conn->host.name)
+    *pipv6_ip = (strchr(*phostname, ':') != NULL);
+  else
+    *pipv6_ip = cf->conn->bits.ipv6_ip;
+
+  return CURLE_OK;
+}
+
+CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
+                                        struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        int http_version_major)
+{
+  const char *hostname = NULL;
+  char *authority = NULL;
+  int port;
+  bool ipv6_ip;
+  CURLcode result;
+  struct httpreq *req = NULL;
+
+  result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
+  if(result)
+    goto out;
+
+  authority = aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname,
+                      ipv6_ip?"]":"", port);
+  if(!authority) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+
+  result = Curl_http_req_make(&req, "CONNECT", sizeof("CONNECT")-1,
+                              NULL, 0, authority, strlen(authority),
+                              NULL, 0);
+  if(result)
+    goto out;
+
+  /* Setup the proxy-authorization header, if any */
+  result = Curl_http_output_auth(data, cf->conn, req->method, HTTPREQ_GET,
+                                 req->authority, TRUE);
+  if(result)
+    goto out;
+
+  /* If user is not overriding Host: header, we add for HTTP/1.x */
+  if(http_version_major == 1 &&
+     !Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
+    result = Curl_dynhds_cadd(&req->headers, "Host", authority);
+    if(result)
+      goto out;
+  }
+
+  if(data->state.aptr.proxyuserpwd) {
+    result = Curl_dynhds_h1_cadd_line(&req->headers,
+                                      data->state.aptr.proxyuserpwd);
+    if(result)
+      goto out;
+  }
+
+  if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("User-Agent"))
+     && data->set.str[STRING_USERAGENT]) {
+    result = Curl_dynhds_cadd(&req->headers, "User-Agent",
+                              data->set.str[STRING_USERAGENT]);
+    if(result)
+      goto out;
+  }
+
+  if(http_version_major == 1 &&
+    !Curl_checkProxyheaders(data, cf->conn, STRCONST("Proxy-Connection"))) {
+    result = Curl_dynhds_cadd(&req->headers, "Proxy-Connection", "Keep-Alive");
+    if(result)
+      goto out;
+  }
+
+  result = Curl_dynhds_add_custom(data, TRUE, &req->headers);
+
+out:
+  if(result && req) {
+    Curl_http_req_free(req);
+    req = NULL;
+  }
+  free(authority);
+  *preq = req;
+  return result;
+}
+
+
 struct cf_proxy_ctx {
   /* the protocol specific sub-filter we install during connect */
   struct Curl_cfilter *cf_protocol;
@@ -69,9 +176,9 @@
     return CURLE_OK;
   }
 
-  DEBUGF(LOG_CF(data, cf, "connect"));
+  CURL_TRC_CF(data, cf, "connect");
 connect_sub:
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -86,7 +193,7 @@
     case CURL_HTTP_VERSION_NONE:
     case CURL_HTTP_VERSION_1_0:
     case CURL_HTTP_VERSION_1_1:
-      DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/1.1"));
+      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
       infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
             (alpn == CURL_HTTP_VERSION_1_0)? 0 : 1);
       result = Curl_cf_h1_proxy_insert_after(cf, data);
@@ -96,7 +203,7 @@
       break;
 #ifdef USE_NGHTTP2
     case CURL_HTTP_VERSION_2:
-      DEBUGF(LOG_CF(data, cf, "installing subfilter for HTTP/2"));
+      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
       infof(data, "CONNECT tunnel: HTTP/2 negotiated");
       result = Curl_cf_h2_proxy_insert_after(cf, data);
       if(result)
@@ -105,7 +212,6 @@
       break;
 #endif
     default:
-      DEBUGF(LOG_CF(data, cf, "installing subfilter for default HTTP/1.1"));
       infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
       result = CURLE_COULDNT_CONNECT;
       goto out;
@@ -156,7 +262,7 @@
   struct cf_proxy_ctx *ctx = cf->ctx;
 
   (void)data;
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   free(ctx);
 }
 
@@ -165,7 +271,7 @@
 {
   struct cf_proxy_ctx *ctx = cf->ctx;
 
-  DEBUGF(LOG_CF(data, cf, "close"));
+  CURL_TRC_CF(data, cf, "close");
   cf->connected = FALSE;
   if(ctx->cf_protocol) {
     struct Curl_cfilter *f;
@@ -181,7 +287,7 @@
     ctx->cf_protocol = NULL;
   }
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 
 
diff --git a/Utilities/cmcurl/lib/http_proxy.h b/Utilities/cmcurl/lib/http_proxy.h
index a1a0372..2b5f7ae 100644
--- a/Utilities/cmcurl/lib/http_proxy.h
+++ b/Utilities/cmcurl/lib/http_proxy.h
@@ -30,6 +30,15 @@
 
 #include "urldata.h"
 
+CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
+                                         const char **phostname,
+                                         int *pport, bool *pipv6_ip);
+
+CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
+                                        struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        int http_version_major);
+
 /* Default proxy timeout in milliseconds */
 #define PROXY_TIMEOUT (3600*1000)
 
diff --git a/Utilities/cmcurl/lib/idn.c b/Utilities/cmcurl/lib/idn.c
index 5f4b07e..a024691 100644
--- a/Utilities/cmcurl/lib/idn.c
+++ b/Utilities/cmcurl/lib/idn.c
@@ -68,27 +68,61 @@
 
 #define IDN_MAX_LENGTH 255
 
-bool Curl_win32_idn_to_ascii(const char *in, char **out)
+static CURLcode win32_idn_to_ascii(const char *in, char **out)
 {
-  bool success = FALSE;
-
   wchar_t *in_w = curlx_convert_UTF8_to_wchar(in);
+  *out = NULL;
   if(in_w) {
     wchar_t punycode[IDN_MAX_LENGTH];
-    int chars = IdnToAscii(0, in_w, -1, punycode, IDN_MAX_LENGTH);
+    int chars = IdnToAscii(0, in_w, (int)(wcslen(in_w) + 1), punycode,
+                           IDN_MAX_LENGTH);
     curlx_unicodefree(in_w);
     if(chars) {
       char *mstr = curlx_convert_wchar_to_UTF8(punycode);
       if(mstr) {
         *out = strdup(mstr);
         curlx_unicodefree(mstr);
-        if(*out)
-          success = TRUE;
+        if(!*out)
+          return CURLE_OUT_OF_MEMORY;
+      }
+      else
+        return CURLE_OUT_OF_MEMORY;
+    }
+    else
+      return CURLE_URL_MALFORMAT;
+  }
+  else
+    return CURLE_URL_MALFORMAT;
+
+  return CURLE_OK;
+}
+
+static CURLcode win32_ascii_to_idn(const char *in, char **output)
+{
+  char *out = NULL;
+
+  wchar_t *in_w = curlx_convert_UTF8_to_wchar(in);
+  if(in_w) {
+    WCHAR idn[IDN_MAX_LENGTH]; /* stores a UTF-16 string */
+    int chars = IdnToUnicode(0, in_w, (int)(wcslen(in_w) + 1), idn,
+                             IDN_MAX_LENGTH);
+    if(chars) {
+      /* 'chars' is "the number of characters retrieved" */
+      char *mstr = curlx_convert_wchar_to_UTF8(idn);
+      if(mstr) {
+        out = strdup(mstr);
+        curlx_unicodefree(mstr);
+        if(!out)
+          return CURLE_OUT_OF_MEMORY;
       }
     }
+    else
+      return CURLE_URL_MALFORMAT;
   }
-
-  return success;
+  else
+    return CURLE_URL_MALFORMAT;
+  *output = out;
+  return CURLE_OK;
 }
 
 #endif /* USE_WIN32_IDN */
@@ -115,10 +149,15 @@
 /*
  * Curl_idn_decode() returns an allocated IDN decoded string if it was
  * possible. NULL on error.
+ *
+ * CURLE_URL_MALFORMAT - the host name could not be converted
+ * CURLE_OUT_OF_MEMORY - memory problem
+ *
  */
-static char *idn_decode(const char *input)
+static CURLcode idn_decode(const char *input, char **output)
 {
   char *decoded = NULL;
+  CURLcode result = CURLE_OK;
 #ifdef USE_LIBIDN2
   if(idn2_check_version(IDN2_VERSION)) {
     int flags = IDN2_NFC_INPUT
@@ -135,26 +174,71 @@
          compatibility */
       rc = IDN2_LOOKUP(input, &decoded, IDN2_TRANSITIONAL);
     if(rc != IDN2_OK)
-      decoded = NULL;
+      result = CURLE_URL_MALFORMAT;
   }
+  else
+    /* a too old libidn2 version */
+    result = CURLE_NOT_BUILT_IN;
 #elif defined(USE_WIN32_IDN)
-  if(!Curl_win32_idn_to_ascii(input, &decoded))
-    decoded = NULL;
+  result = win32_idn_to_ascii(input, &decoded);
 #endif
-  return decoded;
+  if(!result)
+    *output = decoded;
+  return result;
 }
 
-char *Curl_idn_decode(const char *input)
+static CURLcode idn_encode(const char *puny, char **output)
 {
-  char *d = idn_decode(input);
+  char *enc = NULL;
 #ifdef USE_LIBIDN2
-  if(d) {
+  int rc = idn2_to_unicode_8z8z(puny, &enc, 0);
+  if(rc != IDNA_SUCCESS)
+    return rc == IDNA_MALLOC_ERROR ? CURLE_OUT_OF_MEMORY : CURLE_URL_MALFORMAT;
+#elif defined(USE_WIN32_IDN)
+  CURLcode result = win32_ascii_to_idn(puny, &enc);
+  if(result)
+    return result;
+#endif
+  *output = enc;
+  return CURLE_OK;
+}
+
+CURLcode Curl_idn_decode(const char *input, char **output)
+{
+  char *d = NULL;
+  CURLcode result = idn_decode(input, &d);
+#ifdef USE_LIBIDN2
+  if(!result) {
     char *c = strdup(d);
     idn2_free(d);
-    d = c;
+    if(c)
+      d = c;
+    else
+      result = CURLE_OUT_OF_MEMORY;
   }
 #endif
-  return d;
+  if(!result)
+    *output = d;
+  return result;
+}
+
+CURLcode Curl_idn_encode(const char *puny, char **output)
+{
+  char *d = NULL;
+  CURLcode result = idn_encode(puny, &d);
+#ifdef USE_LIBIDN2
+  if(!result) {
+    char *c = strdup(d);
+    idn2_free(d);
+    if(c)
+      d = c;
+    else
+      result = CURLE_OUT_OF_MEMORY;
+  }
+#endif
+  if(!result)
+    *output = d;
+  return result;
 }
 
 /*
@@ -182,8 +266,9 @@
 #ifdef USE_IDN
   /* Check name for non-ASCII and convert hostname if we can */
   if(!Curl_is_ASCII_name(host->name)) {
-    char *decoded = idn_decode(host->name);
-    if(decoded) {
+    char *decoded;
+    CURLcode result = idn_decode(host->name, &decoded);
+    if(!result) {
       if(!*decoded) {
         /* zero length is a bad host name */
         Curl_idn_free(decoded);
@@ -195,7 +280,7 @@
       host->name = host->encalloc;
     }
     else
-      return CURLE_URL_MALFORMAT;
+      return result;
   }
 #endif
   return CURLE_OK;
diff --git a/Utilities/cmcurl/lib/idn.h b/Utilities/cmcurl/lib/idn.h
index 6c0bbb7..74bbcaf 100644
--- a/Utilities/cmcurl/lib/idn.h
+++ b/Utilities/cmcurl/lib/idn.h
@@ -24,15 +24,13 @@
  *
  ***************************************************************************/
 
-#ifdef USE_WIN32_IDN
-bool Curl_win32_idn_to_ascii(const char *in, char **out);
-#endif /* USE_WIN32_IDN */
 bool Curl_is_ASCII_name(const char *hostname);
 CURLcode Curl_idnconvert_hostname(struct hostname *host);
 #if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
 #define USE_IDN
 void Curl_free_idnconverted_hostname(struct hostname *host);
-char *Curl_idn_decode(const char *input);
+CURLcode Curl_idn_decode(const char *input, char **output);
+CURLcode Curl_idn_encode(const char *input, char **output);
 #ifdef USE_LIBIDN2
 #define Curl_idn_free(x) idn2_free(x)
 #else
diff --git a/Utilities/cmcurl/lib/if2ip.c b/Utilities/cmcurl/lib/if2ip.c
index 6bf0ce1..5249f6c 100644
--- a/Utilities/cmcurl/lib/if2ip.c
+++ b/Utilities/cmcurl/lib/if2ip.c
@@ -92,6 +92,8 @@
 }
 #endif
 
+#ifndef CURL_DISABLE_BINDLOCAL
+
 #if defined(HAVE_GETIFADDRS)
 
 if2ip_result_t Curl_if2ip(int af,
@@ -254,3 +256,5 @@
 }
 
 #endif
+
+#endif /* CURL_DISABLE_BINDLOCAL */
diff --git a/Utilities/cmcurl/lib/imap.c b/Utilities/cmcurl/lib/imap.c
index ed197c9..de64c2a 100644
--- a/Utilities/cmcurl/lib/imap.c
+++ b/Utilities/cmcurl/lib/imap.c
@@ -45,9 +45,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -385,11 +382,11 @@
 
 /***********************************************************************
  *
- * state()
+ * imap_state()
  *
  * This is the ONLY way to change IMAP state!
  */
-static void state(struct Curl_easy *data, imapstate newstate)
+static void imap_state(struct Curl_easy *data, imapstate newstate)
 {
   struct imap_conn *imapc = &data->conn->proto.imapc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -441,7 +438,7 @@
   result = imap_sendf(data, "CAPABILITY");
 
   if(!result)
-    state(data, IMAP_CAPABILITY);
+    imap_state(data, IMAP_CAPABILITY);
 
   return result;
 }
@@ -458,7 +455,7 @@
   CURLcode result = imap_sendf(data, "STARTTLS");
 
   if(!result)
-    state(data, IMAP_STARTTLS);
+    imap_state(data, IMAP_STARTTLS);
 
   return result;
 }
@@ -487,7 +484,7 @@
   if(!result) {
     imapc->ssldone = ssldone;
     if(imapc->state != IMAP_UPGRADETLS)
-      state(data, IMAP_UPGRADETLS);
+      imap_state(data, IMAP_UPGRADETLS);
 
     if(imapc->ssldone) {
       imap_to_imaps(conn);
@@ -514,7 +511,7 @@
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!data->state.aptr.user) {
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
     return result;
   }
@@ -531,7 +528,7 @@
   free(passwd);
 
   if(!result)
-    state(data, IMAP_LOGIN);
+    imap_state(data, IMAP_LOGIN);
 
   return result;
 }
@@ -615,7 +612,7 @@
      with and end the connect phase if we don't */
   if(imapc->preauth ||
      !Curl_sasl_can_authenticate(&imapc->sasl, data)) {
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
     return result;
   }
 
@@ -624,7 +621,7 @@
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
-      state(data, IMAP_AUTHENTICATE);
+      imap_state(data, IMAP_AUTHENTICATE);
     else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
       /* Perform clear text authentication */
       result = imap_perform_login(data, conn);
@@ -667,7 +664,7 @@
   }
 
   if(!result)
-    state(data, IMAP_LIST);
+    imap_state(data, IMAP_LIST);
 
   return result;
 }
@@ -707,7 +704,7 @@
   free(mailbox);
 
   if(!result)
-    state(data, IMAP_SELECT);
+    imap_state(data, IMAP_SELECT);
 
   return result;
 }
@@ -749,7 +746,7 @@
     return CURLE_URL_MALFORMAT;
   }
   if(!result)
-    state(data, IMAP_FETCH);
+    imap_state(data, IMAP_FETCH);
 
   return result;
 }
@@ -820,7 +817,7 @@
   free(mailbox);
 
   if(!result)
-    state(data, IMAP_APPEND);
+    imap_state(data, IMAP_APPEND);
 
   return result;
 }
@@ -846,7 +843,7 @@
   result = imap_sendf(data, "SEARCH %s", imap->query);
 
   if(!result)
-    state(data, IMAP_SEARCH);
+    imap_state(data, IMAP_SEARCH);
 
   return result;
 }
@@ -863,7 +860,7 @@
   CURLcode result = imap_sendf(data, "LOGOUT");
 
   if(!result)
-    state(data, IMAP_LOGOUT);
+    imap_state(data, IMAP_LOGOUT);
 
   return result;
 }
@@ -1017,7 +1014,7 @@
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      state(data, IMAP_STOP);  /* Authenticated */
+      imap_state(data, IMAP_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
       if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
@@ -1049,7 +1046,7 @@
   }
   else
     /* End of connect phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1075,7 +1072,7 @@
     result = CURLE_QUOTE_ERROR;
   else
     /* End of DO phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1094,10 +1091,19 @@
 
   if(imapcode == '*') {
     /* See if this is an UIDVALIDITY response */
-    char tmp[20];
-    if(sscanf(line + 2, "OK [UIDVALIDITY %19[0123456789]]", tmp) == 1) {
-      Curl_safefree(imapc->mailbox_uidvalidity);
-      imapc->mailbox_uidvalidity = strdup(tmp);
+    if(checkprefix("OK [UIDVALIDITY ", line + 2)) {
+      size_t len = 0;
+      const char *p = &line[2] + strlen("OK [UIDVALIDITY ");
+      while((len < 20) && p[len] && ISDIGIT(p[len]))
+        len++;
+      if(len && (p[len] == ']')) {
+        struct dynbuf uid;
+        Curl_dyn_init(&uid, 20);
+        if(Curl_dyn_addn(&uid, p, len))
+          return CURLE_OUT_OF_MEMORY;
+        Curl_safefree(imapc->mailbox_uidvalidity);
+        imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid);
+      }
     }
   }
   else if(imapcode == IMAP_RESP_OK) {
@@ -1109,7 +1115,10 @@
     }
     else {
       /* Note the currently opened mailbox on this connection */
+      DEBUGASSERT(!imapc->mailbox);
       imapc->mailbox = strdup(imap->mailbox);
+      if(!imapc->mailbox)
+        return CURLE_OUT_OF_MEMORY;
 
       if(imap->custom)
         result = imap_perform_list(data);
@@ -1143,7 +1152,7 @@
 
   if(imapcode != '*') {
     Curl_pgrsSetDownloadSize(data, -1);
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
     return CURLE_REMOTE_FILE_NOT_FOUND;
   }
 
@@ -1178,7 +1187,7 @@
 
       if(!chunk) {
         /* no size, we're done with the data */
-        state(data, IMAP_STOP);
+        imap_state(data, IMAP_STOP);
         return CURLE_OK;
       }
       result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk);
@@ -1224,7 +1233,7 @@
   }
 
   /* End of DO phase */
-  state(data, IMAP_STOP);
+  imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1242,7 +1251,7 @@
     result = CURLE_WEIRD_SERVER_REPLY;
   else
     /* End of DONE phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1265,7 +1274,7 @@
     Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
 
     /* End of DO phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
   }
 
   return result;
@@ -1284,7 +1293,7 @@
     result = CURLE_UPLOAD_FAILED;
   else
     /* End of DONE phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1372,7 +1381,7 @@
       /* fallthrough, just stop! */
     default:
       /* internal error */
-      state(data, IMAP_STOP);
+      imap_state(data, IMAP_STOP);
       break;
     }
   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
@@ -1475,7 +1484,7 @@
     return result;
 
   /* Start off waiting for the server greeting response */
-  state(data, IMAP_SERVERGREET);
+  imap_state(data, IMAP_SERVERGREET);
 
   /* Start off with an response id of '*' */
   strcpy(imapc->resptag, "*");
@@ -1516,12 +1525,12 @@
     /* Handle responses after FETCH or APPEND transfer has finished */
 
     if(!data->state.upload && data->set.mimepost.kind == MIMEKIND_NONE)
-      state(data, IMAP_FETCH_FINAL);
+      imap_state(data, IMAP_FETCH_FINAL);
     else {
       /* End the APPEND command first by sending an empty line */
       result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
       if(!result)
-        state(data, IMAP_APPEND_FINAL);
+        imap_state(data, IMAP_APPEND_FINAL);
     }
 
     /* Run the state-machine */
@@ -1777,7 +1786,7 @@
 
   /* Calculate the tag based on the connection ID and command ID */
   msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
-            'A' + curlx_sltosi(data->conn->connection_id % 26),
+            'A' + curlx_sltosi((long)(data->conn->connection_id % 26)),
             ++imapc->cmdid);
 
   /* start with a blank buffer */
@@ -1806,79 +1815,37 @@
  */
 static char *imap_atom(const char *str, bool escape_only)
 {
-  /* !checksrc! disable PARENBRACE 1 */
-  const char atom_specials[] = "(){ %*]";
-  const char *p1;
-  char *p2;
-  size_t backsp_count = 0;
-  size_t quote_count = 0;
-  bool others_exists = FALSE;
-  size_t newlen = 0;
-  char *newstr = NULL;
+  struct dynbuf line;
+  size_t nclean;
+  size_t len;
 
   if(!str)
     return NULL;
 
-  /* Look for "atom-specials", counting the backslash and quote characters as
-     these will need escaping */
-  p1 = str;
-  while(*p1) {
-    if(*p1 == '\\')
-      backsp_count++;
-    else if(*p1 == '"')
-      quote_count++;
-    else if(!escape_only) {
-      const char *p3 = atom_specials;
-
-      while(*p3 && !others_exists) {
-        if(*p1 == *p3)
-          others_exists = TRUE;
-
-        p3++;
-      }
-    }
-
-    p1++;
-  }
-
-  /* Does the input contain any "atom-special" characters? */
-  if(!backsp_count && !quote_count && !others_exists)
+  len = strlen(str);
+  nclean = strcspn(str, "() {%*]\\\"");
+  if(len == nclean)
+    /* nothing to escape, return a strdup */
     return strdup(str);
 
-  /* Calculate the new string length */
-  newlen = strlen(str) + backsp_count + quote_count + (escape_only ? 0 : 2);
+  Curl_dyn_init(&line, 2000);
 
-  /* Allocate the new string */
-  newstr = (char *) malloc((newlen + 1) * sizeof(char));
-  if(!newstr)
+  if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
     return NULL;
 
-  /* Surround the string in quotes if necessary */
-  p2 = newstr;
-  if(!escape_only) {
-    newstr[0] = '"';
-    newstr[newlen - 1] = '"';
-    p2++;
+  while(*str) {
+    if((*str == '\\' || *str == '"') &&
+       Curl_dyn_addn(&line, "\\", 1))
+      return NULL;
+    if(Curl_dyn_addn(&line, str, 1))
+      return NULL;
+    str++;
   }
 
-  /* Copy the string, escaping backslash and quote characters along the way */
-  p1 = str;
-  while(*p1) {
-    if(*p1 == '\\' || *p1 == '"') {
-      *p2 = '\\';
-      p2++;
-    }
+  if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
+    return NULL;
 
-   *p2 = *p1;
-
-    p1++;
-    p2++;
-  }
-
-  /* Terminate the string */
-  newstr[newlen] = '\0';
-
-  return newstr;
+  return Curl_dyn_ptr(&line);
 }
 
 /***********************************************************************
@@ -1925,6 +1892,7 @@
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
   const char *ptr = conn->options;
+  bool prefer_login = false;
 
   while(!result && ptr && *ptr) {
     const char *key = ptr;
@@ -1938,26 +1906,39 @@
     while(*ptr && *ptr != ';')
       ptr++;
 
-    if(strncasecompare(key, "AUTH=", 5))
+    if(strncasecompare(key, "AUTH=+LOGIN", 11)) {
+      /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */
+      prefer_login = true;
+      imapc->sasl.prefmech = SASL_AUTH_NONE;
+    }
+    else if(strncasecompare(key, "AUTH=", 5)) {
+      prefer_login = false;
       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
                                                value, ptr - value);
-    else
+    }
+    else {
+      prefer_login = false;
       result = CURLE_URL_MALFORMAT;
+    }
 
     if(*ptr == ';')
       ptr++;
   }
 
-  switch(imapc->sasl.prefmech) {
-  case SASL_AUTH_NONE:
-    imapc->preftype = IMAP_TYPE_NONE;
-    break;
-  case SASL_AUTH_DEFAULT:
-    imapc->preftype = IMAP_TYPE_ANY;
-    break;
-  default:
-    imapc->preftype = IMAP_TYPE_SASL;
-    break;
+  if(prefer_login)
+    imapc->preftype = IMAP_TYPE_CLEARTEXT;
+  else {
+    switch(imapc->sasl.prefmech) {
+    case SASL_AUTH_NONE:
+      imapc->preftype = IMAP_TYPE_NONE;
+      break;
+    case SASL_AUTH_DEFAULT:
+      imapc->preftype = IMAP_TYPE_ANY;
+      break;
+    default:
+      imapc->preftype = IMAP_TYPE_SASL;
+      break;
+    }
   }
 
   return result;
diff --git a/Utilities/cmcurl/lib/inet_ntop.c b/Utilities/cmcurl/lib/inet_ntop.c
index fa90773..c9cee0c 100644
--- a/Utilities/cmcurl/lib/inet_ntop.c
+++ b/Utilities/cmcurl/lib/inet_ntop.c
@@ -96,10 +96,10 @@
   char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
   char *tp;
   struct {
-    long base;
-    long len;
+    int base;
+    int len;
   } best, cur;
-  unsigned long words[IN6ADDRSZ / INT16SZ];
+  unsigned int words[IN6ADDRSZ / INT16SZ];
   int i;
 
   /* Preprocess:
@@ -108,7 +108,7 @@
    */
   memset(words, '\0', sizeof(words));
   for(i = 0; i < IN6ADDRSZ; i++)
-    words[i/2] |= (src[i] << ((1 - (i % 2)) << 3));
+    words[i/2] |= ((unsigned int)src[i] << ((1 - (i % 2)) << 3));
 
   best.base = -1;
   cur.base  = -1;
@@ -117,8 +117,9 @@
 
   for(i = 0; i < (IN6ADDRSZ / INT16SZ); i++) {
     if(words[i] == 0) {
-      if(cur.base == -1)
-        cur.base = i, cur.len = 1;
+      if(cur.base == -1) {
+        cur.base = i; cur.len = 1;
+      }
       else
         cur.len++;
     }
@@ -158,7 +159,7 @@
       tp += strlen(tp);
       break;
     }
-    tp += msnprintf(tp, 5, "%lx", words[i]);
+    tp += msnprintf(tp, 5, "%x", words[i]);
   }
 
   /* Was it a trailing run of 0x00's?
diff --git a/Utilities/cmcurl/lib/krb5.c b/Utilities/cmcurl/lib/krb5.c
index a71779a..a1102e5 100644
--- a/Utilities/cmcurl/lib/krb5.c
+++ b/Utilities/cmcurl/lib/krb5.c
@@ -72,7 +72,7 @@
   char *sptr = s;
   CURLcode result = CURLE_OK;
 #ifdef HAVE_GSSAPI
-  enum protection_level data_sec = conn->data_prot;
+  unsigned char data_sec = conn->data_prot;
 #endif
 
   if(!cmd)
@@ -91,7 +91,7 @@
 #ifdef HAVE_GSSAPI
     conn->data_prot = PROT_CMD;
 #endif
-    result = Curl_write(data, conn->sock[FIRSTSOCKET], sptr, write_len,
+    result = Curl_nwrite(data, FIRSTSOCKET, sptr, write_len,
                         &bytes_written);
 #ifdef HAVE_GSSAPI
     DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
@@ -261,7 +261,7 @@
     }
     /* We pass NULL as |output_name_type| to avoid a leak. */
     gss_display_name(&min, gssname, &output_buffer, NULL);
-    infof(data, "Trying against %s", output_buffer.value);
+    infof(data, "Trying against %s", (char *)output_buffer.value);
     gssresp = GSS_C_NO_BUFFER;
     *context = GSS_C_NO_CONTEXT;
 
@@ -385,7 +385,7 @@
 };
 
 static const struct {
-  enum protection_level level;
+  unsigned char level;
   const char *name;
 } level_names[] = {
   { PROT_CLEAR, "clear" },
@@ -394,8 +394,7 @@
   { PROT_PRIVATE, "private" }
 };
 
-static enum protection_level
-name_to_level(const char *name)
+static unsigned char name_to_level(const char *name)
 {
   int i;
   for(i = 0; i < (int)sizeof(level_names)/(int)sizeof(level_names[0]); i++)
@@ -734,7 +733,7 @@
 {
   int code;
   struct connectdata *conn = data->conn;
-  enum protection_level level = conn->request_data_prot;
+  unsigned char level = conn->request_data_prot;
 
   DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
 
@@ -793,7 +792,7 @@
 int
 Curl_sec_request_prot(struct connectdata *conn, const char *level)
 {
-  enum protection_level l = name_to_level(level);
+  unsigned char l = name_to_level(level);
   if(l == PROT_NONE)
     return -1;
   DEBUGASSERT(l > PROT_NONE && l < PROT_LAST);
diff --git a/Utilities/cmcurl/lib/ldap.c b/Utilities/cmcurl/lib/ldap.c
index 4c88b0a..239d3fb 100644
--- a/Utilities/cmcurl/lib/ldap.c
+++ b/Utilities/cmcurl/lib/ldap.c
@@ -50,6 +50,14 @@
 #endif
 
 #ifdef USE_WIN32_LDAP           /* Use Windows LDAP implementation. */
+# ifdef _MSC_VER
+#  pragma warning(push)
+#  pragma warning(disable: 4201)
+# endif
+# include <subauth.h>  /* for [P]UNICODE_STRING */
+# ifdef _MSC_VER
+#  pragma warning(pop)
+# endif
 # include <winldap.h>
 # ifndef LDAP_VENDOR_NAME
 #  error Your Platform SDK is NOT sufficient for LDAP support! \
@@ -231,7 +239,7 @@
   }
   else
 #endif
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_DIGEST_AUTH)
   if(authflags & CURLAUTH_DIGEST) {
     method = LDAP_AUTH_DIGEST;
   }
@@ -727,7 +735,9 @@
       if(result)
         goto quit;
       dlsize++;
-      Curl_pgrsSetDownloadCounter(data, dlsize);
+      result = Curl_pgrsSetDownloadCounter(data, dlsize);
+      if(result)
+        goto quit;
     }
 
     if(ber)
@@ -754,7 +764,7 @@
 
   /* no data to transfer */
   Curl_setup_transfer(data, -1, -1, FALSE, -1);
-  connclose(conn, "LDAP connection always disable re-use");
+  connclose(conn, "LDAP connection always disable reuse");
 
   return result;
 }
diff --git a/Utilities/cmcurl/lib/macos.c b/Utilities/cmcurl/lib/macos.c
new file mode 100644
index 0000000..9e8e76e
--- /dev/null
+++ b/Utilities/cmcurl/lib/macos.c
@@ -0,0 +1,55 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef CURL_MACOS_CALL_COPYPROXIES
+
+#include <curl/curl.h>
+
+#include "macos.h"
+
+#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
+
+CURLcode Curl_macos_init(void)
+{
+  {
+    /*
+     * The automagic conversion from IPv4 literals to IPv6 literals only
+     * works if the SCDynamicStoreCopyProxies system function gets called
+     * first. As Curl currently doesn't support system-wide HTTP proxies, we
+     * therefore don't use any value this function might return.
+     *
+     * This function is only available on macOS and is not needed for
+     * IPv4-only builds, hence the conditions for defining
+     * CURL_MACOS_CALL_COPYPROXIES in curl_setup.h.
+     */
+    CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+    if(dict)
+      CFRelease(dict);
+  }
+  return CURLE_OK;
+}
+
+#endif
diff --git a/Utilities/cmcurl/lib/macos.h b/Utilities/cmcurl/lib/macos.h
new file mode 100644
index 0000000..637860e
--- /dev/null
+++ b/Utilities/cmcurl/lib/macos.h
@@ -0,0 +1,39 @@
+#ifndef HEADER_CURL_MACOS_H
+#define HEADER_CURL_MACOS_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef CURL_MACOS_CALL_COPYPROXIES
+
+CURLcode Curl_macos_init(void);
+
+#else
+
+#define Curl_macos_init() CURLE_OK
+
+#endif
+
+#endif /* HEADER_CURL_MACOS_H */
diff --git a/Utilities/cmcurl/lib/md4.c b/Utilities/cmcurl/lib/md4.c
index 9ff093b..30ab62e 100644
--- a/Utilities/cmcurl/lib/md4.c
+++ b/Utilities/cmcurl/lib/md4.c
@@ -42,6 +42,7 @@
 
 #ifdef USE_WOLFSSL
 #include <wolfssl/options.h>
+#define VOID_MD4_INIT
 #ifdef NO_MD4
 #define WOLFSSL_NO_MD4
 #endif
@@ -92,9 +93,10 @@
 
 typedef struct md4_ctx MD4_CTX;
 
-static void MD4_Init(MD4_CTX *ctx)
+static int MD4_Init(MD4_CTX *ctx)
 {
   md4_init(ctx);
+  return 1;
 }
 
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
@@ -114,9 +116,9 @@
 #elif defined(AN_APPLE_OS)
 typedef CC_MD4_CTX MD4_CTX;
 
-static void MD4_Init(MD4_CTX *ctx)
+static int MD4_Init(MD4_CTX *ctx)
 {
-  (void)CC_MD4_Init(ctx);
+  return CC_MD4_Init(ctx);
 }
 
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
@@ -137,15 +139,22 @@
 };
 typedef struct md4_ctx MD4_CTX;
 
-static void MD4_Init(MD4_CTX *ctx)
+static int MD4_Init(MD4_CTX *ctx)
 {
   ctx->hCryptProv = 0;
   ctx->hHash = 0;
 
-  if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL,
-                         CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
-    CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash);
+  if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL,
+                          CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+    return 0;
+
+  if(!CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash)) {
+    CryptReleaseContext(ctx->hCryptProv, 0);
+    ctx->hCryptProv = 0;
+    return 0;
   }
+
+  return 1;
 }
 
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
@@ -176,10 +185,11 @@
 };
 typedef struct md4_ctx MD4_CTX;
 
-static void MD4_Init(MD4_CTX *ctx)
+static int MD4_Init(MD4_CTX *ctx)
 {
   ctx->data = NULL;
   ctx->size = 0;
+  return 1;
 }
 
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
@@ -258,7 +268,7 @@
 };
 typedef struct md4_ctx MD4_CTX;
 
-static void MD4_Init(MD4_CTX *ctx);
+static int MD4_Init(MD4_CTX *ctx);
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size);
 static void MD4_Final(unsigned char *result, MD4_CTX *ctx);
 
@@ -397,7 +407,7 @@
   return ptr;
 }
 
-static void MD4_Init(MD4_CTX *ctx)
+static int MD4_Init(MD4_CTX *ctx)
 {
   ctx->a = 0x67452301;
   ctx->b = 0xefcdab89;
@@ -406,6 +416,7 @@
 
   ctx->lo = 0;
   ctx->hi = 0;
+  return 1;
 }
 
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
@@ -496,14 +507,21 @@
 
 #endif /* CRYPTO LIBS */
 
-void Curl_md4it(unsigned char *output, const unsigned char *input,
-                const size_t len)
+CURLcode Curl_md4it(unsigned char *output, const unsigned char *input,
+                    const size_t len)
 {
   MD4_CTX ctx;
 
+#ifdef VOID_MD4_INIT
   MD4_Init(&ctx);
+#else
+  if(!MD4_Init(&ctx))
+    return CURLE_FAILED_INIT;
+#endif
+
   MD4_Update(&ctx, input, curlx_uztoui(len));
   MD4_Final(output, &ctx);
+  return CURLE_OK;
 }
 
 #endif /* USE_CURL_NTLM_CORE */
diff --git a/Utilities/cmcurl/lib/md5.c b/Utilities/cmcurl/lib/md5.c
index 0a02cc0..01415af 100644
--- a/Utilities/cmcurl/lib/md5.c
+++ b/Utilities/cmcurl/lib/md5.c
@@ -24,7 +24,8 @@
 
 #include "curl_setup.h"
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \
+    || !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include <string.h>
 #include <curl/curl.h>
@@ -213,7 +214,8 @@
 
   if(!CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash)) {
     CryptReleaseContext(ctx->hCryptProv, 0);
-    return CURLE_OUT_OF_MEMORY;
+    ctx->hCryptProv = 0;
+    return CURLE_FAILED_INIT;
   }
 
   return CURLE_OK;
@@ -651,4 +653,4 @@
   return CURLE_OK;
 }
 
-#endif /* CURL_DISABLE_CRYPTO_AUTH */
+#endif /* Using NTLM (without SSPI) || Digest */
diff --git a/Utilities/cmcurl/lib/mime.c b/Utilities/cmcurl/lib/mime.c
index 39aac8f..3b27e59 100644
--- a/Utilities/cmcurl/lib/mime.c
+++ b/Utilities/cmcurl/lib/mime.c
@@ -84,7 +84,7 @@
 };
 
 /* Base64 encoding table */
-static const char base64[] =
+static const char base64enc[] =
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /* Quoted-printable character class table.
@@ -469,10 +469,10 @@
     i = st->buf[st->bufbeg++] & 0xFF;
     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
-    *ptr++ = base64[(i >> 18) & 0x3F];
-    *ptr++ = base64[(i >> 12) & 0x3F];
-    *ptr++ = base64[(i >> 6) & 0x3F];
-    *ptr++ = base64[i & 0x3F];
+    *ptr++ = base64enc[(i >> 18) & 0x3F];
+    *ptr++ = base64enc[(i >> 12) & 0x3F];
+    *ptr++ = base64enc[(i >> 6) & 0x3F];
+    *ptr++ = base64enc[i & 0x3F];
     cursize += 4;
     st->pos += 4;
     size -= 4;
@@ -496,10 +496,10 @@
           i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
 
         i |= (st->buf[st->bufbeg] & 0xFF) << 16;
-        ptr[0] = base64[(i >> 18) & 0x3F];
-        ptr[1] = base64[(i >> 12) & 0x3F];
+        ptr[0] = base64enc[(i >> 18) & 0x3F];
+        ptr[1] = base64enc[(i >> 12) & 0x3F];
         if(++st->bufbeg != st->bufend) {
-          ptr[2] = base64[(i >> 6) & 0x3F];
+          ptr[2] = base64enc[(i >> 6) & 0x3F];
           st->bufbeg++;
         }
         cursize += 4;
@@ -1167,14 +1167,16 @@
 
 void Curl_mime_cleanpart(curl_mimepart *part)
 {
-  cleanup_part_content(part);
-  curl_slist_free_all(part->curlheaders);
-  if(part->flags & MIME_USERHEADERS_OWNER)
-    curl_slist_free_all(part->userheaders);
-  Curl_safefree(part->mimetype);
-  Curl_safefree(part->name);
-  Curl_safefree(part->filename);
-  Curl_mime_initpart(part);
+  if(part) {
+    cleanup_part_content(part);
+    curl_slist_free_all(part->curlheaders);
+    if(part->flags & MIME_USERHEADERS_OWNER)
+      curl_slist_free_all(part->userheaders);
+    Curl_safefree(part->mimetype);
+    Curl_safefree(part->name);
+    Curl_safefree(part->filename);
+    Curl_mime_initpart(part);
+  }
 }
 
 /* Recursively delete a mime handle and its parts. */
@@ -1287,9 +1289,9 @@
     mime->lastpart = NULL;
 
     memset(mime->boundary, '-', MIME_BOUNDARY_DASHES);
-    if(Curl_rand_hex(easy,
-                     (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES],
-                     MIME_RAND_BOUNDARY_CHARS + 1)) {
+    if(Curl_rand_alnum(easy,
+                       (unsigned char *) &mime->boundary[MIME_BOUNDARY_DASHES],
+                       MIME_RAND_BOUNDARY_CHARS + 1)) {
       /* failed to get random separator, bail out */
       free(mime);
       return NULL;
diff --git a/Utilities/cmcurl/lib/mime.h b/Utilities/cmcurl/lib/mime.h
index 04adf2d..0a05c2a 100644
--- a/Utilities/cmcurl/lib/mime.h
+++ b/Utilities/cmcurl/lib/mime.h
@@ -27,7 +27,7 @@
 #include "curl_setup.h"
 
 #define MIME_BOUNDARY_DASHES            24  /* leading boundary dashes */
-#define MIME_RAND_BOUNDARY_CHARS        16  /* Nb. of random boundary chars. */
+#define MIME_RAND_BOUNDARY_CHARS        22  /* Nb. of random boundary chars. */
 #define MAX_ENCODED_LINE_LENGTH         76  /* Maximum encoded line length. */
 #define ENCODING_BUFFER_SIZE            256 /* Encoding temp buffers size. */
 
diff --git a/Utilities/cmcurl/lib/mqtt.c b/Utilities/cmcurl/lib/mqtt.c
index dbe7239..54f8882 100644
--- a/Utilities/cmcurl/lib/mqtt.c
+++ b/Utilities/cmcurl/lib/mqtt.c
@@ -109,6 +109,7 @@
   mq = calloc(1, sizeof(struct MQTT));
   if(!mq)
     return CURLE_OUT_OF_MEMORY;
+  Curl_dyn_init(&mq->recvbuf, DYN_MQTT_RECV);
   data->req.p.mqtt = mq;
   return CURLE_OK;
 }
@@ -117,11 +118,9 @@
                           char *buf, size_t len)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   struct MQTT *mq = data->req.p.mqtt;
   ssize_t n;
-  result = Curl_write(data, sockfd, buf, len, &n);
+  result = Curl_nwrite(data, FIRSTSOCKET, buf, len, &n);
   if(result)
     return result;
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
@@ -297,12 +296,12 @@
   /* set initial values for the CONNECT packet */
   pos = init_connpack(packet, remain, remain_pos);
 
-  result = Curl_rand_hex(data, (unsigned char *)&client_id[clen],
-                         MQTT_CLIENTID_LEN - clen + 1);
+  result = Curl_rand_alnum(data, (unsigned char *)&client_id[clen],
+                           MQTT_CLIENTID_LEN - clen + 1);
   /* add client id */
   rc = add_client_id(client_id, strlen(client_id), packet, pos + 1);
   if(rc) {
-    failf(data, "Client ID length mismatched: [%lu]", strlen(client_id));
+    failf(data, "Client ID length mismatched: [%zu]", strlen(client_id));
     result = CURLE_WEIRD_SERVER_REPLY;
     goto end;
   }
@@ -319,7 +318,7 @@
     rc = add_user(username, ulen,
                   (unsigned char *)packet, start_user, remain_pos);
     if(rc) {
-      failf(data, "Username is too large: [%lu]", ulen);
+      failf(data, "Username is too large: [%zu]", ulen);
       result = CURLE_WEIRD_SERVER_REPLY;
       goto end;
     }
@@ -329,7 +328,7 @@
   if(plen) {
     rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
     if(rc) {
-      failf(data, "Password is too large: [%lu]", plen);
+      failf(data, "Password is too large: [%zu]", plen);
       result = CURLE_WEIRD_SERVER_REPLY;
       goto end;
     }
@@ -352,36 +351,66 @@
   struct MQTT *mq = data->req.p.mqtt;
   result = mqtt_send(data, (char *)"\xe0\x00", 2);
   Curl_safefree(mq->sendleftovers);
+  Curl_dyn_free(&mq->recvbuf);
   return result;
 }
 
+static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
+{
+  struct MQTT *mq = data->req.p.mqtt;
+  size_t rlen = Curl_dyn_len(&mq->recvbuf);
+  CURLcode result;
+
+  if(rlen < nbytes) {
+    unsigned char readbuf[1024];
+    ssize_t nread;
+
+    DEBUGASSERT(nbytes - rlen < sizeof(readbuf));
+    result = Curl_read(data, data->conn->sock[FIRSTSOCKET],
+                       (char *)readbuf, nbytes - rlen, &nread);
+    if(result)
+      return result;
+    DEBUGASSERT(nread >= 0);
+    if(Curl_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread))
+      return CURLE_OUT_OF_MEMORY;
+    rlen = Curl_dyn_len(&mq->recvbuf);
+  }
+  return (rlen >= nbytes)? CURLE_OK : CURLE_AGAIN;
+}
+
+static void mqtt_recv_consume(struct Curl_easy *data, size_t nbytes)
+{
+  struct MQTT *mq = data->req.p.mqtt;
+  size_t rlen = Curl_dyn_len(&mq->recvbuf);
+  if(rlen <= nbytes)
+    Curl_dyn_reset(&mq->recvbuf);
+  else
+    Curl_dyn_tail(&mq->recvbuf, rlen - nbytes);
+}
+
 static CURLcode mqtt_verify_connack(struct Curl_easy *data)
 {
+  struct MQTT *mq = data->req.p.mqtt;
   CURLcode result;
-  struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-  unsigned char readbuf[MQTT_CONNACK_LEN];
-  ssize_t nread;
+  char *ptr;
 
-  result = Curl_read(data, sockfd, (char *)readbuf, MQTT_CONNACK_LEN, &nread);
+  result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN);
   if(result)
     goto fail;
 
-  Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread);
+  /* verify CONNACK */
+  DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN);
+  ptr = Curl_dyn_ptr(&mq->recvbuf);
+  Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_CONNACK_LEN);
 
-  /* fixme */
-  if(nread < MQTT_CONNACK_LEN) {
+  if(ptr[0] != 0x00 || ptr[1] != 0x00) {
+    failf(data, "Expected %02x%02x but got %02x%02x",
+          0x00, 0x00, ptr[0], ptr[1]);
+    Curl_dyn_reset(&mq->recvbuf);
     result = CURLE_WEIRD_SERVER_REPLY;
     goto fail;
   }
-
-  /* verify CONNACK */
-  if(readbuf[0] != 0x00 || readbuf[1] != 0x00) {
-    failf(data, "Expected %02x%02x but got %02x%02x",
-          0x00, 0x00, readbuf[0], readbuf[1]);
-    result = CURLE_WEIRD_SERVER_REPLY;
-  }
-
+  mqtt_recv_consume(data, MQTT_CONNACK_LEN);
 fail:
   return result;
 }
@@ -454,31 +483,29 @@
  */
 static CURLcode mqtt_verify_suback(struct Curl_easy *data)
 {
-  CURLcode result;
+  struct MQTT *mq = data->req.p.mqtt;
   struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
-  unsigned char readbuf[MQTT_SUBACK_LEN];
-  ssize_t nread;
   struct mqtt_conn *mqtt = &conn->proto.mqtt;
+  CURLcode result;
+  char *ptr;
 
-  result = Curl_read(data, sockfd, (char *)readbuf, MQTT_SUBACK_LEN, &nread);
+  result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN);
   if(result)
     goto fail;
 
-  Curl_debug(data, CURLINFO_HEADER_IN, (char *)readbuf, (size_t)nread);
+  /* verify SUBACK */
+  DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN);
+  ptr = Curl_dyn_ptr(&mq->recvbuf);
+  Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_SUBACK_LEN);
 
-  /* fixme */
-  if(nread < MQTT_SUBACK_LEN) {
+  if(((unsigned char)ptr[0]) != ((mqtt->packetid >> 8) & 0xff) ||
+     ((unsigned char)ptr[1]) != (mqtt->packetid & 0xff) ||
+     ptr[2] != 0x00) {
+    Curl_dyn_reset(&mq->recvbuf);
     result = CURLE_WEIRD_SERVER_REPLY;
     goto fail;
   }
-
-  /* verify SUBACK */
-  if(readbuf[0] != ((mqtt->packetid >> 8) & 0xff) ||
-     readbuf[1] != (mqtt->packetid & 0xff) ||
-     readbuf[2] != 0x00)
-    result = CURLE_WEIRD_SERVER_REPLY;
-
+  mqtt_recv_consume(data, MQTT_SUBACK_LEN);
 fail:
   return result;
 }
@@ -636,7 +663,7 @@
 
     /* -- switched state -- */
     remlen = mq->remaining_length;
-    infof(data, "Remaining length: %zd bytes", remlen);
+    infof(data, "Remaining length: %zu bytes", remlen);
     if(data->set.max_filesize &&
        (curl_off_t)remlen > data->set.max_filesize) {
       failf(data, "Maximum file size exceeded");
@@ -670,7 +697,9 @@
 
     mq->npacket -= nread;
     k->bytecount += nread;
-    Curl_pgrsSetDownloadCounter(data, k->bytecount);
+    result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
+    if(result)
+      goto end;
 
     /* if QoS is set, message contains packet id */
 
@@ -713,6 +742,7 @@
   (void)status;
   (void)premature;
   Curl_safefree(mq->sendleftovers);
+  Curl_dyn_free(&mq->recvbuf);
   return CURLE_OK;
 }
 
diff --git a/Utilities/cmcurl/lib/mqtt.h b/Utilities/cmcurl/lib/mqtt.h
index 6396136..84f1770 100644
--- a/Utilities/cmcurl/lib/mqtt.h
+++ b/Utilities/cmcurl/lib/mqtt.h
@@ -56,6 +56,7 @@
   size_t npacket; /* byte counter */
   unsigned char firstbyte;
   size_t remaining_length;
+  struct dynbuf recvbuf;
 };
 
 #endif /* HEADER_CURL_MQTT_H */
diff --git a/Utilities/cmcurl/lib/multi.c b/Utilities/cmcurl/lib/multi.c
index d1d32b7..ff753ac 100644
--- a/Utilities/cmcurl/lib/multi.c
+++ b/Utilities/cmcurl/lib/multi.c
@@ -112,7 +112,7 @@
 static void process_pending_handles(struct Curl_multi *multi);
 
 #ifdef DEBUGBUILD
-static const char * const statename[]={
+static const char * const multi_statename[]={
   "INIT",
   "PENDING",
   "CONNECT",
@@ -194,15 +194,10 @@
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   if(data->mstate >= MSTATE_PENDING &&
      data->mstate < MSTATE_COMPLETED) {
-    long connection_id = -5000;
-
-    if(data->conn)
-      connection_id = data->conn->connection_id;
-
     infof(data,
-          "STATE: %s => %s handle %p; line %d (connection #%ld)",
-          statename[oldstate], statename[data->mstate],
-          (void *)data, lineno, connection_id);
+          "STATE: %s => %s handle %p; line %d",
+          multi_statename[oldstate], multi_statename[data->mstate],
+          (void *)data, lineno);
   }
 #endif
 
@@ -464,6 +459,20 @@
                            CURL_DNS_HASH_SIZE);
 }
 
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
+{
+  if(!multi->warned) {
+    infof(data, "!!! WARNING !!!");
+    infof(data, "This is a debug build of libcurl, "
+          "do not use in production.");
+    multi->warned = true;
+  }
+}
+#else
+#define multi_warn_debug(x,y) Curl_nop_stmt
+#endif
+
 /* returns TRUE if the easy handle is supposed to be present in the main link
    list */
 static bool in_main_list(struct Curl_easy *data)
@@ -623,8 +632,13 @@
     data->set.server_response_timeout;
   data->state.conn_cache->closure_handle->set.no_signal =
     data->set.no_signal;
+  data->id = data->state.conn_cache->next_easy_id++;
+  if(data->state.conn_cache->next_easy_id <= 0)
+    data->state.conn_cache->next_easy_id = 0;
   CONNCACHE_UNLOCK(data);
 
+  multi_warn_debug(multi, data);
+
   return CURLM_OK;
 }
 
@@ -651,10 +665,15 @@
 {
   CURLcode result;
   struct connectdata *conn = data->conn;
-  unsigned int i;
 
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d",
+               multi_statename[data->mstate],
+               (int)status, (int)premature, data->state.done));
+#else
   DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d",
                (int)status, (int)premature, data->state.done));
+#endif
 
   if(data->state.done)
     /* Stop if multi_done() has already been called */
@@ -701,12 +720,7 @@
 
   Curl_safefree(data->state.ulbuf);
 
-  /* if the transfer was completed in a paused state there can be buffered
-     data left to free */
-  for(i = 0; i < data->state.tempcount; i++) {
-    Curl_dyn_free(&data->state.tempwrite[i].b);
-  }
-  data->state.tempcount = 0;
+  Curl_client_cleanup(data);
 
   CONNCACHE_LOCK(data);
   Curl_detach_connection(data);
@@ -737,11 +751,12 @@
 
      if premature is TRUE, it means this connection was said to be DONE before
      the entire request operation is complete and thus we can't know in what
-     state it is for re-using, so we're forced to close it. In a perfect world
+     state it is for reusing, so we're forced to close it. In a perfect world
      we can add code that keep track of if we really must close it here or not,
      but currently we have no such detail knowledge.
   */
 
+  data->state.recent_conn_id = conn->connection_id;
   if((data->set.reuse_forbid
 #if defined(USE_NTLM)
       && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
@@ -753,8 +768,9 @@
 #endif
      ) || conn->bits.close
        || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
-    DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d"
-                 ", close=%d, premature=%d, conn_multiplex=%d",
+    DEBUGF(infof(data, "multi_done, not reusing connection=%"
+                       CURL_FORMAT_CURL_OFF_T ", forbid=%d"
+                       ", close=%d, premature=%d, conn_multiplex=%d",
                  conn->connection_id,
                  data->set.reuse_forbid, conn->bits.close, premature,
                  Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
@@ -774,15 +790,16 @@
       conn->bits.conn_to_host ? conn->conn_to_host.dispname :
       conn->host.dispname;
     /* create string before returning the connection */
-    long connection_id = conn->connection_id;
+    curl_off_t connection_id = conn->connection_id;
     msnprintf(buffer, sizeof(buffer),
-              "Connection #%ld to host %s left intact",
+              "Connection #%" CURL_FORMAT_CURL_OFF_T " to host %s left intact",
               connection_id, host);
     /* the connection is no longer in use by this transfer */
     CONNCACHE_UNLOCK(data);
     if(Curl_conncache_return_conn(data, conn)) {
       /* remember the most recently used connection */
       data->state.lastconnect_id = connection_id;
+      data->state.recent_conn_id = connection_id;
       infof(data, "%s", buffer);
     }
     else
@@ -1087,8 +1104,7 @@
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
-  data = multi->easyp;
-  while(data) {
+  for(data = multi->easyp; data; data = data->next) {
     int bitmap;
 #ifdef __clang_analyzer_
     /* to prevent "The left operand of '>=' is a garbage value" warnings */
@@ -1097,30 +1113,21 @@
     bitmap = multi_getsock(data, sockbunch);
 
     for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      curl_socket_t s = CURL_SOCKET_BAD;
-
-      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK(sockbunch[i])) {
+      if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
         if(!FDSET_SOCK(sockbunch[i]))
           /* pretend it doesn't exist */
           continue;
-        FD_SET(sockbunch[i], read_fd_set);
-        s = sockbunch[i];
+        if(bitmap & GETSOCK_READSOCK(i))
+          FD_SET(sockbunch[i], read_fd_set);
+        if(bitmap & GETSOCK_WRITESOCK(i))
+          FD_SET(sockbunch[i], write_fd_set);
+        if((int)sockbunch[i] > this_max_fd)
+          this_max_fd = (int)sockbunch[i];
       }
-      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK(sockbunch[i])) {
-        if(!FDSET_SOCK(sockbunch[i]))
-          /* pretend it doesn't exist */
-          continue;
-        FD_SET(sockbunch[i], write_fd_set);
-        s = sockbunch[i];
-      }
-      if(s == CURL_SOCKET_BAD)
-        /* this socket is unused, break out of loop */
+      else {
         break;
-      if((int)s > this_max_fd)
-        this_max_fd = (int)s;
+      }
     }
-
-    data = data->next; /* check next handle */
   }
 
   *max_fd = this_max_fd;
@@ -1183,27 +1190,17 @@
     return CURLM_BAD_FUNCTION_ARGUMENT;
 
   /* Count up how many fds we have from the multi handle */
-  data = multi->easyp;
-  while(data) {
+  for(data = multi->easyp; data; data = data->next) {
     bitmap = multi_getsock(data, sockbunch);
 
-    for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      curl_socket_t s = CURL_SOCKET_BAD;
-
-      if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
+    for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
+      if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
         ++nfds;
-        s = sockbunch[i];
       }
-      if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
-        ++nfds;
-        s = sockbunch[i];
-      }
-      if(s == CURL_SOCKET_BAD) {
+      else {
         break;
       }
     }
-
-    data = data->next; /* check next handle */
   }
 
   /* If the internally desired timeout is actually shorter than requested from
@@ -1243,49 +1240,42 @@
 
   if(curlfds) {
     /* Add the curl handles to our pollfds first */
-    data = multi->easyp;
-    while(data) {
+    for(data = multi->easyp; data; data = data->next) {
       bitmap = multi_getsock(data, sockbunch);
 
       for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-        curl_socket_t s = CURL_SOCKET_BAD;
+        if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
+          struct pollfd *ufd = &ufds[nfds++];
 #ifdef USE_WINSOCK
-        long mask = 0;
+          long mask = 0;
 #endif
-        if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
-          s = sockbunch[i];
+          ufd->fd = sockbunch[i];
+          ufd->events = 0;
+          if(bitmap & GETSOCK_READSOCK(i)) {
 #ifdef USE_WINSOCK
-          mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
+            mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
 #endif
-          ufds[nfds].fd = s;
-          ufds[nfds].events = POLLIN;
-          ++nfds;
+            ufd->events |= POLLIN;
+          }
+          if(bitmap & GETSOCK_WRITESOCK(i)) {
+#ifdef USE_WINSOCK
+            mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
+            reset_socket_fdwrite(sockbunch[i]);
+#endif
+            ufd->events |= POLLOUT;
+          }
+#ifdef USE_WINSOCK
+          if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) {
+            if(ufds_malloc)
+              free(ufds);
+            return CURLM_INTERNAL_ERROR;
+          }
+#endif
         }
-        if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
-          s = sockbunch[i];
-#ifdef USE_WINSOCK
-          mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
-          reset_socket_fdwrite(s);
-#endif
-          ufds[nfds].fd = s;
-          ufds[nfds].events = POLLOUT;
-          ++nfds;
-        }
-        /* s is only set if either being readable or writable is checked */
-        if(s == CURL_SOCKET_BAD) {
-          /* break on entry not checked for being readable or writable */
+        else {
           break;
         }
-#ifdef USE_WINSOCK
-        if(WSAEventSelect(s, multi->wsa_event, mask) != 0) {
-          if(ufds_malloc)
-            free(ufds);
-          return CURLM_INTERNAL_ERROR;
-        }
-#endif
       }
-
-      data = data->next; /* check next handle */
     }
   }
 
@@ -1394,8 +1384,8 @@
       /* Count up all our own sockets that had activity,
          and remove them from the event. */
       if(curlfds) {
-        data = multi->easyp;
-        while(data) {
+
+        for(data = multi->easyp; data; data = data->next) {
           bitmap = multi_getsock(data, sockbunch);
 
           for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
@@ -1412,8 +1402,6 @@
               break;
             }
           }
-
-          data = data->next;
         }
       }
 
@@ -1559,6 +1547,18 @@
   return retval;
 }
 
+/*
+ * Curl_multi_connchanged() is called to tell that there is a connection in
+ * this multi handle that has changed state (multiplexing become possible, the
+ * number of allowed streams changed or similar), and a subsequent use of this
+ * multi handle should move CONNECT_PEND handles back to CONNECT to have them
+ * retry.
+ */
+void Curl_multi_connchanged(struct Curl_multi *multi)
+{
+  multi->recheckstate = TRUE;
+}
+
 CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
                                  struct Curl_easy *data,
                                  struct connectdata *conn)
@@ -1593,7 +1593,6 @@
   DEBUGASSERT(conn->handler);
 
   if(conn->handler->do_it)
-    /* generic protocol-specific function pointer set in curl_connect() */
     result = conn->handler->do_it(data, done);
 
   return result;
@@ -1769,9 +1768,8 @@
  */
 static CURLcode readrewind(struct Curl_easy *data)
 {
-  struct connectdata *conn = data->conn;
   curl_mimepart *mimepart = &data->set.mimepost;
-  DEBUGASSERT(conn);
+  DEBUGASSERT(data->conn);
 
   data->state.rewindbeforesend = FALSE; /* we rewind now */
 
@@ -1784,12 +1782,12 @@
   /* We have sent away data. If not using CURLOPT_POSTFIELDS or
      CURLOPT_HTTPPOST, call app to rewind
   */
-  if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
-    struct HTTP *http = data->req.p.http;
-
-    if(http->sendit)
-      mimepart = http->sendit;
+#ifndef CURL_DISABLE_HTTP
+  if(data->conn->handler->protocol & PROTO_FAMILY_HTTP) {
+    if(data->state.mimepost)
+      mimepart = data->state.mimepost;
   }
+#endif
   if(data->set.postfields ||
      (data->state.httpreq == HTTPREQ_GET) ||
      (data->state.httpreq == HTTPREQ_HEAD))
@@ -1895,14 +1893,7 @@
     multistate(data, MSTATE_COMPLETED);
   }
 
-#ifdef DEBUGBUILD
-  if(!multi->warned) {
-    infof(data, "!!! WARNING !!!");
-    infof(data, "This is a debug build of libcurl, "
-          "do not use in production.");
-    multi->warned = true;
-  }
-#endif
+  multi_warn_debug(multi, data);
 
   do {
     /* A "stream" here is a logical stream if the protocol can handle that
@@ -2134,9 +2125,11 @@
       }
       if(!result)
         result = protocol_connect(data, &protocol_connected);
-      if(!result && !protocol_connected)
+      if(!result && !protocol_connected) {
         /* switch to waiting state */
         multistate(data, MSTATE_PROTOCONNECTING);
+        rc = CURLM_CALL_MULTI_PERFORM;
+      }
       else if(!result) {
         /* protocol connect has completed, go WAITDO or DO */
         multistate(data, MSTATE_DO);
@@ -2223,6 +2216,7 @@
             /* DO was not completed in one function call, we must continue
                DOING... */
             multistate(data, MSTATE_DOING);
+            rc = CURLM_CALL_MULTI_PERFORM;
           }
 
           /* after DO, go DO_DONE... or DO_MORE */
@@ -2230,6 +2224,7 @@
             /* we're supposed to do more, but we need to sit down, relax
                and wait a little while first */
             multistate(data, MSTATE_DOING_MORE);
+            rc = CURLM_CALL_MULTI_PERFORM;
           }
           else {
             /* we're done with the DO, now DID */
@@ -2449,7 +2444,7 @@
 
       if(done || (result == CURLE_RECV_ERROR)) {
         /* If CURLE_RECV_ERROR happens early enough, we assume it was a race
-         * condition and the server closed the re-used connection exactly when
+         * condition and the server closed the reused connection exactly when
          * we wanted to use it, so figure out if that is indeed the case.
          */
         CURLcode ret = Curl_retry_request(data, &newurl);
@@ -2491,7 +2486,7 @@
       if(result) {
         /*
          * The transfer phase returned error, we mark the connection to get
-         * closed to prevent being re-used. This is because we can't possibly
+         * closed to prevent being reused. This is because we can't possibly
          * know if the connection is in a good shape or not now.  Unless it is
          * a protocol which uses two "channels" like FTP, as then the error
          * happened in the data connection.
@@ -2922,7 +2917,7 @@
 
   /* walk over the sockets we got right now */
   for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) &&
-        (curraction & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i)));
+      (curraction & GETSOCK_MASK_RW(i));
       i++) {
     unsigned char action = CURL_POLL_NONE;
     unsigned char prevaction = 0;
@@ -3142,7 +3137,7 @@
     struct Curl_llist_element *n = e->next;
     timediff_t diff;
     node = (struct time_node *)e->ptr;
-    diff = Curl_timediff(node->time, now);
+    diff = Curl_timediff_us(node->time, now);
     if(diff <= 0)
       /* remove outdated entry */
       Curl_llist_remove(list, e, NULL);
@@ -3425,20 +3420,10 @@
 
     if(Curl_splaycomparekeys(multi->timetree->key, now) > 0) {
       /* some time left before expiration */
-      timediff_t diff = Curl_timediff(multi->timetree->key, now);
-      if(diff <= 0)
-        /*
-         * Since we only provide millisecond resolution on the returned value
-         * and the diff might be less than one millisecond here, we don't
-         * return zero as that may cause short bursts of busyloops on fast
-         * processors while the diff is still present but less than one
-         * millisecond! instead we return 1 until the time is ripe.
-         */
-        *timeout_ms = 1;
-      else
-        /* this should be safe even on 64 bit archs, as we don't use that
-           overly long timeouts */
-        *timeout_ms = (long)diff;
+      timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now);
+      /* this should be safe even on 32 bit archs, as we don't use that
+         overly long timeouts */
+      *timeout_ms = (long)diff;
     }
     else
       /* 0 means immediately */
@@ -3690,7 +3675,7 @@
     }
 
 #ifdef DEBUGBUILD
-    infof(data, "Expire cleared (transfer %p)", data);
+    infof(data, "Expire cleared");
 #endif
     nowp->tv_sec = 0;
     nowp->tv_usec = 0;
@@ -3786,41 +3771,26 @@
           (easy->multi_easy && easy->multi_easy->in_callback));
 }
 
-#ifdef DEBUGBUILD
-void Curl_multi_dump(struct Curl_multi *multi)
-{
-  struct Curl_easy *data;
-  int i;
-  fprintf(stderr, "* Multi status: %d handles, %d alive\n",
-          multi->num_easy, multi->num_alive);
-  for(data = multi->easyp; data; data = data->next) {
-    if(data->mstate < MSTATE_COMPLETED) {
-      /* only display handles that are not completed */
-      fprintf(stderr, "handle %p, state %s, %d sockets\n",
-              (void *)data,
-              statename[data->mstate], data->numsocks);
-      for(i = 0; i < data->numsocks; i++) {
-        curl_socket_t s = data->sockets[i];
-        struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
-
-        fprintf(stderr, "%d ", (int)s);
-        if(!entry) {
-          fprintf(stderr, "INTERNAL CONFUSION\n");
-          continue;
-        }
-        fprintf(stderr, "[%s %s] ",
-                (entry->action&CURL_POLL_IN)?"RECVING":"",
-                (entry->action&CURL_POLL_OUT)?"SENDING":"");
-      }
-      if(data->numsocks)
-        fprintf(stderr, "\n");
-    }
-  }
-}
-#endif
-
 unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
 {
   DEBUGASSERT(multi);
   return multi->max_concurrent_streams;
 }
+
+struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi)
+{
+  struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) *
+                                (multi->num_easy + 1));
+  if(a) {
+    int i = 0;
+    struct Curl_easy *e = multi->easyp;
+    while(e) {
+      DEBUGASSERT(i < multi->num_easy);
+      if(!e->internal)
+        a[i++] = e;
+      e = e->next;
+    }
+    a[i] = NULL; /* last entry is a NULL */
+  }
+  return a;
+}
diff --git a/Utilities/cmcurl/lib/multiif.h b/Utilities/cmcurl/lib/multiif.h
index cae02cb..7a344fa 100644
--- a/Utilities/cmcurl/lib/multiif.h
+++ b/Utilities/cmcurl/lib/multiif.h
@@ -41,6 +41,8 @@
 bool Curl_is_in_callback(struct Curl_easy *easy);
 CURLcode Curl_preconnect(struct Curl_easy *data);
 
+void Curl_multi_connchanged(struct Curl_multi *multi);
+
 /* Internal version of curl_multi_init() accepts size parameters for the
    socket, connection and dns hashes */
 struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize,
@@ -57,14 +59,8 @@
 /* set the bit for the given sock number to make the bitmap for readable */
 #define GETSOCK_READSOCK(x) (1 << (x))
 
-#ifdef DEBUGBUILD
- /*
-  * Curl_multi_dump is not a stable public function, this is only meant to
-  * allow easier tracking of the internal handle's state and what sockets
-  * they use. Only for research and development DEBUGBUILD enabled builds.
-  */
-void Curl_multi_dump(struct Curl_multi *multi);
-#endif
+/* mask for checking if read and/or write is set for index x */
+#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x))
 
 /* Return the value of the CURLMOPT_MAX_HOST_CONNECTIONS option */
 size_t Curl_multi_max_host_connections(struct Curl_multi *multi);
diff --git a/Utilities/cmcurl/lib/openldap.c b/Utilities/cmcurl/lib/openldap.c
index 41fecf9..3aff306 100644
--- a/Utilities/cmcurl/lib/openldap.c
+++ b/Utilities/cmcurl/lib/openldap.c
@@ -199,11 +199,11 @@
 };
 
 /*
- * state()
+ * oldap_state()
  *
  * This is the ONLY way to change LDAP state!
  */
-static void state(struct Curl_easy *data, ldapstate newstate)
+static void oldap_state(struct Curl_easy *data, ldapstate newstate)
 {
   struct ldapconninfo *ldapc = data->conn->proto.ldapc;
 
@@ -444,7 +444,7 @@
   rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
                       NULL, NULL, &li->msgid);
   if(rc == LDAP_SUCCESS)
-    state(data, newstate);
+    oldap_state(data, newstate);
   else
     result = oldap_map_error(rc,
                              data->state.aptr.user?
@@ -467,7 +467,7 @@
                        (char **) supportedSASLMechanisms, 0,
                        NULL, NULL, NULL, 0, &li->msgid);
   if(rc == LDAP_SUCCESS)
-    state(data, OLDAP_MECHS);
+    oldap_state(data, OLDAP_MECHS);
   else
     result = oldap_map_error(rc, CURLE_LOGIN_DENIED);
   return result;
@@ -480,7 +480,7 @@
   struct ldapconninfo *li = data->conn->proto.ldapc;
   CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
 
-  state(data, OLDAP_SASL);
+  oldap_state(data, OLDAP_SASL);
   if(!result && progress != SASL_INPROGRESS)
     result = CURLE_LOGIN_DENIED;
   return result;
@@ -503,7 +503,7 @@
 
   result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
   if(!result) {
-    state(data, newstate);
+    oldap_state(data, newstate);
 
     if(ssldone) {
       Sockbuf *sb;
@@ -527,7 +527,7 @@
   int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
 
   if(rc == LDAP_SUCCESS)
-    state(data, OLDAP_STARTTLS);
+    oldap_state(data, OLDAP_STARTTLS);
   else
     result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
   return result;
@@ -682,7 +682,7 @@
   else {
     result = Curl_sasl_continue(&li->sasl, data, code, &progress);
     if(!result && progress != SASL_INPROGRESS)
-      state(data, OLDAP_STOP);
+      oldap_state(data, OLDAP_STOP);
   }
 
   if(li->servercred)
@@ -710,7 +710,7 @@
     result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
   }
   else
-    state(data, OLDAP_STOP);
+    oldap_state(data, OLDAP_STOP);
 
   if(bv)
     ber_bvfree(bv);
@@ -804,7 +804,8 @@
       else if(data->state.aptr.user)
         result = oldap_perform_bind(data, OLDAP_BIND);
       else {
-        state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
+        /* Version 3 supported: no bind required */
+        oldap_state(data, OLDAP_STOP);
         result = CURLE_OK;
       }
     }
diff --git a/Utilities/cmcurl/lib/pingpong.c b/Utilities/cmcurl/lib/pingpong.c
index f3f7cb9..0081c9c 100644
--- a/Utilities/cmcurl/lib/pingpong.c
+++ b/Utilities/cmcurl/lib/pingpong.c
@@ -204,8 +204,7 @@
 #ifdef HAVE_GSSAPI
   conn->data_prot = PROT_CMD;
 #endif
-  result = Curl_write(data, conn->sock[FIRSTSOCKET], s, write_len,
-                      &bytes_written);
+  result = Curl_nwrite(data, FIRSTSOCKET, s, write_len, &bytes_written);
   if(result)
     return result;
 #ifdef HAVE_GSSAPI
@@ -341,7 +340,7 @@
       ssize_t clipamount = 0;
       bool restart = FALSE;
 
-      data->req.headerbytecount += (long)gotbytes;
+      data->req.headerbytecount += (unsigned int)gotbytes;
 
       pp->nread_resp += gotbytes;
       for(i = 0; i < gotbytes; ptr++, i++) {
@@ -362,7 +361,7 @@
            * for "headers". The response lines can be seen as a kind of
            * headers.
            */
-          result = Curl_client_write(data, CLIENTWRITE_HEADER,
+          result = Curl_client_write(data, CLIENTWRITE_INFO,
                                      pp->linestart_resp, perline);
           if(result)
             return result;
@@ -467,11 +466,10 @@
                            struct pingpong *pp)
 {
   /* we have a piece of a command still left to send */
-  struct connectdata *conn = data->conn;
   ssize_t written;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  CURLcode result = Curl_write(data, sock, pp->sendthis + pp->sendsize -
-                               pp->sendleft, pp->sendleft, &written);
+  CURLcode result = Curl_nwrite(data, FIRSTSOCKET,
+                                pp->sendthis + pp->sendsize - pp->sendleft,
+                                pp->sendleft, &written);
   if(result)
     return result;
 
diff --git a/Utilities/cmcurl/lib/pop3.c b/Utilities/cmcurl/lib/pop3.c
index 0de34cc..a9d5fdd6 100644
--- a/Utilities/cmcurl/lib/pop3.c
+++ b/Utilities/cmcurl/lib/pop3.c
@@ -47,9 +47,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -282,11 +279,11 @@
 
 /***********************************************************************
  *
- * state()
+ * pop3_state()
  *
  * This is the ONLY way to change POP3 state!
  */
-static void state(struct Curl_easy *data, pop3state newstate)
+static void pop3_state(struct Curl_easy *data, pop3state newstate)
 {
   struct pop3_conn *pop3c = &data->conn->proto.pop3c;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -335,7 +332,7 @@
   result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA");
 
   if(!result)
-    state(data, POP3_CAPA);
+    pop3_state(data, POP3_CAPA);
 
   return result;
 }
@@ -353,7 +350,7 @@
   CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS");
 
   if(!result)
-    state(data, POP3_STARTTLS);
+    pop3_state(data, POP3_STARTTLS);
 
   return result;
 }
@@ -383,7 +380,7 @@
   if(!result) {
     pop3c->ssldone = ssldone;
     if(pop3c->state != POP3_UPGRADETLS)
-      state(data, POP3_UPGRADETLS);
+      pop3_state(data, POP3_UPGRADETLS);
 
     if(pop3c->ssldone) {
       pop3_to_pop3s(conn);
@@ -408,7 +405,7 @@
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!data->state.aptr.user) {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
     return result;
   }
@@ -417,12 +414,12 @@
   result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s",
                          conn->user ? conn->user : "");
   if(!result)
-    state(data, POP3_USER);
+    pop3_state(data, POP3_USER);
 
   return result;
 }
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
 /***********************************************************************
  *
  * pop3_perform_apop()
@@ -442,7 +439,7 @@
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!data->state.aptr.user) {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
     return result;
   }
@@ -468,7 +465,7 @@
   result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret);
 
   if(!result)
-    state(data, POP3_APOP);
+    pop3_state(data, POP3_APOP);
 
   return result;
 }
@@ -552,7 +549,7 @@
   /* Check we have enough data to authenticate with and end the
      connect phase if we don't */
   if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
     return result;
   }
 
@@ -562,11 +559,11 @@
 
     if(!result)
       if(progress == SASL_INPROGRESS)
-        state(data, POP3_AUTH);
+        pop3_state(data, POP3_AUTH);
   }
 
   if(!result && progress == SASL_IDLE) {
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
       /* Perform APOP authentication */
       result = pop3_perform_apop(data, conn);
@@ -620,7 +617,7 @@
                             pop3->custom : command));
 
   if(!result)
-    state(data, POP3_COMMAND);
+    pop3_state(data, POP3_COMMAND);
 
   return result;
 }
@@ -638,7 +635,7 @@
   CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT");
 
   if(!result)
-    state(data, POP3_QUIT);
+    pop3_state(data, POP3_QUIT);
 
   return result;
 }
@@ -831,10 +828,10 @@
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      state(data, POP3_STOP);  /* Authenticated */
+      pop3_state(data, POP3_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
       if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_APOP)
         /* Perform APOP authentication */
         result = pop3_perform_apop(data, conn);
@@ -855,7 +852,7 @@
   return result;
 }
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
 /* For APOP responses */
 static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
                                      pop3state instate)
@@ -869,7 +866,7 @@
   }
   else
     /* End of connect phase */
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
   return result;
 }
@@ -892,7 +889,7 @@
     result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
                            conn->passwd ? conn->passwd : "");
   if(!result)
-    state(data, POP3_PASS);
+    pop3_state(data, POP3_PASS);
 
   return result;
 }
@@ -910,7 +907,7 @@
   }
   else
     /* End of connect phase */
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
   return result;
 }
@@ -929,7 +926,7 @@
   (void)instate; /* no use for this yet */
 
   if(pop3code != '+') {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
     return CURLE_WEIRD_SERVER_REPLY;
   }
 
@@ -967,7 +964,7 @@
   }
 
   /* End of DO phase */
-  state(data, POP3_STOP);
+  pop3_state(data, POP3_STOP);
 
   return result;
 }
@@ -1018,7 +1015,7 @@
       result = pop3_state_auth_resp(data, pop3code, pop3c->state);
       break;
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
     case POP3_APOP:
       result = pop3_state_apop_resp(data, pop3code, pop3c->state);
       break;
@@ -1037,12 +1034,12 @@
       break;
 
     case POP3_QUIT:
-      state(data, POP3_STOP);
+      pop3_state(data, POP3_STOP);
       break;
 
     default:
       /* internal error */
-      state(data, POP3_STOP);
+      pop3_state(data, POP3_STOP);
       break;
     }
   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
@@ -1143,7 +1140,7 @@
     return result;
 
   /* Start off waiting for the server greeting response */
-  state(data, POP3_SERVERGREET);
+  pop3_state(data, POP3_SERVERGREET);
 
   result = pop3_multi_statemach(data, done);
 
diff --git a/Utilities/cmcurl/lib/progress.c b/Utilities/cmcurl/lib/progress.c
index 6092b78..e783a9c 100644
--- a/Utilities/cmcurl/lib/progress.c
+++ b/Utilities/cmcurl/lib/progress.c
@@ -317,9 +317,16 @@
 /*
  * Set the number of downloaded bytes so far.
  */
-void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
+CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
 {
+  if(data->set.max_filesize && (size > data->set.max_filesize)) {
+    failf(data, "Exceeded the maximum allowed file size "
+          "(%" CURL_FORMAT_CURL_OFF_T ")",
+          data->set.max_filesize);
+    return CURLE_FILESIZE_EXCEEDED;
+  }
   data->progress.downloaded = size;
+  return CURLE_OK;
 }
 
 /*
diff --git a/Utilities/cmcurl/lib/progress.h b/Utilities/cmcurl/lib/progress.h
index 0049cd0..fc39e34 100644
--- a/Utilities/cmcurl/lib/progress.h
+++ b/Utilities/cmcurl/lib/progress.h
@@ -46,7 +46,10 @@
 void Curl_pgrsStartNow(struct Curl_easy *data);
 void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size);
 void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size);
-void Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size);
+
+/* It is fine to not check the return code if 'size' is set to 0 */
+CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size);
+
 void Curl_pgrsSetUploadCounter(struct Curl_easy *data, curl_off_t size);
 void Curl_ratelimit(struct Curl_easy *data, struct curltime now);
 int Curl_pgrsUpdate(struct Curl_easy *data);
diff --git a/Utilities/cmcurl/lib/rand.c b/Utilities/cmcurl/lib/rand.c
index 7d24765..6bd9613 100644
--- a/Utilities/cmcurl/lib/rand.c
+++ b/Utilities/cmcurl/lib/rand.c
@@ -24,6 +24,8 @@
 
 #include "curl_setup.h"
 
+#include <limits.h>
+
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
@@ -36,10 +38,12 @@
 #endif
 
 #include <curl/curl.h>
+#include "urldata.h"
 #include "vtls/vtls.h"
 #include "sendf.h"
 #include "timeval.h"
 #include "rand.h"
+#include "escape.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -48,12 +52,7 @@
 
 #ifdef WIN32
 
-#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
-#  define HAVE_MINGW_ORIGINAL
-#endif
-
-#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 && \
-  !defined(HAVE_MINGW_ORIGINAL)
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
 #  define HAVE_WIN_BCRYPTGENRANDOM
 #  include <bcrypt.h>
 #  ifdef _MSC_VER
@@ -187,7 +186,7 @@
  * 'rnd' points to.
  *
  * If libcurl is built without TLS support or with a TLS backend that lacks a
- * proper random API (rustls, Gskit or mbedTLS), this function will use "weak"
+ * proper random API (rustls or mbedTLS), this function will use "weak"
  * random.
  *
  * When built *with* TLS support and a backend that offers strong random, it
@@ -233,9 +232,7 @@
                        size_t num)
 {
   CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
-  const char *hex = "0123456789abcdef";
   unsigned char buffer[128];
-  unsigned char *bufp = buffer;
   DEBUGASSERT(num > 1);
 
 #ifdef __clang_analyzer__
@@ -254,13 +251,37 @@
   if(result)
     return result;
 
+  Curl_hexencode(buffer, num/2, rnd, num + 1);
+  return result;
+}
+
+/*
+ * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random
+ * alphanumerical chars PLUS a null-terminating byte.
+ */
+
+static const char alnum[] =
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+
+CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
+                         size_t num)
+{
+  CURLcode result = CURLE_OK;
+  const int alnumspace = sizeof(alnum) - 1;
+  unsigned int r;
+  DEBUGASSERT(num > 1);
+
+  num--; /* save one for null-termination */
+
   while(num) {
-    /* clang-tidy warns on this line without this comment: */
-    /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
-    *rnd++ = hex[(*bufp & 0xF0)>>4];
-    *rnd++ = hex[*bufp & 0x0F];
-    bufp++;
-    num -= 2;
+    do {
+      result = randit(data, &r);
+      if(result)
+        return result;
+    } while(r >= (UINT_MAX - UINT_MAX % alnumspace));
+
+    *rnd++ = alnum[r % alnumspace];
+    num--;
   }
   *rnd = 0;
 
diff --git a/Utilities/cmcurl/lib/rand.h b/Utilities/cmcurl/lib/rand.h
index cbe0567..1d009f5 100644
--- a/Utilities/cmcurl/lib/rand.h
+++ b/Utilities/cmcurl/lib/rand.h
@@ -24,20 +24,6 @@
  *
  ***************************************************************************/
 
-/*
- * Curl_rand() stores 'num' number of random unsigned characters in the buffer
- * 'rnd' points to.
- *
- * If libcurl is built without TLS support or with a TLS backend that lacks a
- * proper random API (Gskit or mbedTLS), this function will use "weak" random.
- *
- * When built *with* TLS support and a backend that offers strong random, it
- * will return error if it cannot provide strong random values.
- *
- * NOTE: 'data' may be passed in as NULL when coming from external API without
- * easy handle!
- *
- */
 CURLcode Curl_rand(struct Curl_easy *data, unsigned char *rnd, size_t num);
 
 /*
@@ -48,6 +34,13 @@
 CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
                        size_t num);
 
+/*
+ * Curl_rand_alnum() fills the 'rnd' buffer with a given 'num' size with random
+ * alphanumerical chars PLUS a null-terminating byte.
+ */
+CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
+                         size_t num);
+
 #ifdef WIN32
 /* Random generator shared between the Schannel vtls and Curl_rand*()
    functions */
diff --git a/Utilities/cmcurl/lib/sendf.c b/Utilities/cmcurl/lib/sendf.c
index 81ee864..0482c5d 100644
--- a/Utilities/cmcurl/lib/sendf.c
+++ b/Utilities/cmcurl/lib/sendf.c
@@ -40,6 +40,7 @@
 #include "sendf.h"
 #include "cfilters.h"
 #include "connect.h"
+#include "content_encoding.h"
 #include "vtls/vtls.h"
 #include "vssh/ssh.h"
 #include "easyif.h"
@@ -139,6 +140,56 @@
 #endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
 
 /*
+ * Curl_nwrite() is an internal write function that sends data to the
+ * server. Works with a socket index for the connection.
+ *
+ * If the write would block (CURLE_AGAIN), it returns CURLE_OK and
+ * (*nwritten == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_nwrite(struct Curl_easy *data,
+                     int sockindex,
+                     const void *buf,
+                     size_t blen,
+                     ssize_t *pnwritten)
+{
+  ssize_t nwritten;
+  CURLcode result = CURLE_OK;
+  struct connectdata *conn;
+
+  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
+  DEBUGASSERT(pnwritten);
+  DEBUGASSERT(data);
+  DEBUGASSERT(data->conn);
+  conn = data->conn;
+#ifdef CURLDEBUG
+  {
+    /* Allow debug builds to override this logic to force short sends
+    */
+    char *p = getenv("CURL_SMALLSENDS");
+    if(p) {
+      size_t altsize = (size_t)strtoul(p, NULL, 10);
+      if(altsize)
+        blen = CURLMIN(blen, altsize);
+    }
+  }
+#endif
+  nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
+  if(result == CURLE_AGAIN) {
+    nwritten = 0;
+    result = CURLE_OK;
+  }
+  else if(result) {
+    nwritten = -1; /* make sure */
+  }
+  else {
+    DEBUGASSERT(nwritten >= 0);
+  }
+
+  *pnwritten = nwritten;
+  return result;
+}
+
+/*
  * Curl_write() is an internal write function that sends data to the
  * server. Works with plain sockets, SCP, SSL or kerberos.
  *
@@ -151,52 +202,19 @@
                     size_t len,
                     ssize_t *written)
 {
-  ssize_t bytes_written;
-  CURLcode result = CURLE_OK;
   struct connectdata *conn;
   int num;
+
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   conn = data->conn;
   num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
-
-#ifdef CURLDEBUG
-  {
-    /* Allow debug builds to override this logic to force short sends
-    */
-    char *p = getenv("CURL_SMALLSENDS");
-    if(p) {
-      size_t altsize = (size_t)strtoul(p, NULL, 10);
-      if(altsize)
-        len = CURLMIN(len, altsize);
-    }
-  }
-#endif
-  bytes_written = conn->send[num](data, num, mem, len, &result);
-
-  *written = bytes_written;
-  if(bytes_written >= 0)
-    /* we completely ignore the curlcode value when subzero is not returned */
-    return CURLE_OK;
-
-  /* handle CURLE_AGAIN or a send failure */
-  switch(result) {
-  case CURLE_AGAIN:
-    *written = 0;
-    return CURLE_OK;
-
-  case CURLE_OK:
-    /* general send failure */
-    return CURLE_SEND_ERROR;
-
-  default:
-    /* we got a specific curlcode, forward it */
-    return result;
-  }
+  return Curl_nwrite(data, num, mem, len, written);
 }
 
 static CURLcode pausewrite(struct Curl_easy *data,
                            int type, /* what type of data */
+                           bool paused_body,
                            const char *ptr,
                            size_t len)
 {
@@ -212,7 +230,8 @@
 
   if(s->tempcount) {
     for(i = 0; i< s->tempcount; i++) {
-      if(s->tempwrite[i].type == type) {
+      if(s->tempwrite[i].type == type &&
+         !!s->tempwrite[i].paused_body == !!paused_body) {
         /* data for this type exists */
         newtype = FALSE;
         break;
@@ -230,6 +249,7 @@
     /* store this information in the state struct for later use */
     Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER);
     s->tempwrite[i].type = type;
+    s->tempwrite[i].paused_body = paused_body;
     s->tempcount++;
   }
 
@@ -249,6 +269,7 @@
  */
 static CURLcode chop_write(struct Curl_easy *data,
                            int type,
+                           bool skip_body_write,
                            char *optr,
                            size_t olen)
 {
@@ -265,10 +286,12 @@
   /* If reading is paused, append this data to the already held data for this
      type. */
   if(data->req.keepon & KEEP_RECV_PAUSE)
-    return pausewrite(data, type, ptr, len);
+    return pausewrite(data, type, !skip_body_write, ptr, len);
 
   /* Determine the callback(s) to use. */
-  if(type & CLIENTWRITE_BODY) {
+  if(!skip_body_write &&
+     ((type & CLIENTWRITE_BODY) ||
+      ((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
 #ifdef USE_WEBSOCKETS
     if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
       writebody = Curl_ws_writecb;
@@ -278,7 +301,7 @@
 #endif
     writebody = data->set.fwrite_func;
   }
-  if((type & CLIENTWRITE_HEADER) &&
+  if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
      (data->set.fwrite_header || data->set.writeheader)) {
     /*
      * Write headers to the same callback or to the especially setup
@@ -306,7 +329,7 @@
           failf(data, "Write callback asked for PAUSE when not supported");
           return CURLE_WRITE_ERROR;
         }
-        return pausewrite(data, type, ptr, len);
+        return pausewrite(data, type, TRUE, ptr, len);
       }
       if(wrote != chunklen) {
         failf(data, "Failure writing output to destination");
@@ -341,13 +364,7 @@
     Curl_set_in_callback(data, false);
 
     if(CURL_WRITEFUNC_PAUSE == wrote)
-      /* here we pass in the HEADER bit only since if this was body as well
-         then it was passed already and clearly that didn't trigger the
-         pause, so this is saved for later with the HEADER bit only */
-      return pausewrite(data, CLIENTWRITE_HEADER |
-                        (type & (CLIENTWRITE_STATUS|CLIENTWRITE_CONNECT|
-                                 CLIENTWRITE_1XX|CLIENTWRITE_TRAILER)),
-                        optr, olen);
+      return pausewrite(data, type, FALSE, optr, olen);
     if(wrote != olen) {
       failf(data, "Failed writing header");
       return CURLE_WRITE_ERROR;
@@ -381,9 +398,187 @@
     len = convert_lineends(data, ptr, len);
   }
 #endif
-  return chop_write(data, type, ptr, len);
+  /* it is one of those, at least */
+  DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
+  /* BODY is only BODY */
+  DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY));
+  /* INFO is only INFO */
+  DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
+
+  if(type == CLIENTWRITE_BODY) {
+    if(data->req.ignorebody)
+      return CURLE_OK;
+
+    if(data->req.writer_stack && !data->set.http_ce_skip)
+      return Curl_unencode_write(data, data->req.writer_stack, ptr, len);
+  }
+  return chop_write(data, type, FALSE, ptr, len);
 }
 
+CURLcode Curl_client_unpause(struct Curl_easy *data)
+{
+  CURLcode result = CURLE_OK;
+
+  if(data->state.tempcount) {
+    /* there are buffers for sending that can be delivered as the receive
+       pausing is lifted! */
+    unsigned int i;
+    unsigned int count = data->state.tempcount;
+    struct tempbuf writebuf[3]; /* there can only be three */
+
+    /* copy the structs to allow for immediate re-pausing */
+    for(i = 0; i < data->state.tempcount; i++) {
+      writebuf[i] = data->state.tempwrite[i];
+      Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
+    }
+    data->state.tempcount = 0;
+
+    for(i = 0; i < count; i++) {
+      /* even if one function returns error, this loops through and frees
+         all buffers */
+      if(!result)
+        result = chop_write(data, writebuf[i].type,
+                            !writebuf[i].paused_body,
+                            Curl_dyn_ptr(&writebuf[i].b),
+                            Curl_dyn_len(&writebuf[i].b));
+      Curl_dyn_free(&writebuf[i].b);
+    }
+  }
+  return result;
+}
+
+void Curl_client_cleanup(struct Curl_easy *data)
+{
+  struct contenc_writer *writer = data->req.writer_stack;
+  size_t i;
+
+  while(writer) {
+    data->req.writer_stack = writer->downstream;
+    writer->handler->close_writer(data, writer);
+    free(writer);
+    writer = data->req.writer_stack;
+  }
+
+  for(i = 0; i < data->state.tempcount; i++) {
+    Curl_dyn_free(&data->state.tempwrite[i].b);
+  }
+  data->state.tempcount = 0;
+
+}
+
+/* Real client writer: no downstream. */
+static CURLcode client_cew_init(struct Curl_easy *data,
+                                struct contenc_writer *writer)
+{
+  (void) data;
+  (void)writer;
+  return CURLE_OK;
+}
+
+static CURLcode client_cew_write(struct Curl_easy *data,
+                                 struct contenc_writer *writer,
+                                 const char *buf, size_t nbytes)
+{
+  (void)writer;
+  if(!nbytes || data->req.ignorebody)
+    return CURLE_OK;
+  return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes);
+}
+
+static void client_cew_close(struct Curl_easy *data,
+                             struct contenc_writer *writer)
+{
+  (void) data;
+  (void) writer;
+}
+
+static const struct content_encoding client_cew = {
+  NULL,
+  NULL,
+  client_cew_init,
+  client_cew_write,
+  client_cew_close,
+  sizeof(struct contenc_writer)
+};
+
+/* Create an unencoding writer stage using the given handler. */
+CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
+                                   struct Curl_easy *data,
+                                   const struct content_encoding *ce_handler,
+                                   int order)
+{
+  struct contenc_writer *writer;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+
+  DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer));
+  writer = (struct contenc_writer *) calloc(1, ce_handler->writersize);
+  if(!writer)
+    goto out;
+
+  writer->handler = ce_handler;
+  writer->order = order;
+  result = ce_handler->init_writer(data, writer);
+
+out:
+  *pwriter = result? NULL : writer;
+  if(result)
+    free(writer);
+  return result;
+}
+
+void Curl_client_free_writer(struct Curl_easy *data,
+                             struct contenc_writer *writer)
+{
+  if(writer) {
+    writer->handler->close_writer(data, writer);
+    free(writer);
+  }
+}
+
+/* allow no more than 5 "chained" compression steps */
+#define MAX_ENCODE_STACK 5
+
+
+static CURLcode init_writer_stack(struct Curl_easy *data)
+{
+  DEBUGASSERT(!data->req.writer_stack);
+  return Curl_client_create_writer(&data->req.writer_stack,
+                                   data, &client_cew, 0);
+}
+
+CURLcode Curl_client_add_writer(struct Curl_easy *data,
+                                struct contenc_writer *writer)
+{
+  CURLcode result;
+
+  if(!data->req.writer_stack) {
+    result = init_writer_stack(data);
+    if(result)
+      return result;
+  }
+
+  if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) {
+    failf(data, "Reject response due to more than %u content encodings",
+          MAX_ENCODE_STACK);
+    return CURLE_BAD_CONTENT_ENCODING;
+  }
+
+  /* Stack the unencoding stage. */
+  if(writer->order >= data->req.writer_stack->order) {
+    writer->downstream = data->req.writer_stack;
+    data->req.writer_stack = writer;
+  }
+  else {
+    struct contenc_writer *w = data->req.writer_stack;
+    while(w->downstream && writer->order < w->downstream->order)
+      w = w->downstream;
+    writer->downstream = w->downstream;
+    w->downstream = writer;
+  }
+  return CURLE_OK;
+}
+
+
 /*
  * Internal read-from-socket function. This is meant to deal with plain
  * sockets, SSL sockets and kerberos sockets.
@@ -419,8 +614,5 @@
   *n += nread;
   result = CURLE_OK;
 out:
-  /* DEBUGF(infof(data, "Curl_read(handle=%p) -> %d, nread=%ld",
-        data, result, nread)); */
   return result;
 }
-
diff --git a/Utilities/cmcurl/lib/sendf.h b/Utilities/cmcurl/lib/sendf.h
index d0c9275..9ee00bb 100644
--- a/Utilities/cmcurl/lib/sendf.h
+++ b/Utilities/cmcurl/lib/sendf.h
@@ -26,20 +26,69 @@
 
 #include "curl_setup.h"
 
-#include "curl_log.h"
+#include "curl_trc.h"
 
-
-#define CLIENTWRITE_BODY    (1<<0)
-#define CLIENTWRITE_HEADER  (1<<1)
-#define CLIENTWRITE_STATUS  (1<<2) /* the first "header" is the status line */
-#define CLIENTWRITE_CONNECT (1<<3) /* a CONNECT response */
-#define CLIENTWRITE_1XX     (1<<4) /* a 1xx response */
-#define CLIENTWRITE_TRAILER (1<<5) /* a trailer header */
-#define CLIENTWRITE_BOTH   (CLIENTWRITE_BODY|CLIENTWRITE_HEADER)
+/**
+ * Type of data that is being written to the client (application)
+ * - data written can be either BODY or META data
+ * - META data is either INFO or HEADER
+ * - INFO is meta information, e.g. not BODY, that cannot be interpreted
+ *   as headers of a response. Example FTP/IMAP pingpong answers.
+ * - HEADER can have additional bits set (more than one)
+ *   - STATUS special "header", e.g. response status line in HTTP
+ *   - CONNECT header was received during proxying the connection
+ *   - 1XX header is part of an intermediate response, e.g. HTTP 1xx code
+ *   - TRAILER header is trailing response data, e.g. HTTP trailers
+ * BODY, INFO and HEADER should not be mixed, as this would lead to
+ * confusion on how to interpret/format/convert the data.
+ */
+#define CLIENTWRITE_BODY    (1<<0) /* non-meta information, BODY */
+#define CLIENTWRITE_INFO    (1<<1) /* meta information, not a HEADER */
+#define CLIENTWRITE_HEADER  (1<<2) /* meta information, HEADER */
+#define CLIENTWRITE_STATUS  (1<<3) /* a special status HEADER */
+#define CLIENTWRITE_CONNECT (1<<4) /* a CONNECT related HEADER */
+#define CLIENTWRITE_1XX     (1<<5) /* a 1xx response related HEADER */
+#define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
 
 CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
                            size_t len) WARN_UNUSED_RESULT;
 
+CURLcode Curl_client_unpause(struct Curl_easy *data);
+void Curl_client_cleanup(struct Curl_easy *data);
+
+struct contenc_writer {
+  const struct content_encoding *handler;  /* Encoding handler. */
+  struct contenc_writer *downstream;  /* Downstream writer. */
+  unsigned int order; /* Ordering within writer stack. */
+};
+
+/* Content encoding writer. */
+struct content_encoding {
+  const char *name;        /* Encoding name. */
+  const char *alias;       /* Encoding name alias. */
+  CURLcode (*init_writer)(struct Curl_easy *data,
+                          struct contenc_writer *writer);
+  CURLcode (*unencode_write)(struct Curl_easy *data,
+                             struct contenc_writer *writer,
+                             const char *buf, size_t nbytes);
+  void (*close_writer)(struct Curl_easy *data,
+                       struct contenc_writer *writer);
+  size_t writersize;
+};
+
+
+CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
+                                   struct Curl_easy *data,
+                                   const struct content_encoding *ce_handler,
+                                   int order);
+
+void Curl_client_free_writer(struct Curl_easy *data,
+                             struct contenc_writer *writer);
+
+CURLcode Curl_client_add_writer(struct Curl_easy *data,
+                                struct contenc_writer *writer);
+
+
 /* internal read-function, does plain socket, SSL and krb4 */
 CURLcode Curl_read(struct Curl_easy *data, curl_socket_t sockfd,
                    char *buf, size_t buffersize,
@@ -51,4 +100,11 @@
                     const void *mem, size_t len,
                     ssize_t *written);
 
+/* internal write-function, using sockindex for connection destination */
+CURLcode Curl_nwrite(struct Curl_easy *data,
+                     int sockindex,
+                     const void *buf,
+                     size_t blen,
+                     ssize_t *pnwritten);
+
 #endif /* HEADER_CURL_SENDF_H */
diff --git a/Utilities/cmcurl/lib/setopt.c b/Utilities/cmcurl/lib/setopt.c
index 0c3b963..0d399ad 100644
--- a/Utilities/cmcurl/lib/setopt.c
+++ b/Utilities/cmcurl/lib/setopt.c
@@ -666,17 +666,20 @@
       data->set.method = HTTPREQ_GET;
     break;
 
-#ifndef CURL_DISABLE_MIME
+#ifndef CURL_DISABLE_FORM_API
   case CURLOPT_HTTPPOST:
     /*
-     * Set to make us do HTTP POST
+     * Set to make us do HTTP POST. Legacy API-style.
      */
     data->set.httppost = va_arg(param, struct curl_httppost *);
     data->set.method = HTTPREQ_POST_FORM;
     data->set.opt_no_body = FALSE; /* this is implied */
+    Curl_mime_cleanpart(data->state.formp);
+    Curl_safefree(data->state.formp);
     break;
 #endif
 
+#if !defined(CURL_DISABLE_AWS)
   case CURLOPT_AWS_SIGV4:
     /*
      * String that is merged to some authentication
@@ -690,6 +693,7 @@
     if(data->set.str[STRING_AWS_SIGV4])
       data->set.httpauth = CURLAUTH_AWS_SIGV4;
     break;
+#endif
 
   case CURLOPT_REFERER:
     /*
@@ -985,6 +989,10 @@
     if(!result) {
       data->set.method = HTTPREQ_POST_MIME;
       data->set.opt_no_body = FALSE; /* this is implied */
+#ifndef CURL_DISABLE_FORM_API
+      Curl_mime_cleanpart(data->state.formp);
+      Curl_safefree(data->state.formp);
+#endif
     }
     break;
 
@@ -1237,6 +1245,7 @@
     data->set.out = va_arg(param, void *);
     break;
 
+#ifdef CURL_LIST_ONLY_PROTOCOL
   case CURLOPT_DIRLISTONLY:
     /*
      * An option that changes the command to one that asks for a list only, no
@@ -1244,7 +1253,7 @@
      */
     data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
     break;
-
+#endif
   case CURLOPT_APPEND:
     /*
      * We want to upload and append to an existing file. Used for FTP and
@@ -1867,6 +1876,15 @@
      */
     data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
     break;
+  case CURLOPT_HAPROXY_CLIENT_IP:
+    /*
+     * Set the client IP to send through HAProxy PROXY protocol
+     */
+    result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP],
+                            va_arg(param, char *));
+    /* We enable implicitly the HAProxy protocol if we use this flag. */
+    data->set.haproxyprotocol = TRUE;
+    break;
 #endif
   case CURLOPT_INTERFACE:
     /*
@@ -1876,6 +1894,7 @@
     result = Curl_setstropt(&data->set.str[STRING_DEVICE],
                             va_arg(param, char *));
     break;
+#ifndef CURL_DISABLE_BINDLOCAL
   case CURLOPT_LOCALPORT:
     /*
      * Set what local port to bind the socket to when performing an operation.
@@ -1894,6 +1913,7 @@
       return CURLE_BAD_FUNCTION_ARGUMENT;
     data->set.localportrange = curlx_sltous(arg);
     break;
+#endif
   case CURLOPT_GSSAPI_DELEGATION:
     /*
      * GSS-API credential delegation bitmask
@@ -2056,7 +2076,7 @@
     else
 #endif
       result = CURLE_NOT_BUILT_IN;
-        break;
+    break;
   case CURLOPT_PINNEDPUBLICKEY:
     /*
      * Set pinned public key for SSL connection.
@@ -2711,7 +2731,7 @@
     /* Set the list of mail recipients */
     data->set.mail_rcpt = va_arg(param, struct curl_slist *);
     break;
-  case CURLOPT_MAIL_RCPT_ALLLOWFAILS:
+  case CURLOPT_MAIL_RCPT_ALLOWFAILS:
     /* allow RCPT TO command to fail for some recipients */
     data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE;
     break;
diff --git a/Utilities/cmcurl/lib/setup-os400.h b/Utilities/cmcurl/lib/setup-os400.h
index 7595834..53e9177 100644
--- a/Utilities/cmcurl/lib/setup-os400.h
+++ b/Utilities/cmcurl/lib/setup-os400.h
@@ -34,6 +34,9 @@
 /* No OS/400 header file defines u_int32_t. */
 typedef unsigned long   u_int32_t;
 
+/* OS/400 has no idea of a tty! */
+#define isatty(fd)      0
+
 
 /* System API wrapper prototypes & definitions to support ASCII parameters. */
 
@@ -57,94 +60,6 @@
                               int flags);
 #define getnameinfo             Curl_getnameinfo_a
 
-
-/* GSKit wrappers. */
-
-extern int      Curl_gsk_environment_open(gsk_handle * my_env_handle);
-#define gsk_environment_open    Curl_gsk_environment_open
-
-extern int      Curl_gsk_secure_soc_open(gsk_handle my_env_handle,
-                                         gsk_handle * my_session_handle);
-#define gsk_secure_soc_open     Curl_gsk_secure_soc_open
-
-extern int      Curl_gsk_environment_close(gsk_handle * my_env_handle);
-#define gsk_environment_close   Curl_gsk_environment_close
-
-extern int      Curl_gsk_secure_soc_close(gsk_handle * my_session_handle);
-#define gsk_secure_soc_close    Curl_gsk_secure_soc_close
-
-extern int      Curl_gsk_environment_init(gsk_handle my_env_handle);
-#define gsk_environment_init    Curl_gsk_environment_init
-
-extern int      Curl_gsk_secure_soc_init(gsk_handle my_session_handle);
-#define gsk_secure_soc_init     Curl_gsk_secure_soc_init
-
-extern int      Curl_gsk_attribute_set_buffer_a(gsk_handle my_gsk_handle,
-                                                GSK_BUF_ID bufID,
-                                                const char *buffer,
-                                                int bufSize);
-#define gsk_attribute_set_buffer        Curl_gsk_attribute_set_buffer_a
-
-extern int      Curl_gsk_attribute_set_enum(gsk_handle my_gsk_handle,
-                                            GSK_ENUM_ID enumID,
-                                            GSK_ENUM_VALUE enumValue);
-#define gsk_attribute_set_enum  Curl_gsk_attribute_set_enum
-
-extern int      Curl_gsk_attribute_set_numeric_value(gsk_handle my_gsk_handle,
-                                                     GSK_NUM_ID numID,
-                                                     int numValue);
-#define gsk_attribute_set_numeric_value Curl_gsk_attribute_set_numeric_value
-
-extern int      Curl_gsk_attribute_set_callback(gsk_handle my_gsk_handle,
-                                                GSK_CALLBACK_ID callBackID,
-                                                void *callBackAreaPtr);
-#define gsk_attribute_set_callback      Curl_gsk_attribute_set_callback
-
-extern int      Curl_gsk_attribute_get_buffer_a(gsk_handle my_gsk_handle,
-                                                GSK_BUF_ID bufID,
-                                                const char **buffer,
-                                                int *bufSize);
-#define gsk_attribute_get_buffer        Curl_gsk_attribute_get_buffer_a
-
-extern int      Curl_gsk_attribute_get_enum(gsk_handle my_gsk_handle,
-                                            GSK_ENUM_ID enumID,
-                                            GSK_ENUM_VALUE *enumValue);
-#define gsk_attribute_get_enum  Curl_gsk_attribute_get_enum
-
-extern int      Curl_gsk_attribute_get_numeric_value(gsk_handle my_gsk_handle,
-                                                     GSK_NUM_ID numID,
-                                                     int *numValue);
-#define gsk_attribute_get_numeric_value Curl_gsk_attribute_get_numeric_value
-
-extern int      Curl_gsk_attribute_get_cert_info(gsk_handle my_gsk_handle,
-                                 GSK_CERT_ID certID,
-                                 const gsk_cert_data_elem **certDataElem,
-                                 int *certDataElementCount);
-#define gsk_attribute_get_cert_info     Curl_gsk_attribute_get_cert_info
-
-extern int      Curl_gsk_secure_soc_misc(gsk_handle my_session_handle,
-                                         GSK_MISC_ID miscID);
-#define gsk_secure_soc_misc     Curl_gsk_secure_soc_misc
-
-extern int      Curl_gsk_secure_soc_read(gsk_handle my_session_handle,
-                                         char *readBuffer,
-                                         int readBufSize, int *amtRead);
-#define gsk_secure_soc_read     Curl_gsk_secure_soc_read
-
-extern int      Curl_gsk_secure_soc_write(gsk_handle my_session_handle,
-                                          char *writeBuffer,
-                                          int writeBufSize, int *amtWritten);
-#define gsk_secure_soc_write    Curl_gsk_secure_soc_write
-
-extern const char *     Curl_gsk_strerror_a(int gsk_return_value);
-#define gsk_strerror    Curl_gsk_strerror_a
-
-extern int      Curl_gsk_secure_soc_startInit(gsk_handle my_session_handle,
-                                      int IOCompletionPort,
-                                      Qso_OverlappedIO_t * communicationsArea);
-#define gsk_secure_soc_startInit        Curl_gsk_secure_soc_startInit
-
-
 /* GSSAPI wrappers. */
 
 extern OM_uint32 Curl_gss_import_name_a(OM_uint32 * minor_status,
diff --git a/Utilities/cmcurl/lib/setup-vms.h b/Utilities/cmcurl/lib/setup-vms.h
index 46657b2..645cc1a 100644
--- a/Utilities/cmcurl/lib/setup-vms.h
+++ b/Utilities/cmcurl/lib/setup-vms.h
@@ -262,7 +262,6 @@
 #define PKCS12_parse PKCS12_PARSE
 #define RAND_add RAND_ADD
 #define RAND_bytes RAND_BYTES
-#define RAND_egd RAND_EGD
 #define RAND_file_name RAND_FILE_NAME
 #define RAND_load_file RAND_LOAD_FILE
 #define RAND_status RAND_STATUS
diff --git a/Utilities/cmcurl/lib/sha256.c b/Utilities/cmcurl/lib/sha256.c
index 767d879..4a02045 100644
--- a/Utilities/cmcurl/lib/sha256.c
+++ b/Utilities/cmcurl/lib/sha256.c
@@ -25,7 +25,8 @@
 
 #include "curl_setup.h"
 
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
+    || defined(USE_LIBSSH2)
 
 #include "warnless.h"
 #include "curl_sha256.h"
@@ -110,7 +111,10 @@
   if(!ctx->openssl_ctx)
     return CURLE_OUT_OF_MEMORY;
 
-  EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL);
+  if(!EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL)) {
+    EVP_MD_CTX_destroy(ctx->openssl_ctx);
+    return CURLE_FAILED_INIT;
+  }
   return CURLE_OK;
 }
 
@@ -218,9 +222,14 @@
 
 static CURLcode my_sha256_init(my_sha256_ctx *ctx)
 {
-  if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES,
-                         CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
-    CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash);
+  if(!CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES,
+                         CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+    return CURLE_OUT_OF_MEMORY;
+
+  if(!CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash)) {
+    CryptReleaseContext(ctx->hCryptProv, 0);
+    ctx->hCryptProv = 0;
+    return CURLE_FAILED_INIT;
   }
 
   return CURLE_OK;
@@ -533,4 +542,4 @@
 };
 
 
-#endif /* CURL_DISABLE_CRYPTO_AUTH */
+#endif /* AWS, DIGEST, or libSSH2 */
diff --git a/Utilities/cmcurl/lib/sigpipe.h b/Utilities/cmcurl/lib/sigpipe.h
index 48761ad..9b29403 100644
--- a/Utilities/cmcurl/lib/sigpipe.h
+++ b/Utilities/cmcurl/lib/sigpipe.h
@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if defined(HAVE_SIGNAL_H) && defined(HAVE_SIGACTION) &&        \
+#if defined(HAVE_SIGACTION) &&        \
   (defined(USE_OPENSSL) || defined(USE_MBEDTLS) || defined(USE_WOLFSSL))
 #include <signal.h>
 
diff --git a/Utilities/cmcurl/lib/smb.c b/Utilities/cmcurl/lib/smb.c
index d682221..32c5137 100644
--- a/Utilities/cmcurl/lib/smb.c
+++ b/Utilities/cmcurl/lib/smb.c
@@ -27,8 +27,6 @@
 
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
 
-#define BUILDING_CURL_SMB_C
-
 #ifdef WIN32
 #define getpid GetCurrentProcessId
 #endif
@@ -50,6 +48,199 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/*
+ * Definitions for SMB protocol data structures
+ */
+#if defined(_MSC_VER) || defined(__ILEC400__)
+#  define PACK
+#  pragma pack(push)
+#  pragma pack(1)
+#elif defined(__GNUC__)
+#  define PACK __attribute__((packed))
+#else
+#  define PACK
+#endif
+
+#define SMB_COM_CLOSE                 0x04
+#define SMB_COM_READ_ANDX             0x2e
+#define SMB_COM_WRITE_ANDX            0x2f
+#define SMB_COM_TREE_DISCONNECT       0x71
+#define SMB_COM_NEGOTIATE             0x72
+#define SMB_COM_SETUP_ANDX            0x73
+#define SMB_COM_TREE_CONNECT_ANDX     0x75
+#define SMB_COM_NT_CREATE_ANDX        0xa2
+#define SMB_COM_NO_ANDX_COMMAND       0xff
+
+#define SMB_WC_CLOSE                  0x03
+#define SMB_WC_READ_ANDX              0x0c
+#define SMB_WC_WRITE_ANDX             0x0e
+#define SMB_WC_SETUP_ANDX             0x0d
+#define SMB_WC_TREE_CONNECT_ANDX      0x04
+#define SMB_WC_NT_CREATE_ANDX         0x18
+
+#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
+#define SMB_FLAGS_CASELESS_PATHNAMES  0x08
+#define SMB_FLAGS2_UNICODE_STRINGS    0x8000
+#define SMB_FLAGS2_IS_LONG_NAME       0x0040
+#define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
+
+#define SMB_CAP_LARGE_FILES           0x08
+#define SMB_GENERIC_WRITE             0x40000000
+#define SMB_GENERIC_READ              0x80000000
+#define SMB_FILE_SHARE_ALL            0x07
+#define SMB_FILE_OPEN                 0x01
+#define SMB_FILE_OVERWRITE_IF         0x05
+
+#define SMB_ERR_NOACCESS              0x00050001
+
+struct smb_header {
+  unsigned char nbt_type;
+  unsigned char nbt_flags;
+  unsigned short nbt_length;
+  unsigned char magic[4];
+  unsigned char command;
+  unsigned int status;
+  unsigned char flags;
+  unsigned short flags2;
+  unsigned short pid_high;
+  unsigned char signature[8];
+  unsigned short pad;
+  unsigned short tid;
+  unsigned short pid;
+  unsigned short uid;
+  unsigned short mid;
+} PACK;
+
+struct smb_negotiate_response {
+  struct smb_header h;
+  unsigned char word_count;
+  unsigned short dialect_index;
+  unsigned char security_mode;
+  unsigned short max_mpx_count;
+  unsigned short max_number_vcs;
+  unsigned int max_buffer_size;
+  unsigned int max_raw_size;
+  unsigned int session_key;
+  unsigned int capabilities;
+  unsigned int system_time_low;
+  unsigned int system_time_high;
+  unsigned short server_time_zone;
+  unsigned char encryption_key_length;
+  unsigned short byte_count;
+  char bytes[1];
+} PACK;
+
+struct andx {
+  unsigned char command;
+  unsigned char pad;
+  unsigned short offset;
+} PACK;
+
+struct smb_setup {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short max_buffer_size;
+  unsigned short max_mpx_count;
+  unsigned short vc_number;
+  unsigned int session_key;
+  unsigned short lengths[2];
+  unsigned int pad;
+  unsigned int capabilities;
+  unsigned short byte_count;
+  char bytes[1024];
+} PACK;
+
+struct smb_tree_connect {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short flags;
+  unsigned short pw_len;
+  unsigned short byte_count;
+  char bytes[1024];
+} PACK;
+
+struct smb_nt_create {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned char pad;
+  unsigned short name_length;
+  unsigned int flags;
+  unsigned int root_fid;
+  unsigned int access;
+  curl_off_t allocation_size;
+  unsigned int ext_file_attributes;
+  unsigned int share_access;
+  unsigned int create_disposition;
+  unsigned int create_options;
+  unsigned int impersonation_level;
+  unsigned char security_flags;
+  unsigned short byte_count;
+  char bytes[1024];
+} PACK;
+
+struct smb_nt_create_response {
+  struct smb_header h;
+  unsigned char word_count;
+  struct andx andx;
+  unsigned char op_lock_level;
+  unsigned short fid;
+  unsigned int create_disposition;
+
+  curl_off_t create_time;
+  curl_off_t last_access_time;
+  curl_off_t last_write_time;
+  curl_off_t last_change_time;
+  unsigned int ext_file_attributes;
+  curl_off_t allocation_size;
+  curl_off_t end_of_file;
+} PACK;
+
+struct smb_read {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short fid;
+  unsigned int offset;
+  unsigned short max_bytes;
+  unsigned short min_bytes;
+  unsigned int timeout;
+  unsigned short remaining;
+  unsigned int offset_high;
+  unsigned short byte_count;
+} PACK;
+
+struct smb_write {
+  struct smb_header h;
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short fid;
+  unsigned int offset;
+  unsigned int timeout;
+  unsigned short write_mode;
+  unsigned short remaining;
+  unsigned short pad;
+  unsigned short data_length;
+  unsigned short data_offset;
+  unsigned int offset_high;
+  unsigned short byte_count;
+  unsigned char pad2;
+} PACK;
+
+struct smb_close {
+  unsigned char word_count;
+  unsigned short fid;
+  unsigned int last_mtime;
+  unsigned short byte_count;
+} PACK;
+
+struct smb_tree_disconnect {
+  unsigned char word_count;
+  unsigned short byte_count;
+} PACK;
+
+#if defined(_MSC_VER) || defined(__ILEC400__)
+#  pragma pack(pop)
+#endif
+
 /* Local API functions */
 static CURLcode smb_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn);
@@ -373,12 +564,11 @@
                          size_t upload_size)
 {
   struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   struct smb_conn *smbc = &conn->proto.smbc;
   ssize_t bytes_written;
   CURLcode result;
 
-  result = Curl_write(data, sockfd, data->state.ulbuf,
+  result = Curl_nwrite(data, FIRSTSOCKET, data->state.ulbuf,
                       len, &bytes_written);
   if(result)
     return result;
@@ -396,7 +586,6 @@
 static CURLcode smb_flush(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
-  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   struct smb_conn *smbc = &conn->proto.smbc;
   ssize_t bytes_written;
   ssize_t len = smbc->send_size - smbc->sent;
@@ -405,9 +594,9 @@
   if(!smbc->send_size)
     return CURLE_OK;
 
-  result = Curl_write(data, sockfd,
-                      data->state.ulbuf + smbc->sent,
-                      len, &bytes_written);
+  result = Curl_nwrite(data, FIRSTSOCKET,
+                       data->state.ulbuf + smbc->sent,
+                       len, &bytes_written);
   if(result)
     return result;
 
@@ -860,7 +1049,12 @@
     }
     data->req.bytecount += len;
     data->req.offset += len;
-    Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+    result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+    if(result) {
+      req->result = result;
+      next_state = SMB_CLOSE;
+      break;
+    }
     next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
     break;
 
diff --git a/Utilities/cmcurl/lib/smb.h b/Utilities/cmcurl/lib/smb.h
index c35f3e9..437f4a5 100644
--- a/Utilities/cmcurl/lib/smb.h
+++ b/Utilities/cmcurl/lib/smb.h
@@ -48,203 +48,6 @@
   size_t got;
 };
 
-/*
- * Definitions for SMB protocol data structures
- */
-#ifdef BUILDING_CURL_SMB_C
-
-#if defined(_MSC_VER) || defined(__ILEC400__)
-#  define PACK
-#  pragma pack(push)
-#  pragma pack(1)
-#elif defined(__GNUC__)
-#  define PACK __attribute__((packed))
-#else
-#  define PACK
-#endif
-
-#define SMB_COM_CLOSE                 0x04
-#define SMB_COM_READ_ANDX             0x2e
-#define SMB_COM_WRITE_ANDX            0x2f
-#define SMB_COM_TREE_DISCONNECT       0x71
-#define SMB_COM_NEGOTIATE             0x72
-#define SMB_COM_SETUP_ANDX            0x73
-#define SMB_COM_TREE_CONNECT_ANDX     0x75
-#define SMB_COM_NT_CREATE_ANDX        0xa2
-#define SMB_COM_NO_ANDX_COMMAND       0xff
-
-#define SMB_WC_CLOSE                  0x03
-#define SMB_WC_READ_ANDX              0x0c
-#define SMB_WC_WRITE_ANDX             0x0e
-#define SMB_WC_SETUP_ANDX             0x0d
-#define SMB_WC_TREE_CONNECT_ANDX      0x04
-#define SMB_WC_NT_CREATE_ANDX         0x18
-
-#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
-#define SMB_FLAGS_CASELESS_PATHNAMES  0x08
-#define SMB_FLAGS2_UNICODE_STRINGS    0x8000
-#define SMB_FLAGS2_IS_LONG_NAME       0x0040
-#define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
-
-#define SMB_CAP_LARGE_FILES           0x08
-#define SMB_GENERIC_WRITE             0x40000000
-#define SMB_GENERIC_READ              0x80000000
-#define SMB_FILE_SHARE_ALL            0x07
-#define SMB_FILE_OPEN                 0x01
-#define SMB_FILE_OVERWRITE_IF         0x05
-
-#define SMB_ERR_NOACCESS              0x00050001
-
-struct smb_header {
-  unsigned char nbt_type;
-  unsigned char nbt_flags;
-  unsigned short nbt_length;
-  unsigned char magic[4];
-  unsigned char command;
-  unsigned int status;
-  unsigned char flags;
-  unsigned short flags2;
-  unsigned short pid_high;
-  unsigned char signature[8];
-  unsigned short pad;
-  unsigned short tid;
-  unsigned short pid;
-  unsigned short uid;
-  unsigned short mid;
-} PACK;
-
-struct smb_negotiate_response {
-  struct smb_header h;
-  unsigned char word_count;
-  unsigned short dialect_index;
-  unsigned char security_mode;
-  unsigned short max_mpx_count;
-  unsigned short max_number_vcs;
-  unsigned int max_buffer_size;
-  unsigned int max_raw_size;
-  unsigned int session_key;
-  unsigned int capabilities;
-  unsigned int system_time_low;
-  unsigned int system_time_high;
-  unsigned short server_time_zone;
-  unsigned char encryption_key_length;
-  unsigned short byte_count;
-  char bytes[1];
-} PACK;
-
-struct andx {
-  unsigned char command;
-  unsigned char pad;
-  unsigned short offset;
-} PACK;
-
-struct smb_setup {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short max_buffer_size;
-  unsigned short max_mpx_count;
-  unsigned short vc_number;
-  unsigned int session_key;
-  unsigned short lengths[2];
-  unsigned int pad;
-  unsigned int capabilities;
-  unsigned short byte_count;
-  char bytes[1024];
-} PACK;
-
-struct smb_tree_connect {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short flags;
-  unsigned short pw_len;
-  unsigned short byte_count;
-  char bytes[1024];
-} PACK;
-
-struct smb_nt_create {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned char pad;
-  unsigned short name_length;
-  unsigned int flags;
-  unsigned int root_fid;
-  unsigned int access;
-  curl_off_t allocation_size;
-  unsigned int ext_file_attributes;
-  unsigned int share_access;
-  unsigned int create_disposition;
-  unsigned int create_options;
-  unsigned int impersonation_level;
-  unsigned char security_flags;
-  unsigned short byte_count;
-  char bytes[1024];
-} PACK;
-
-struct smb_nt_create_response {
-  struct smb_header h;
-  unsigned char word_count;
-  struct andx andx;
-  unsigned char op_lock_level;
-  unsigned short fid;
-  unsigned int create_disposition;
-
-  curl_off_t create_time;
-  curl_off_t last_access_time;
-  curl_off_t last_write_time;
-  curl_off_t last_change_time;
-  unsigned int ext_file_attributes;
-  curl_off_t allocation_size;
-  curl_off_t end_of_file;
-} PACK;
-
-struct smb_read {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short fid;
-  unsigned int offset;
-  unsigned short max_bytes;
-  unsigned short min_bytes;
-  unsigned int timeout;
-  unsigned short remaining;
-  unsigned int offset_high;
-  unsigned short byte_count;
-} PACK;
-
-struct smb_write {
-  struct smb_header h;
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short fid;
-  unsigned int offset;
-  unsigned int timeout;
-  unsigned short write_mode;
-  unsigned short remaining;
-  unsigned short pad;
-  unsigned short data_length;
-  unsigned short data_offset;
-  unsigned int offset_high;
-  unsigned short byte_count;
-  unsigned char pad2;
-} PACK;
-
-struct smb_close {
-  unsigned char word_count;
-  unsigned short fid;
-  unsigned int last_mtime;
-  unsigned short byte_count;
-} PACK;
-
-struct smb_tree_disconnect {
-  unsigned char word_count;
-  unsigned short byte_count;
-} PACK;
-
-#if defined(_MSC_VER) || defined(__ILEC400__)
-#  pragma pack(pop)
-#endif
-
-#endif /* BUILDING_CURL_SMB_C */
-
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
     (SIZEOF_CURL_OFF_T > 4)
 
diff --git a/Utilities/cmcurl/lib/smtp.c b/Utilities/cmcurl/lib/smtp.c
index c182cac..81a17e3 100644
--- a/Utilities/cmcurl/lib/smtp.c
+++ b/Utilities/cmcurl/lib/smtp.c
@@ -49,9 +49,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -281,11 +278,11 @@
 
 /***********************************************************************
  *
- * state()
+ * smtp_state()
  *
  * This is the ONLY way to change SMTP state!
  */
-static void state(struct Curl_easy *data, smtpstate newstate)
+static void smtp_state(struct Curl_easy *data, smtpstate newstate)
 {
   struct smtp_conn *smtpc = &data->conn->proto.smtpc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -338,7 +335,7 @@
   result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
 
   if(!result)
-    state(data, SMTP_EHLO);
+    smtp_state(data, SMTP_EHLO);
 
   return result;
 }
@@ -362,7 +359,7 @@
   result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
 
   if(!result)
-    state(data, SMTP_HELO);
+    smtp_state(data, SMTP_HELO);
 
   return result;
 }
@@ -381,7 +378,7 @@
                                   "%s", "STARTTLS");
 
   if(!result)
-    state(data, SMTP_STARTTLS);
+    smtp_state(data, SMTP_STARTTLS);
 
   return result;
 }
@@ -410,7 +407,7 @@
   if(!result) {
     smtpc->ssldone = ssldone;
     if(smtpc->state != SMTP_UPGRADETLS)
-      state(data, SMTP_UPGRADETLS);
+      smtp_state(data, SMTP_UPGRADETLS);
 
     if(smtpc->ssldone) {
       smtp_to_smtps(conn);
@@ -499,7 +496,7 @@
      server supports authentication, and end the connect phase if not */
   if(!smtpc->auth_supported ||
      !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
-    state(data, SMTP_STOP);
+    smtp_state(data, SMTP_STOP);
     return result;
   }
 
@@ -508,7 +505,7 @@
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
-      state(data, SMTP_AUTH);
+      smtp_state(data, SMTP_AUTH);
     else {
       /* Other mechanisms not supported */
       infof(data, "No known authentication mechanisms supported");
@@ -586,7 +583,7 @@
                            smtp->custom : "HELP");
 
   if(!result)
-    state(data, SMTP_COMMAND);
+    smtp_state(data, SMTP_COMMAND);
 
   return result;
 }
@@ -771,7 +768,7 @@
   free(size);
 
   if(!result)
-    state(data, SMTP_MAIL);
+    smtp_state(data, SMTP_MAIL);
 
   return result;
 }
@@ -812,7 +809,7 @@
   free(address);
 
   if(!result)
-    state(data, SMTP_RCPT);
+    smtp_state(data, SMTP_RCPT);
 
   return result;
 }
@@ -830,7 +827,7 @@
   CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
 
   if(!result)
-    state(data, SMTP_QUIT);
+    smtp_state(data, SMTP_QUIT);
 
   return result;
 }
@@ -996,7 +993,7 @@
   }
   else
     /* End of connect phase */
-    state(data, SMTP_STOP);
+    smtp_state(data, SMTP_STOP);
 
   return result;
 }
@@ -1017,7 +1014,7 @@
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      state(data, SMTP_STOP);  /* Authenticated */
+      smtp_state(data, SMTP_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
       failf(data, "Authentication cancelled");
@@ -1064,11 +1061,11 @@
         }
         else
           /* End of DO phase */
-          state(data, SMTP_STOP);
+          smtp_state(data, SMTP_STOP);
       }
       else
         /* End of DO phase */
-        state(data, SMTP_STOP);
+        smtp_state(data, SMTP_STOP);
     }
   }
 
@@ -1145,7 +1142,7 @@
         result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
 
         if(!result)
-          state(data, SMTP_DATA);
+          smtp_state(data, SMTP_DATA);
       }
     }
   }
@@ -1172,7 +1169,7 @@
     Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
 
     /* End of DO phase */
-    state(data, SMTP_STOP);
+    smtp_state(data, SMTP_STOP);
   }
 
   return result;
@@ -1192,7 +1189,7 @@
     result = CURLE_WEIRD_SERVER_REPLY;
 
   /* End of DONE phase */
-  state(data, SMTP_STOP);
+  smtp_state(data, SMTP_STOP);
 
   return result;
 }
@@ -1274,7 +1271,7 @@
       /* fallthrough, just stop! */
     default:
       /* internal error */
-      state(data, SMTP_STOP);
+      smtp_state(data, SMTP_STOP);
       break;
     }
   } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
@@ -1379,7 +1376,7 @@
     return result;
 
   /* Start off waiting for the server greeting response */
-  state(data, SMTP_SERVERGREET);
+  smtp_state(data, SMTP_SERVERGREET);
 
   result = smtp_multi_statemach(data, done);
 
@@ -1461,7 +1458,7 @@
       free(eob);
     }
 
-    state(data, SMTP_POSTDATA);
+    smtp_state(data, SMTP_POSTDATA);
 
     /* Run the state-machine */
     result = smtp_block_statemach(data, conn, FALSE);
diff --git a/Utilities/cmcurl/lib/socks.c b/Utilities/cmcurl/lib/socks.c
index b74071a..a7b5ab0 100644
--- a/Utilities/cmcurl/lib/socks.c
+++ b/Utilities/cmcurl/lib/socks.c
@@ -161,7 +161,7 @@
   enum connect_t oldstate = sx->state;
 #ifdef DEBUG_AND_VERBOSE
   /* synced with the state list in urldata.h */
-  static const char * const statename[] = {
+  static const char * const socks_statename[] = {
     "INIT",
     "SOCKS_INIT",
     "SOCKS_SEND",
@@ -193,7 +193,7 @@
 #ifdef DEBUG_AND_VERBOSE
   infof(data,
         "SXSTATE: %s => %s; line %d",
-        statename[oldstate], statename[sx->state],
+        socks_statename[oldstate], socks_statename[sx->state],
         lineno);
 #endif
 }
@@ -567,7 +567,6 @@
   */
   struct connectdata *conn = cf->conn;
   unsigned char *socksreq = (unsigned char *)data->state.buffer;
-  char dest[256] = "unknown";  /* printable hostname:port */
   int idx;
   CURLcode result;
   CURLproxycode presult;
@@ -820,8 +819,8 @@
     /* FALLTHROUGH */
 CONNECT_RESOLVED:
   case CONNECT_RESOLVED: {
+    char dest[MAX_IPADR_LEN] = "unknown";  /* printable address */
     struct Curl_addrinfo *hp = NULL;
-    size_t destlen;
     if(dns)
       hp = dns->addr;
     if(!hp) {
@@ -831,8 +830,6 @@
     }
 
     Curl_printable_address(hp, dest, sizeof(dest));
-    destlen = strlen(dest);
-    msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port);
 
     len = 0;
     socksreq[len++] = 5; /* version (SOCKS5) */
@@ -848,7 +845,8 @@
         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
+      infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest,
+            sx->remote_port);
     }
 #ifdef ENABLE_IPV6
     else if(hp->ai_family == AF_INET6) {
@@ -862,7 +860,8 @@
           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
+      infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest,
+            sx->remote_port);
     }
 #endif
     else {
@@ -1115,7 +1114,7 @@
     return CURLE_OK;
   }
 
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -1193,7 +1192,7 @@
   DEBUGASSERT(cf->next);
   cf->connected = FALSE;
   socks_proxy_cf_free(cf);
-  cf->next->cft->close(cf->next, data);
+  cf->next->cft->do_close(cf->next, data);
 }
 
 static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,
diff --git a/Utilities/cmcurl/lib/strerror.c b/Utilities/cmcurl/lib/strerror.c
index bd9cc53..be41914 100644
--- a/Utilities/cmcurl/lib/strerror.c
+++ b/Utilities/cmcurl/lib/strerror.c
@@ -938,7 +938,7 @@
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   if(!get_winapi_error(err, buf, buflen)) {
-    msnprintf(buf, buflen, "Unknown error %u (0x%08X)", err, err);
+    msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err);
   }
 #else
   {
diff --git a/Utilities/cmcurl/lib/system_win32.h b/Utilities/cmcurl/lib/system_win32.h
index 24899cb..6482643 100644
--- a/Utilities/cmcurl/lib/system_win32.h
+++ b/Utilities/cmcurl/lib/system_win32.h
@@ -42,7 +42,8 @@
 
 /* This is used to dynamically load DLLs */
 HMODULE Curl_load_library(LPCTSTR filename);
-
-#endif /* WIN32 */
+#else  /* WIN32 */
+#define Curl_win32_init(x) CURLE_OK
+#endif /* !WIN32 */
 
 #endif /* HEADER_CURL_SYSTEM_WIN32_H */
diff --git a/Utilities/cmcurl/lib/telnet.c b/Utilities/cmcurl/lib/telnet.c
index 643e43d..836e255 100644
--- a/Utilities/cmcurl/lib/telnet.c
+++ b/Utilities/cmcurl/lib/telnet.c
@@ -1266,10 +1266,10 @@
         break;
       default:                    /* write! */
         bytes_written = 0;
-        result = Curl_write(data, conn->sock[FIRSTSOCKET],
-                            outbuf + total_written,
-                            outlen - total_written,
-                            &bytes_written);
+        result = Curl_nwrite(data, FIRSTSOCKET,
+                             outbuf + total_written,
+                             outlen - total_written,
+                             &bytes_written);
         total_written += bytes_written;
         break;
     }
@@ -1534,7 +1534,7 @@
   }
 
   while(keepon) {
-    DEBUGF(infof(data, "telnet_do(handle=%p), poll %d fds", data, poll_cnt));
+    DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
     switch(Curl_poll(pfd, poll_cnt, interval_ms)) {
     case -1:                    /* error, stop reading */
       keepon = FALSE;
@@ -1558,8 +1558,7 @@
            * in a clean way? Seems to be timing related, happens more
            * on slow debug build */
           if(data->state.os_errno == ECONNRESET) {
-            DEBUGF(infof(data, "telnet_do(handle=%p), unexpected ECONNRESET"
-                         " on recv", data));
+            DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
           }
           break;
         }
@@ -1571,8 +1570,9 @@
         }
 
         total_dl += nread;
-        Curl_pgrsSetDownloadCounter(data, total_dl);
-        result = telrcv(data, (unsigned char *)buf, nread);
+        result = Curl_pgrsSetDownloadCounter(data, total_dl);
+        if(!result)
+          result = telrcv(data, (unsigned char *)buf, nread);
         if(result) {
           keepon = FALSE;
           break;
diff --git a/Utilities/cmcurl/lib/tftp.c b/Utilities/cmcurl/lib/tftp.c
index 8ed1b88..e78140d 100644
--- a/Utilities/cmcurl/lib/tftp.c
+++ b/Utilities/cmcurl/lib/tftp.c
@@ -1141,12 +1141,15 @@
         result = Curl_client_write(data, CLIENTWRITE_BODY,
                                    (char *)state->rpacket.data + 4,
                                    state->rbytes-4);
+        if(!result) {
+          k->bytecount += state->rbytes-4;
+          result = Curl_pgrsSetDownloadCounter(data,
+                                               (curl_off_t) k->bytecount);
+        }
         if(result) {
           tftp_state_machine(state, TFTP_EVENT_ERROR);
           return result;
         }
-        k->bytecount += state->rbytes-4;
-        Curl_pgrsSetDownloadCounter(data, (curl_off_t) k->bytecount);
       }
       break;
     case TFTP_EVENT_ERROR:
diff --git a/Utilities/cmcurl/lib/timeval.c b/Utilities/cmcurl/lib/timeval.c
index dca1c6f..026d9d1 100644
--- a/Utilities/cmcurl/lib/timeval.c
+++ b/Utilities/cmcurl/lib/timeval.c
@@ -58,7 +58,8 @@
   return now;
 }
 
-#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC)
+#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) ||  \
+  defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
 
 struct curltime Curl_now(void)
 {
@@ -87,6 +88,19 @@
     have_clock_gettime = TRUE;
 #endif
 
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW
+  if(
+#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) &&    \
+        (HAVE_BUILTIN_AVAILABLE == 1)
+    have_clock_gettime &&
+#endif
+    (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) {
+    cnow.tv_sec = tsnow.tv_sec;
+    cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000);
+  }
+  else
+#endif
+
   if(
 #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
         (HAVE_BUILTIN_AVAILABLE == 1)
@@ -196,6 +210,20 @@
 }
 
 /*
+ * Returns: time difference in number of milliseconds, rounded up.
+ * For too large diffs it returns max value.
+ */
+timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older)
+{
+  timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
+  if(diff >= (TIMEDIFF_T_MAX/1000))
+    return TIMEDIFF_T_MAX;
+  else if(diff <= (TIMEDIFF_T_MIN/1000))
+    return TIMEDIFF_T_MIN;
+  return diff * 1000 + (newer.tv_usec - older.tv_usec + 999)/1000;
+}
+
+/*
  * Returns: time difference in number of microseconds. For too large diffs it
  * returns max value.
  */
diff --git a/Utilities/cmcurl/lib/timeval.h b/Utilities/cmcurl/lib/timeval.h
index 92e484a..33dfb5b 100644
--- a/Utilities/cmcurl/lib/timeval.h
+++ b/Utilities/cmcurl/lib/timeval.h
@@ -36,16 +36,24 @@
 struct curltime Curl_now(void);
 
 /*
- * Make sure that the first argument (t1) is the more recent time and t2 is
- * the older time, as otherwise you get a weird negative time-diff back...
+ * Make sure that the first argument (newer) is the more recent time and older
+ * is the older time, as otherwise you get a weird negative time-diff back...
  *
  * Returns: the time difference in number of milliseconds.
  */
-timediff_t Curl_timediff(struct curltime t1, struct curltime t2);
+timediff_t Curl_timediff(struct curltime newer, struct curltime older);
 
 /*
- * Make sure that the first argument (t1) is the more recent time and t2 is
- * the older time, as otherwise you get a weird negative time-diff back...
+ * Make sure that the first argument (newer) is the more recent time and older
+ * is the older time, as otherwise you get a weird negative time-diff back...
+ *
+ * Returns: the time difference in number of milliseconds, rounded up.
+ */
+timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older);
+
+/*
+ * Make sure that the first argument (newer) is the more recent time and older
+ * is the older time, as otherwise you get a weird negative time-diff back...
  *
  * Returns: the time difference in number of microseconds.
  */
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index d2ff0c2..6886764 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -40,9 +40,7 @@
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
-#ifdef HAVE_SIGNAL_H
 #include <signal.h>
-#endif
 
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
@@ -428,7 +426,10 @@
   size_t excess = 0; /* excess bytes read */
   bool readmore = FALSE; /* used by RTP to signal for more data */
   int maxloops = 100;
+  curl_off_t max_recv = data->set.max_recv_speed?
+                        data->set.max_recv_speed : CURL_OFF_T_MAX;
   char *buf = data->state.buffer;
+  bool data_eof_handled = FALSE;
   DEBUGASSERT(buf);
 
   *done = FALSE;
@@ -446,8 +447,7 @@
        to ensure that http2_handle_stream_close is called when we read all
        incoming bytes for a particular stream. */
     bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET);
-    bool data_eof_handled = is_http3
-                            || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
+    data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
 
     if(!data_eof_handled && k->size != -1 && !k->header) {
       /* make sure we don't read too much */
@@ -472,7 +472,7 @@
     else {
       /* read nothing but since we wanted nothing we consider this an OK
          situation to proceed from */
-      DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done")));
+      DEBUGF(infof(data, "readwrite_data: we're done"));
       nread = 0;
     }
 
@@ -490,15 +490,16 @@
     if(0 < nread || is_empty_data) {
       buf[nread] = 0;
     }
-    else {
+    if(!nread) {
       /* if we receive 0 or less here, either the data transfer is done or the
          server closed the connection and we bail out from this! */
       if(data_eof_handled)
         DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
       else
         DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
-      k->keepon &= ~KEEP_RECV;
-      break;
+      k->keepon = 0; /* stop sending as well */
+      if(!is_empty_data)
+        break;
     }
 
     /* Default buffer to use when we write the buffer, it may be changed
@@ -666,8 +667,11 @@
       }
 
       k->bytecount += nread;
+      max_recv -= nread;
 
-      Curl_pgrsSetDownloadCounter(data, k->bytecount);
+      result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
+      if(result)
+        goto out;
 
       if(!k->chunk && (nread || k->badheader || is_empty_data)) {
         /* If this is chunky transfer, it was already written */
@@ -696,19 +700,15 @@
              in http_chunks.c.
              Make sure that ALL_CONTENT_ENCODINGS contains all the
              encodings handled here. */
-          if(data->set.http_ce_skip || !k->writer_stack) {
-            if(!k->ignorebody && nread) {
+          if(!k->ignorebody && nread) {
 #ifndef CURL_DISABLE_POP3
-              if(conn->handler->protocol & PROTO_FAMILY_POP3)
-                result = Curl_pop3_write(data, k->str, nread);
-              else
+            if(conn->handler->protocol & PROTO_FAMILY_POP3)
+              result = Curl_pop3_write(data, k->str, nread);
+            else
 #endif /* CURL_DISABLE_POP3 */
-                result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
-                                           nread);
-            }
+              result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
+                                         nread);
           }
-          else if(!k->ignorebody && nread)
-            result = Curl_unencode_write(data, k->writer_stack, k->str, nread);
         }
         k->badheader = HEADER_NORMAL; /* taken care of now */
 
@@ -749,16 +749,16 @@
       break;
     }
 
-  } while(data_pending(data) && maxloops--);
+  } while((max_recv > 0) && data_pending(data) && maxloops--);
 
-  if(maxloops <= 0) {
+  if(maxloops <= 0 || max_recv <= 0) {
     /* we mark it as read-again-please */
     data->state.dselect_bits = CURL_CSELECT_IN;
     *comeback = TRUE;
   }
 
   if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
-     conn->bits.close) {
+     (conn->bits.close || data_eof_handled)) {
     /* When we've read the entire thing and the close bit is set, the server
        may now close the connection. If there's now any kind of sending going
        on from our side, we need to stop that immediately. */
@@ -768,7 +768,7 @@
 
 out:
   if(result)
-    DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result));
+    DEBUGF(infof(data, "readwrite_data() -> %d", result));
   return result;
 }
 
@@ -821,9 +821,6 @@
   bool sending_http_headers = FALSE;
   struct SingleRequest *k = &data->req;
 
-  if((k->bytecount == 0) && (k->writebytecount == 0))
-    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
-
   *didwhat |= KEEP_SEND;
 
   do {
@@ -1049,6 +1046,19 @@
   return CURLE_OK;
 }
 
+static int select_bits_paused(struct Curl_easy *data, int select_bits)
+{
+  /* See issue #11982: we really need to be careful not to progress
+   * a transfer direction when that direction is paused. Not all parts
+   * of our state machine are handling PAUSED transfers correctly. So, we
+   * do not want to go there.
+   * NOTE: we are only interested in PAUSE, not HOLD. */
+  return (((select_bits & CURL_CSELECT_IN) &&
+           (data->req.keepon & KEEP_RECV_PAUSE)) ||
+          ((select_bits & CURL_CSELECT_OUT) &&
+           (data->req.keepon & KEEP_SEND_PAUSE)));
+}
+
 /*
  * Curl_readwrite() is the low-level function to be called when data is to
  * be read and written to/from the connection.
@@ -1067,12 +1077,20 @@
   int didwhat = 0;
   int select_bits;
 
-
   if(data->state.dselect_bits) {
+    if(select_bits_paused(data, data->state.dselect_bits)) {
+      /* leave the bits unchanged, so they'll tell us what to do when
+       * this transfer gets unpaused. */
+      DEBUGF(infof(data, "readwrite, dselect_bits, early return on PAUSED"));
+      result = CURLE_OK;
+      goto out;
+    }
     select_bits = data->state.dselect_bits;
     data->state.dselect_bits = 0;
   }
   else if(conn->cselect_bits) {
+    /* CAVEAT: adding `select_bits_paused()` check here makes test640 hang
+     * (among others). Which hints at strange state handling in FTP land... */
     select_bits = conn->cselect_bits;
     conn->cselect_bits = 0;
   }
@@ -1233,7 +1251,7 @@
   *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE;
 out:
   if(result)
-    DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result));
+    DEBUGF(infof(data, "Curl_readwrite() -> %d", result));
   return result;
 }
 
@@ -1332,7 +1350,9 @@
   }
 
   data->state.prefer_ascii = data->set.prefer_ascii;
+#ifdef CURL_LIST_ONLY_PROTOCOL
   data->state.list_only = data->set.list_only;
+#endif
   data->state.httpreq = data->set.method;
   data->state.url = data->set.str[STRING_SET_URL];
 
@@ -1394,7 +1414,7 @@
     Curl_pgrsResetTransferSizes(data);
     Curl_pgrsStartNow(data);
 
-    /* In case the handle is re-used and an authentication method was picked
+    /* In case the handle is reused and an authentication method was picked
        in the session we need to make sure we only use the one(s) we now
        consider to be fine */
     data->state.authhost.picked &= data->state.authhost.want;
@@ -1551,10 +1571,11 @@
 
   if((type != FOLLOW_RETRY) &&
      (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
-     Curl_is_absolute_url(newurl, NULL, 0, FALSE))
+     Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
     /* If this is not redirect due to a 401 or 407 response and an absolute
        URL: don't allow a custom port number */
     disallowport = TRUE;
+  }
 
   DEBUGASSERT(data->state.uh);
   uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
@@ -1783,7 +1804,7 @@
      && (data->set.rtspreq != RTSPREQ_RECEIVE)
 #endif
     )
-    /* We got no data, we attempted to re-use a connection. For HTTP this
+    /* We got no data, we attempted to reuse a connection. For HTTP this
        can be a retry so we try again regardless if we expected a body.
        For other protocols we only try again only if we expected a body.
 
diff --git a/Utilities/cmcurl/lib/url.c b/Utilities/cmcurl/lib/url.c
index 0fb6268..61dad44 100644
--- a/Utilities/cmcurl/lib/url.c
+++ b/Utilities/cmcurl/lib/url.c
@@ -414,7 +414,7 @@
     Curl_hsts_cleanup(&data->hsts);
   curl_slist_free_all(data->set.hstslist); /* clean up list */
 #endif
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
   Curl_http_auth_cleanup_digest(data);
 #endif
   Curl_safefree(data->info.contenttype);
@@ -457,6 +457,11 @@
   }
 #endif
 
+  Curl_mime_cleanpart(data->state.formp);
+#ifndef CURL_DISABLE_HTTP
+  Curl_safefree(data->state.formp);
+#endif
+
   /* destruct wildcard structures if it is needed */
   Curl_wildcard_dtor(&data->wildcard);
   Curl_freeset(data);
@@ -490,7 +495,7 @@
 
   set->filesize = -1;        /* we don't know the size */
   set->postfieldsize = -1;   /* unknown size */
-  set->maxredirs = -1;       /* allow any amount by default */
+  set->maxredirs = 30;       /* sensible default */
 
   set->method = HTTPREQ_GET; /* Default HTTP request */
 #ifndef CURL_DISABLE_RTSP
@@ -659,6 +664,9 @@
 
     /* most recent connection is not yet defined */
     data->state.lastconnect_id = -1;
+    data->state.recent_conn_id = -1;
+    /* and not assigned an id yet */
+    data->id = -1;
 
     data->progress.flags |= PGRS_HIDE;
     data->state.current_speed = -1; /* init to negative == impossible */
@@ -680,7 +688,7 @@
 static void conn_shutdown(struct Curl_easy *data)
 {
   DEBUGASSERT(data);
-  infof(data, "Closing connection %ld", data->conn->connection_id);
+  infof(data, "Closing connection");
 
   /* possible left-overs from the async name resolvers */
   Curl_resolver_cancel(data);
@@ -763,7 +771,8 @@
   /* the transfer must be detached from the connection */
   DEBUGASSERT(!data->conn);
 
-  DEBUGF(infof(data, "Curl_disconnect(conn #%ld, dead=%d)",
+  DEBUGF(infof(data, "Curl_disconnect(conn #%"
+         CURL_FORMAT_CURL_OFF_T ", dead=%d)",
          conn->connection_id, dead_connection));
   /*
    * If this connection isn't marked to force-close, leave it open if there
@@ -879,8 +888,8 @@
   idletime /= 1000; /* integer seconds is fine */
 
   if(idletime > data->set.maxage_conn) {
-    infof(data, "Too old connection (%ld seconds idle), disconnect it",
-          idletime);
+    infof(data, "Too old connection (%" CURL_FORMAT_TIMEDIFF_T
+          " seconds idle), disconnect it", idletime);
     return TRUE;
   }
 
@@ -889,8 +898,8 @@
 
   if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
     infof(data,
-          "Too old connection (%ld seconds since creation), disconnect it",
-          lifetime);
+          "Too old connection (%" CURL_FORMAT_TIMEDIFF_T
+          " seconds since creation), disconnect it", lifetime);
     return TRUE;
   }
 
@@ -937,22 +946,25 @@
     else {
       bool input_pending;
 
+      Curl_attach_connection(data, conn);
       dead = !Curl_conn_is_alive(data, conn, &input_pending);
       if(input_pending) {
         /* For reuse, we want a "clean" connection state. The includes
          * that we expect - in general - no waiting input data. Input
          * waiting might be a TLS Notify Close, for example. We reject
          * that.
-         * For protocols where data from other other end may arrive at
+         * For protocols where data from other end may arrive at
          * any time (HTTP/2 PING for example), the protocol handler needs
          * to install its own `connection_check` callback.
          */
         dead = TRUE;
       }
+      Curl_detach_connection(data);
     }
 
     if(dead) {
-      infof(data, "Connection %ld seems to be dead", conn->connection_id);
+      infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead",
+            conn->connection_id);
       Curl_conncache_remove_conn(data, conn, FALSE);
       return TRUE;
     }
@@ -1066,6 +1078,9 @@
   bool wantProxyNTLMhttp = FALSE;
 #endif
 #endif
+  /* plain HTTP with upgrade */
+  bool h2upgrade = (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
+    (needle->handler->protocol & CURLPROTO_HTTP);
 
   *force_reuse = FALSE;
   *waitpipe = FALSE;
@@ -1088,7 +1103,7 @@
           infof(data, "Server doesn't support multiplex yet, wait");
           *waitpipe = TRUE;
           CONNCACHE_UNLOCK(data);
-          return FALSE; /* no re-use */
+          return FALSE; /* no reuse */
         }
 
         infof(data, "Server doesn't support multiplex (yet)");
@@ -1128,7 +1143,7 @@
       }
 
       if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
-          && data->set.ipver != check->ip_version) {
+         && data->set.ipver != check->ip_version) {
         /* skip because the connection is not via the requested IP version */
         continue;
       }
@@ -1143,23 +1158,18 @@
           continue;
         }
 
-        if(Curl_resolver_asynch()) {
-          /* primary_ip[0] is NUL only if the resolving of the name hasn't
-             completed yet and until then we don't re-use this connection */
-          if(!check->primary_ip[0]) {
-            infof(data,
-                  "Connection #%ld is still name resolving, can't reuse",
-                  check->connection_id);
-            continue;
-          }
-        }
+        if(Curl_resolver_asynch() &&
+           /* primary_ip[0] is NUL only if the resolving of the name hasn't
+              completed yet and until then we don't reuse this connection */
+           !check->primary_ip[0])
+          continue;
       }
 
       if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
         foundPendingCandidate = TRUE;
         /* Don't pick a connection that hasn't connected yet */
-        infof(data, "Connection #%ld isn't open enough, can't reuse",
-              check->connection_id);
+        infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T
+              " isn't open enough, can't reuse", check->connection_id);
         continue;
       }
 
@@ -1231,6 +1241,17 @@
       }
 #endif
 
+      if(h2upgrade && !check->httpversion && canmultiplex) {
+        if(data->set.pipewait) {
+          infof(data, "Server upgrade doesn't support multiplex yet, wait");
+          *waitpipe = TRUE;
+          CONNCACHE_UNLOCK(data);
+          return FALSE; /* no reuse */
+        }
+        infof(data, "Server upgrade cannot be used");
+        continue; /* can't be used atm */
+      }
+
       if(!canmultiplex && CONN_INUSE(check))
         /* this request can't be multiplexed but the checked connection is
            already in use so we skip it */
@@ -1247,14 +1268,14 @@
 
       if(needle->localdev || needle->localport) {
         /* If we are bound to a specific local end (IP+port), we must not
-           re-use a random other one, although if we didn't ask for a
+           reuse a random other one, although if we didn't ask for a
            particular one we can reuse one that was bound.
 
            This comparison is a bit rough and too strict. Since the input
            parameters can be specified in numerous ways and still end up the
            same it would take a lot of processing to make it really accurate.
-           Instead, this matching will assume that re-uses of bound connections
-           will most likely also re-use the exact same binding parameters and
+           Instead, this matching will assume that reuses of bound connections
+           will most likely also reuse the exact same binding parameters and
            missing out a few edge cases shouldn't hurt anyone very much.
         */
         if((check->localport != needle->localport) ||
@@ -1287,7 +1308,7 @@
          (((check->httpversion >= 20) &&
            (data->state.httpwant < CURL_HTTP_VERSION_2_0))
           || ((check->httpversion >= 30) &&
-           (data->state.httpwant < CURL_HTTP_VERSION_3))))
+              (data->state.httpwant < CURL_HTTP_VERSION_3))))
         continue;
 #ifdef USE_SSH
       else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
@@ -1335,8 +1356,8 @@
             if(!Curl_ssl_config_matches(&needle->ssl_config,
                                         &check->ssl_config)) {
               DEBUGF(infof(data,
-                           "Connection #%ld has different SSL parameters, "
-                           "can't reuse",
+                           "Connection #%" CURL_FORMAT_CURL_OFF_T
+                           " has different SSL parameters, can't reuse",
                            check->connection_id));
               continue;
             }
@@ -1477,14 +1498,8 @@
                          struct connectdata *conn)
 {
   if(data->set.verbose)
-    infof(data, "Connected to %s (%s) port %u (#%ld)",
-#ifndef CURL_DISABLE_PROXY
-          conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
-          conn->bits.httpproxy ? conn->http_proxy.host.dispname :
-#endif
-          conn->bits.conn_to_host ? conn->conn_to_host.dispname :
-          conn->host.dispname,
-          conn->primary_ip, conn->port, conn->connection_id);
+    infof(data, "Connected to %s (%s) port %u",
+          CURL_CONN_HOST_DISPNAME(conn), conn->primary_ip, conn->port);
 }
 #endif
 
@@ -1580,8 +1595,10 @@
     if(!conn->localdev)
       goto error;
   }
+#ifndef CURL_DISABLE_BINDLOCAL
   conn->localportrange = data->set.localportrange;
   conn->localport = data->set.localport;
+#endif
 
   /* the close socket stuff needs to be copied to the connection struct as
      it may live on without (this specific) Curl_easy */
@@ -1857,7 +1874,7 @@
    * User name and password set with their own options override the
    * credentials possibly set in the URL.
    */
-  if(!data->state.aptr.passwd) {
+  if(!data->set.str[STRING_PASSWORD]) {
     uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
     if(!uc) {
       char *decoded;
@@ -2016,13 +2033,13 @@
 {
   Curl_safefree(data->req.p.http);
   Curl_safefree(data->req.newurl);
-
 #ifndef CURL_DISABLE_DOH
   if(data->req.doh) {
     Curl_close(&data->req.doh->probe[0].easy);
     Curl_close(&data->req.doh->probe[1].easy);
   }
 #endif
+  Curl_client_cleanup(data);
 }
 
 
@@ -2059,7 +2076,6 @@
   char proxy_env[128];
   const char *protop = conn->handler->scheme;
   char *envp = proxy_env;
-  char *prox;
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
 #endif
@@ -2072,7 +2088,7 @@
   strcpy(envp, "_proxy");
 
   /* read the protocol proxy: */
-  prox = curl_getenv(proxy_env);
+  proxy = curl_getenv(proxy_env);
 
   /*
    * We don't try the uppercase version of HTTP_PROXY because of
@@ -2086,23 +2102,35 @@
    * This can cause 'internal' http/ftp requests to be
    * arbitrarily redirected by any external attacker.
    */
-  if(!prox && !strcasecompare("http_proxy", proxy_env)) {
+  if(!proxy && !strcasecompare("http_proxy", proxy_env)) {
     /* There was no lowercase variable, try the uppercase version: */
     Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
-    prox = curl_getenv(proxy_env);
+    proxy = curl_getenv(proxy_env);
   }
 
   envp = proxy_env;
-  if(prox) {
-    proxy = prox; /* use this */
-  }
-  else {
-    envp = (char *)"all_proxy";
-    proxy = curl_getenv(envp); /* default proxy to use */
-    if(!proxy) {
-      envp = (char *)"ALL_PROXY";
-      proxy = curl_getenv(envp);
+  if(!proxy) {
+#ifdef USE_WEBSOCKETS
+    /* websocket proxy fallbacks */
+    if(strcasecompare("ws_proxy", proxy_env)) {
+      proxy = curl_getenv("http_proxy");
     }
+    else if(strcasecompare("wss_proxy", proxy_env)) {
+      proxy = curl_getenv("https_proxy");
+      if(!proxy)
+        proxy = curl_getenv("HTTPS_PROXY");
+    }
+    if(!proxy) {
+#endif
+      envp = (char *)"all_proxy";
+      proxy = curl_getenv(envp); /* default proxy to use */
+      if(!proxy) {
+        envp = (char *)"ALL_PROXY";
+        proxy = curl_getenv(envp);
+      }
+#ifdef USE_WEBSOCKETS
+    }
+#endif
   }
   if(proxy)
     infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
@@ -2113,7 +2141,7 @@
 
 /*
  * If this is supposed to use a proxy, we need to figure out the proxy
- * host name, so that we can re-use an existing connection
+ * host name, so that we can reuse an existing connection
  * that may exist registered to the same proxy host.
  */
 static CURLcode parse_proxy(struct Curl_easy *data,
@@ -2434,7 +2462,7 @@
 
   /***********************************************************************
    * If this is supposed to use a proxy, we need to figure out the proxy host
-   * name, proxy type and port number, so that we can re-use an existing
+   * name, proxy type and port number, so that we can reuse an existing
    * connection that may exist registered to the same proxy host.
    ***********************************************************************/
   if(proxy || socksproxy) {
@@ -2702,7 +2730,9 @@
                           data->set.str[STRING_NETRC_FILE]);
     if(ret > 0) {
       infof(data, "Couldn't find host %s in the %s file; using defaults",
-            conn->host.name, data->set.str[STRING_NETRC_FILE]);
+            conn->host.name,
+            (data->set.str[STRING_NETRC_FILE] ?
+             data->set.str[STRING_NETRC_FILE] : ".netrc"));
     }
     else if(ret < 0) {
       failf(data, ".netrc parser error");
@@ -3197,8 +3227,8 @@
   if(rc == CURLRESOLV_PENDING)
     *async = TRUE;
   else if(rc == CURLRESOLV_TIMEDOUT) {
-    failf(data, "Failed to resolve host '%s' with timeout after %ld ms",
-          connhost->dispname,
+    failf(data, "Failed to resolve host '%s' with timeout after %"
+          CURL_FORMAT_TIMEDIFF_T " ms", connhost->dispname,
           Curl_timediff(Curl_now(), data->progress.t_startsingle));
     return CURLE_OPERATION_TIMEDOUT;
   }
@@ -3252,7 +3282,7 @@
   /* Resolve the name of the server or proxy */
   if(conn->bits.reuse) {
     /* We're reusing the connection - no need to resolve anything, and
-       idnconvert_hostname() was called already in create_conn() for the re-use
+       idnconvert_hostname() was called already in create_conn() for the reuse
        case. */
     *async = FALSE;
     return CURLE_OK;
@@ -3271,7 +3301,7 @@
                        struct connectdata *existing)
 {
   /* get the user+password information from the temp struct since it may
-   * be new for this request even when we re-use an existing connection */
+   * be new for this request even when we reuse an existing connection */
   if(temp->user) {
     /* use the new user name and password though */
     Curl_safefree(existing->user);
@@ -3331,14 +3361,14 @@
   existing->hostname_resolve = temp->hostname_resolve;
   temp->hostname_resolve = NULL;
 
-  /* re-use init */
-  existing->bits.reuse = TRUE; /* yes, we're re-using here */
+  /* reuse init */
+  existing->bits.reuse = TRUE; /* yes, we're reusing here */
 
   conn_free(data, temp);
 }
 
 /**
- * create_conn() sets up a new connectdata struct, or re-uses an already
+ * create_conn() sets up a new connectdata struct, or reuses an already
  * existing one, and resolves host name.
  *
  * if this function returns CURLE_OK and *async is set to TRUE, the resolve
@@ -3650,7 +3680,7 @@
 
   /*************************************************************
    * Check the current list of connections to see if we can
-   * re-use an already existing one or if we have to create a
+   * reuse an already existing one or if we have to create a
    * new one.
    *************************************************************/
 
@@ -3658,7 +3688,7 @@
   DEBUGASSERT(conn->passwd);
 
   /* reuse_fresh is TRUE if we are told to use a new connection by force, but
-     we only acknowledge this option if this is not a re-used connection
+     we only acknowledge this option if this is not a reused connection
      already (which happens due to follow-location or during an HTTP
      authentication phase). CONNECT_ONLY transfers also refuse reuse. */
   if((data->set.reuse_fresh && !data->state.followlocation) ||
@@ -3678,15 +3708,14 @@
     *in_connect = conn;
 
 #ifndef CURL_DISABLE_PROXY
-    infof(data, "Re-using existing connection #%ld with %s %s",
-          conn->connection_id,
+    infof(data, "Re-using existing connection with %s %s",
           conn->bits.proxy?"proxy":"host",
           conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
           conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
           conn->host.dispname);
 #else
-    infof(data, "Re-using existing connection #%ld with host %s",
-          conn->connection_id, conn->host.dispname);
+    infof(data, "Re-using existing connection with host %s",
+          conn->host.dispname);
 #endif
   }
   else {
diff --git a/Utilities/cmcurl/lib/urlapi.c b/Utilities/cmcurl/lib/urlapi.c
index a4530f9..4efab61 100644
--- a/Utilities/cmcurl/lib/urlapi.c
+++ b/Utilities/cmcurl/lib/urlapi.c
@@ -100,7 +100,7 @@
 
 /*
  * Find the separator at the end of the host name, or the '?' in cases like
- * http://www.url.com?id=2380
+ * http://www.example.com?id=2380
  */
 static const char *find_host_sep(const char *url)
 {
@@ -201,7 +201,7 @@
 size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
                             bool guess_scheme)
 {
-  int i;
+  int i = 0;
   DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN));
   (void)buflen; /* only used in debug-builds */
   if(buf)
@@ -210,17 +210,18 @@
   if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url))
     return 0;
 #endif
-  for(i = 0; i < MAX_SCHEME_LEN; ++i) {
-    char s = url[i];
-    if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) {
-      /* RFC 3986 3.1 explains:
-        scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
-      */
+  if(ISALPHA(url[0]))
+    for(i = 1; i < MAX_SCHEME_LEN; ++i) {
+      char s = url[i];
+      if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) {
+        /* RFC 3986 3.1 explains:
+           scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+        */
+      }
+      else {
+        break;
+      }
     }
-    else {
-      break;
-    }
-  }
   if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) {
     /* If this does not guess scheme, the scheme always ends with the colon so
        that this also detects data: URLs etc. In guessing mode, data: could
@@ -337,7 +338,7 @@
       pathsep = strchr(protsep, '/');
       if(pathsep) {
         /* When people use badly formatted URLs, such as
-           "http://www.url.com?dir=/home/daniel" we must not use the first
+           "http://www.example.com?dir=/home/daniel" we must not use the first
            slash, if there's a ?-letter before it! */
         char *sep = strchr(protsep, '?');
         if(sep && (sep < pathsep))
@@ -346,9 +347,9 @@
       }
       else {
         /* There was no slash. Now, since we might be operating on a badly
-           formatted URL, such as "http://www.url.com?id=2380" which doesn't
-           use a slash separator as it is supposed to, we need to check for a
-           ?-letter as well! */
+           formatted URL, such as "http://www.example.com?id=2380" which
+           doesn't use a slash separator as it is supposed to, we need to check
+           for a ?-letter as well! */
         pathsep = strchr(protsep, '?');
         if(pathsep)
           *pathsep = 0;
@@ -1108,6 +1109,7 @@
     if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) {
       /* This cannot be done with strcpy, as the memory chunks overlap! */
       path++;
+      pathlen--;
     }
 #endif
 
@@ -1383,6 +1385,7 @@
     DUP(u, in, path);
     DUP(u, in, query);
     DUP(u, in, fragment);
+    DUP(u, in, zoneid);
     u->portnum = in->portnum;
   }
   return u;
@@ -1400,6 +1403,7 @@
   bool urldecode = (flags & CURLU_URLDECODE)?1:0;
   bool urlencode = (flags & CURLU_URLENCODE)?1:0;
   bool punycode = FALSE;
+  bool depunyfy = FALSE;
   bool plusdecode = FALSE;
   (void)flags;
   if(!u)
@@ -1430,6 +1434,7 @@
     ptr = u->host;
     ifmissing = CURLUE_NO_HOST;
     punycode = (flags & CURLU_PUNYCODE)?1:0;
+    depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
     break;
   case CURLUPART_ZONEID:
     ptr = u->zoneid;
@@ -1480,6 +1485,7 @@
     char *port = u->port;
     char *allochost = NULL;
     punycode = (flags & CURLU_PUNYCODE)?1:0;
+    depunyfy = (flags & CURLU_PUNY2IDN)?1:0;
     if(u->scheme && strcasecompare("file", u->scheme)) {
       url = aprintf("file://%s%s%s",
                     u->path,
@@ -1539,14 +1545,28 @@
 #ifndef USE_IDN
           return CURLUE_LACKS_IDN;
 #else
-          allochost = Curl_idn_decode(u->host);
-          if(!allochost)
-            return CURLUE_OUT_OF_MEMORY;
+          CURLcode result = Curl_idn_decode(u->host, &allochost);
+          if(result)
+            return (result == CURLE_OUT_OF_MEMORY) ?
+              CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
+#endif
+        }
+      }
+      else if(depunyfy) {
+        if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) {
+#ifndef USE_IDN
+          return CURLUE_LACKS_IDN;
+#else
+          CURLcode result = Curl_idn_encode(u->host, &allochost);
+          if(result)
+            /* this is the most likely error */
+            return (result == CURLE_OUT_OF_MEMORY) ?
+              CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
 #endif
         }
       }
 
-      url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+      url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
                     scheme,
                     u->user ? u->user : "",
                     u->password ? ":": "",
@@ -1557,7 +1577,6 @@
                     allochost ? allochost : u->host,
                     port ? ":": "",
                     port ? port : "",
-                    (u->path && (u->path[0] != '/')) ? "/": "",
                     u->path ? u->path : "/",
                     (u->query && u->query[0]) ? "?": "",
                     (u->query && u->query[0]) ? u->query : "",
@@ -1616,9 +1635,26 @@
 #ifndef USE_IDN
         return CURLUE_LACKS_IDN;
 #else
-        char *allochost = Curl_idn_decode(*part);
-        if(!allochost)
-          return CURLUE_OUT_OF_MEMORY;
+        char *allochost;
+        CURLcode result = Curl_idn_decode(*part, &allochost);
+        if(result)
+          return (result == CURLE_OUT_OF_MEMORY) ?
+            CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
+        free(*part);
+        *part = allochost;
+#endif
+      }
+    }
+    else if(depunyfy) {
+      if(Curl_is_ASCII_name(u->host)  && !strncmp("xn--", u->host, 4)) {
+#ifndef USE_IDN
+        return CURLUE_LACKS_IDN;
+#else
+        char *allochost;
+        CURLcode result = Curl_idn_encode(*part, &allochost);
+        if(result)
+          return (result == CURLE_OUT_OF_MEMORY) ?
+            CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME;
         free(*part);
         *part = allochost;
 #endif
@@ -1639,8 +1675,10 @@
   bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
   bool plusencode = FALSE;
   bool urlskipslash = FALSE;
+  bool leadingslash = FALSE;
   bool appendquery = FALSE;
   bool equalsencode = FALSE;
+  size_t nalloc;
 
   if(!u)
     return CURLUE_BAD_HANDLE;
@@ -1693,6 +1731,11 @@
     return CURLUE_OK;
   }
 
+  nalloc = strlen(part);
+  if(nalloc > CURL_MAX_INPUT_LENGTH)
+    /* excessive input length */
+    return CURLUE_MALFORMED_INPUT;
+
   switch(what) {
   case CURLUPART_SCHEME: {
     size_t plen = strlen(part);
@@ -1706,13 +1749,17 @@
       return CURLUE_UNSUPPORTED_SCHEME;
     storep = &u->scheme;
     urlencode = FALSE; /* never */
-    /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
-    while(plen--) {
-      if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
-        s++; /* fine */
-      else
-        return CURLUE_BAD_SCHEME;
+    if(ISALPHA(*s)) {
+      /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
+      while(--plen) {
+        if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
+          s++; /* fine */
+        else
+          return CURLUE_BAD_SCHEME;
+      }
     }
+    else
+      return CURLUE_BAD_SCHEME;
     break;
   }
   case CURLUPART_USER:
@@ -1746,6 +1793,7 @@
   break;
   case CURLUPART_PATH:
     urlskipslash = TRUE;
+    leadingslash = TRUE; /* enforce */
     storep = &u->path;
     break;
   case CURLUPART_QUERY:
@@ -1768,6 +1816,10 @@
     char *oldurl;
     char *redired_url;
 
+    if(!nalloc)
+      /* a blank URL is not a valid URL */
+      return CURLUE_MALFORMED_INPUT;
+
     /* if the new thing is absolute or the old one is not
      * (we could not get an absolute url in 'oldurl'),
      * then replace the existing with the new. */
@@ -1794,18 +1846,17 @@
   }
   DEBUGASSERT(storep);
   {
-    const char *newp = part;
-    size_t nalloc = strlen(part);
+    const char *newp;
+    struct dynbuf enc;
+    Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
 
-    if(nalloc > CURL_MAX_INPUT_LENGTH)
-      /* excessive input length */
-      return CURLUE_MALFORMED_INPUT;
-
+    if(leadingslash && (part[0] != '/')) {
+      CURLcode result = Curl_dyn_addn(&enc, "/", 1);
+      if(result)
+        return CURLUE_OUT_OF_MEMORY;
+    }
     if(urlencode) {
       const unsigned char *i;
-      struct dynbuf enc;
-
-      Curl_dyn_init(&enc, nalloc * 3 + 1);
 
       for(i = (const unsigned char *)part; *i; i++) {
         CURLcode result;
@@ -1814,7 +1865,7 @@
           if(result)
             return CURLUE_OUT_OF_MEMORY;
         }
-        else if(Curl_isunreserved(*i) ||
+        else if(ISUNRESERVED(*i) ||
                 ((*i == '/') && urlskipslash) ||
                 ((*i == '=') && equalsencode)) {
           if((*i == '=') && equalsencode)
@@ -1833,14 +1884,13 @@
             return CURLUE_OUT_OF_MEMORY;
         }
       }
-      newp = Curl_dyn_ptr(&enc);
     }
     else {
       char *p;
-      newp = strdup(part);
-      if(!newp)
+      CURLcode result = Curl_dyn_add(&enc, part);
+      if(result)
         return CURLUE_OUT_OF_MEMORY;
-      p = (char *)newp;
+      p = Curl_dyn_ptr(&enc);
       while(*p) {
         /* make sure percent encoded are lower case */
         if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
@@ -1853,6 +1903,7 @@
           p++;
       }
     }
+    newp = Curl_dyn_ptr(&enc);
 
     if(appendquery) {
       /* Append the 'newp' string onto the old query. Add a '&' separator if
@@ -1861,24 +1912,24 @@
       size_t querylen = u->query ? strlen(u->query) : 0;
       bool addamperand = querylen && (u->query[querylen -1] != '&');
       if(querylen) {
-        struct dynbuf enc;
-        Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+        struct dynbuf qbuf;
+        Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
 
-        if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */
+        if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
           goto nomem;
 
         if(addamperand) {
-          if(Curl_dyn_addn(&enc, "&", 1))
+          if(Curl_dyn_addn(&qbuf, "&", 1))
             goto nomem;
         }
-        if(Curl_dyn_add(&enc, newp))
+        if(Curl_dyn_add(&qbuf, newp))
           goto nomem;
-        free((char *)newp);
+        Curl_dyn_free(&enc);
         free(*storep);
-        *storep = Curl_dyn_ptr(&enc);
+        *storep = Curl_dyn_ptr(&qbuf);
         return CURLUE_OK;
 nomem:
-        free((char *)newp);
+        Curl_dyn_free(&enc);
         return CURLUE_OUT_OF_MEMORY;
       }
     }
@@ -1890,7 +1941,7 @@
       }
       else {
         if(!n || hostname_check(u, (char *)newp, n)) {
-          free((char *)newp);
+          Curl_dyn_free(&enc);
           return CURLUE_BAD_HOSTNAME;
         }
       }
diff --git a/Utilities/cmcurl/lib/urldata.h b/Utilities/cmcurl/lib/urldata.h
index f02e665..dff26e6 100644
--- a/Utilities/cmcurl/lib/urldata.h
+++ b/Utilities/cmcurl/lib/urldata.h
@@ -101,6 +101,12 @@
 #define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS)
 #define PROTO_FAMILY_SSH  (CURLPROTO_SCP|CURLPROTO_SFTP)
 
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH) ||   \
+  !defined(CURL_DISABLE_POP3)
+/* these protocols support CURLOPT_DIRLISTONLY */
+#define CURL_LIST_ONLY_PROTOCOL 1
+#endif
+
 #define DEFAULT_CONNCACHE_SIZE 5
 
 /* length of longest IPv6 address string including the trailing null */
@@ -330,6 +336,7 @@
 #include "curl_sspi.h"
 #endif
 
+#ifndef CURL_DISABLE_DIGEST_AUTH
 /* Struct used for Digest challenge-response authentication */
 struct digestdata {
 #if defined(USE_WINDOWS_SSPI)
@@ -353,6 +360,7 @@
   BIT(userhash);
 #endif
 };
+#endif
 
 typedef enum {
   NTLMSTATE_NONE,
@@ -489,7 +497,7 @@
 #endif
   /* always modify bits.close with the connclose() and connkeep() macros! */
   BIT(close); /* if set, we close the connection after this request */
-  BIT(reuse); /* if set, this is a re-used connection */
+  BIT(reuse); /* if set, this is a reused connection */
   BIT(altused); /* this is an alt-svc "redirect" */
   BIT(conn_to_host); /* if set, this connection has a "connect to host"
                         that overrides the host in the URL */
@@ -629,17 +637,18 @@
   curl_off_t bytecount;         /* total number of bytes read */
   curl_off_t writebytecount;    /* number of bytes written */
 
-  curl_off_t headerbytecount;   /* only count received headers */
-  curl_off_t deductheadercount; /* this amount of bytes doesn't count when we
-                                   check if anything has been transferred at
-                                   the end of a connection. We use this
-                                   counter to make only a 100 reply (without a
-                                   following second response code) result in a
-                                   CURLE_GOT_NOTHING error code */
-
   curl_off_t pendingheader;      /* this many bytes left to send is actually
                                     header and not body */
   struct curltime start;         /* transfer started at this time */
+  unsigned int headerbytecount;  /* received server headers (not CONNECT
+                                    headers) */
+  unsigned int allheadercount;   /* all received headers (server + CONNECT) */
+  unsigned int deductheadercount; /* this amount of bytes doesn't count when
+                                     we check if anything has been transferred
+                                     at the end of a connection. We use this
+                                     counter to make only a 100 reply (without
+                                     a following second response code) result
+                                     in a CURLE_GOT_NOTHING error code */
   enum {
     HEADER_NORMAL,              /* no bad header at all */
     HEADER_PARTHEADER,          /* part of the chunk is a bad header, the rest
@@ -701,7 +710,9 @@
   struct curltime last_sndbuf_update;  /* last time readwrite_upload called
                                           win_update_buffer_size */
 #endif
+#ifndef CURL_DISABLE_COOKIES
   unsigned char setcookies;
+#endif
   unsigned char writer_stack_depth; /* Unencoding stack depth. */
   BIT(header);        /* incoming data has HTTP header */
   BIT(content_range); /* set TRUE if Content-Range: was found */
@@ -747,7 +758,7 @@
    * after the connect() and everything is done, as a step in the connection.
    * The 'done' pointer points to a bool that should be set to TRUE if the
    * function completes before return. If it doesn't complete, the caller
-   * should call the curl_connecting() function until it is.
+   * should call the ->connecting() function until it is.
    */
   CURLcode (*connect_it)(struct Curl_easy *data, bool *done);
 
@@ -882,12 +893,12 @@
 #define CONN_INUSE(c) ((c)->easyq.size)
 
   /**** Fields set when inited and not modified again */
-  long connection_id; /* Contains a unique number to make it easier to
-                         track the connections in the log output */
+  curl_off_t connection_id; /* Contains a unique number to make it easier to
+                               track the connections in the log output */
 
   /* 'dns_entry' is the particular host we use. This points to an entry in the
      DNS cache and it will not get pruned while locked. It gets unlocked in
-     multi_done(). This entry will be NULL if the connection is re-used as then
+     multi_done(). This entry will be NULL if the connection is reused as then
      there is no name resolve done. */
   struct Curl_dns_entry *dns_entry;
 
@@ -1024,14 +1035,19 @@
 #ifndef CURL_DISABLE_SMB
     struct smb_conn smbc;
 #endif
+#ifdef USE_LIBRTMP
     void *rtmp;
+#endif
+#ifdef USE_OPENLDAP
     struct ldapconninfo *ldapc;
+#endif
 #ifndef CURL_DISABLE_MQTT
     struct mqtt_conn mqtt;
 #endif
 #ifdef USE_WEBSOCKETS
     struct websocket *ws;
 #endif
+    unsigned int unused:1; /* avoids empty union */
   } proto;
 
   struct connectbundle *bundle; /* The bundle we are member of */
@@ -1045,7 +1061,7 @@
   /* When this connection is created, store the conditions for the local end
      bind. This is stored before the actual bind and before any connection is
      made and will serve the purpose of being used for comparison reasons so
-     that subsequent bound-requested connections aren't accidentally re-using
+     that subsequent bound-requested connections aren't accidentally reusing
      wrong connections. */
   char *localdev;
   unsigned short localportrange;
@@ -1077,6 +1093,18 @@
   unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */
 };
 
+#ifndef CURL_DISABLE_PROXY
+#define CURL_CONN_HOST_DISPNAME(c) \
+          ((c)->bits.socksproxy ? (c)->socks_proxy.host.dispname : \
+            (c)->bits.httpproxy ? (c)->http_proxy.host.dispname : \
+              (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \
+                (c)->host.dispname)
+#else
+#define CURL_CONN_HOST_DISPNAME(c) \
+          (c)->bits.conn_to_host ? (c)->conn_to_host.dispname : \
+            (c)->host.dispname
+#endif
+
 /* The end of connectdata. */
 
 /*
@@ -1089,7 +1117,6 @@
   int httpversion; /* the http version number X.Y = X*10+Y */
   time_t filetime; /* If requested, this is might get set. Set to -1 if the
                       time was unretrievable. */
-  curl_off_t header_size;  /* size of read header(s) in bytes */
   curl_off_t request_size; /* the amount of bytes sent in the request(s) */
   unsigned long proxyauthavail; /* what proxy auth types were announced */
   unsigned long httpauthavail;  /* what host auth types were announced */
@@ -1097,6 +1124,7 @@
   char *contenttype; /* the content type of the object */
   char *wouldredirect; /* URL this would've been redirected to if asked to */
   curl_off_t retry_after; /* info from Retry-After: header */
+  unsigned int header_size;  /* size of read header(s) in bytes */
 
   /* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
      and, 'conn_local_port' are copied over from the connectdata struct in
@@ -1116,10 +1144,8 @@
   int conn_local_port;
   const char *conn_scheme;
   unsigned int conn_protocol;
-  struct curl_certinfo certs; /* info about the certs, only populated in
-                                 OpenSSL, GnuTLS, Schannel, NSS and GSKit
-                                 builds. Asked for with CURLOPT_CERTINFO
-                                 / CURLINFO_CERTINFO */
+  struct curl_certinfo certs; /* info about the certs. Asked for with
+                                 CURLOPT_CERTINFO / CURLINFO_CERTINFO */
   CURLproxycode pxcode;
   BIT(timecond);  /* set to TRUE if the time condition didn't match, which
                      thus made the document NOT get fetched */
@@ -1236,6 +1262,7 @@
   struct dynbuf b;
   int type;   /* type of the 'tempwrite' buffer as a bitmask that is used with
                  Curl_client_write() */
+  BIT(paused_body); /* if PAUSE happened before/during BODY write */
 };
 
 /* Timers */
@@ -1294,7 +1321,9 @@
   /* buffers to store authentication data in, as parsed from input options */
   struct curltime keeps_speed; /* for the progress meter really */
 
-  long lastconnect_id; /* The last connection, -1 if undefined */
+  curl_off_t lastconnect_id; /* The last connection, -1 if undefined */
+  curl_off_t recent_conn_id; /* The most recent connection used, might no
+                              * longer exist */
   struct dynbuf headerb; /* buffer to store headers in */
 
   char *buffer; /* download buffer */
@@ -1323,7 +1352,7 @@
   /* storage for the previous bag^H^H^HSIGPIPE signal handler :-) */
   void (*prev_signal)(int sig);
 #endif
-#ifndef CURL_DISABLE_CRYPTO_AUTH
+#ifndef CURL_DISABLE_DIGEST_AUTH
   struct digestdata digest;      /* state data for host Digest auth */
   struct digestdata proxydigest; /* state data for proxy Digest auth */
 #endif
@@ -1381,6 +1410,9 @@
   struct curl_slist *resolve; /* set to point to the set.resolve list when
                                  this should be dealt with in pretransfer */
 #ifndef CURL_DISABLE_HTTP
+  curl_mimepart *mimepost;
+  curl_mimepart *formp; /* storage for old API form-posting, alloced on
+                           demand */
   size_t trailers_bytes_sent;
   struct dynbuf trailers_buf; /* a buffer containing the compiled trailing
                                  headers */
@@ -1452,9 +1484,13 @@
                 when multi_done() is called, to prevent multi_done() to get
                 invoked twice when the multi interface is used. */
   BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
+#ifndef CURL_DISABLE_COOKIES
   BIT(cookie_engine);
+#endif
   BIT(prefer_ascii);   /* ASCII rather than binary */
+#ifdef CURL_LIST_ONLY_PROTOCOL
   BIT(list_only);      /* list directory contents */
+#endif
   BIT(url_alloc);   /* URL string is malloc()'ed */
   BIT(referer_alloc); /* referer string is malloc()ed */
   BIT(wildcard_resolve); /* Set to true if any resolve change is a wildcard */
@@ -1563,6 +1599,7 @@
   STRING_DNS_LOCAL_IP6,
   STRING_SSL_EC_CURVES,
   STRING_AWS_SIGV4, /* Parameters for V4 signature */
+  STRING_HAPROXY_CLIENT_IP,     /* CURLOPT_HAPROXY_CLIENT_IP */
 
   /* -- end of null-terminated strings -- */
 
@@ -1610,10 +1647,12 @@
   curl_off_t postfieldsize; /* if POST, this might have a size to use instead
                                of strlen(), and then the data *may* be binary
                                (contain zero bytes) */
+#ifndef CURL_DISABLE_BINDLOCAL
   unsigned short localport; /* local port number to bind to */
   unsigned short localportrange; /* number of additional port numbers to test
                                     in case the 'localport' one can't be
                                     bind()ed */
+#endif
   curl_write_callback fwrite_func;   /* function that stores the output */
   curl_write_callback fwrite_header; /* function that stores headers */
   curl_write_callback fwrite_rtp;    /* function that stores interleaved RTP */
@@ -1801,7 +1840,9 @@
   BIT(tftp_no_options); /* do not send TFTP options requests */
 #endif
   BIT(sep_headers);     /* handle host and proxy headers separately */
+#ifndef CURL_DISABLE_COOKIES
   BIT(cookiesession);   /* new cookie session? */
+#endif
   BIT(crlf);            /* convert crlf on ftp upload(?) */
   BIT(ssh_compression);            /* enable SSH compression */
 
@@ -1816,7 +1857,9 @@
   BIT(tunnel_thru_httpproxy); /* use CONNECT through an HTTP proxy */
   BIT(prefer_ascii);     /* ASCII rather than binary */
   BIT(remote_append);    /* append, not overwrite, on upload */
+#ifdef CURL_LIST_ONLY_PROTOCOL
   BIT(list_only);        /* list directory */
+#endif
 #ifndef CURL_DISABLE_FTP
   BIT(ftp_use_port);     /* use the FTP PORT command */
   BIT(ftp_use_epsv);     /* if EPSV is to be attempted or not */
@@ -1840,7 +1883,7 @@
   BIT(verbose);        /* output verbosity */
   BIT(krb);            /* Kerberos connection requested */
   BIT(reuse_forbid);   /* forbidden to be reused, close after use */
-  BIT(reuse_fresh);    /* do not re-use an existing connection  */
+  BIT(reuse_fresh);    /* do not reuse an existing connection  */
   BIT(no_signal);      /* do not use any signal/alarm handler */
   BIT(tcp_nodelay);    /* whether to enable TCP_NODELAY or not */
   BIT(ignorecl);       /* ignore content length */
@@ -1902,6 +1945,13 @@
   /* First a simple identifier to easier detect if a user mix up this easy
      handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */
   unsigned int magic;
+  /* once an easy handle is tied to a connection cache
+     a non-negative number to distinguish this transfer from
+     other using the same cache. For easier tracking
+     in log output.
+     This may wrap around after LONG_MAX to 0 again, so it
+     has no uniqueness guarantee for very large processings. */
+  curl_off_t id;
 
   /* first, two fields for the linked list of these */
   struct Curl_easy *next;
@@ -1963,6 +2013,10 @@
 #ifdef USE_HYPER
   struct hyptransfer hyp;
 #endif
+
+  /* internal: true if this easy handle was created for internal use and the
+     user does not have ownership of the handle. */
+  bool internal;
 };
 
 #define LIBCURL_NAME "libcurl"
diff --git a/Utilities/cmcurl/lib/vauth/cleartext.c b/Utilities/cmcurl/lib/vauth/cleartext.c
index c651fc5..972a874 100644
--- a/Utilities/cmcurl/lib/vauth/cleartext.c
+++ b/Utilities/cmcurl/lib/vauth/cleartext.c
@@ -35,7 +35,6 @@
 #include "urldata.h"
 
 #include "vauth/vauth.h"
-#include "curl_md5.h"
 #include "warnless.h"
 #include "strtok.h"
 #include "sendf.h"
diff --git a/Utilities/cmcurl/lib/vauth/cram.c b/Utilities/cmcurl/lib/vauth/cram.c
index 5894ed4..91fb261 100644
--- a/Utilities/cmcurl/lib/vauth/cram.c
+++ b/Utilities/cmcurl/lib/vauth/cram.c
@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#ifndef CURL_DISABLE_DIGEST_AUTH
 
 #include <curl/curl.h>
 #include "urldata.h"
@@ -94,4 +94,4 @@
   return CURLE_OK;
 }
 
-#endif /* !CURL_DISABLE_CRYPTO_AUTH */
+#endif /* !CURL_DISABLE_DIGEST_AUTH */
diff --git a/Utilities/cmcurl/lib/vauth/digest.c b/Utilities/cmcurl/lib/vauth/digest.c
index fda2d91..12c6f7d 100644
--- a/Utilities/cmcurl/lib/vauth/digest.c
+++ b/Utilities/cmcurl/lib/vauth/digest.c
@@ -27,7 +27,7 @@
 
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#ifndef CURL_DISABLE_DIGEST_AUTH
 
 #include <curl/curl.h>
 
@@ -420,7 +420,7 @@
     msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
 
   /* Generate our SPN */
-  spn = Curl_auth_build_spn(service, realm, NULL);
+  spn = Curl_auth_build_spn(service, data->conn->host.name, NULL);
   if(!spn)
     return CURLE_OUT_OF_MEMORY;
 
@@ -992,4 +992,4 @@
 }
 #endif  /* !USE_WINDOWS_SSPI */
 
-#endif  /* CURL_DISABLE_CRYPTO_AUTH */
+#endif  /* !CURL_DISABLE_DIGEST_AUTH */
diff --git a/Utilities/cmcurl/lib/vauth/digest.h b/Utilities/cmcurl/lib/vauth/digest.h
index 68fdb28..99ce1f9 100644
--- a/Utilities/cmcurl/lib/vauth/digest.h
+++ b/Utilities/cmcurl/lib/vauth/digest.h
@@ -26,7 +26,7 @@
 
 #include <curl/curl.h>
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#ifndef CURL_DISABLE_DIGEST_AUTH
 
 #define DIGEST_MAX_VALUE_LENGTH           256
 #define DIGEST_MAX_CONTENT_LENGTH         1024
diff --git a/Utilities/cmcurl/lib/vauth/digest_sspi.c b/Utilities/cmcurl/lib/vauth/digest_sspi.c
index 8fb8669..02e36ea 100644
--- a/Utilities/cmcurl/lib/vauth/digest_sspi.c
+++ b/Utilities/cmcurl/lib/vauth/digest_sspi.c
@@ -27,7 +27,7 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include <curl/curl.h>
 
@@ -665,4 +665,4 @@
   Curl_safefree(digest->passwd);
 }
 
-#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */
+#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_DIGEST_AUTH */
diff --git a/Utilities/cmcurl/lib/vauth/ntlm.c b/Utilities/cmcurl/lib/vauth/ntlm.c
index 93096ba..ed7cee8 100644
--- a/Utilities/cmcurl/lib/vauth/ntlm.c
+++ b/Utilities/cmcurl/lib/vauth/ntlm.c
@@ -45,12 +45,6 @@
 #include "rand.h"
 #include "vtls/vtls.h"
 
-/* SSL backend-specific #if branches in this file must be kept in the order
-   documented in curl_ntlm_core. */
-#if defined(NTLM_NEEDS_NSS_INIT)
-#include "vtls/nssg.h" /* for Curl_nss_force_init() */
-#endif
-
 #define BUILDING_CURL_NTLM_MSGS_C
 #include "vauth/vauth.h"
 #include "vauth/ntlm.h"
@@ -274,12 +268,7 @@
   const unsigned char *type2 = Curl_bufref_ptr(type2ref);
   size_t type2len = Curl_bufref_len(type2ref);
 
-#if defined(NTLM_NEEDS_NSS_INIT)
-  /* Make sure the crypto backend is initialized */
-  result = Curl_nss_force_init(data);
-  if(result)
-    return result;
-#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
   (void)data;
 #endif
 
diff --git a/Utilities/cmcurl/lib/vauth/vauth.h b/Utilities/cmcurl/lib/vauth/vauth.h
index d8cff24..9da0540 100644
--- a/Utilities/cmcurl/lib/vauth/vauth.h
+++ b/Utilities/cmcurl/lib/vauth/vauth.h
@@ -30,7 +30,7 @@
 
 struct Curl_easy;
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_DIGEST_AUTH)
 struct digestdata;
 #endif
 
@@ -86,7 +86,7 @@
 CURLcode Curl_auth_create_external_message(const char *user,
                                            struct bufref *out);
 
-#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+#ifndef CURL_DISABLE_DIGEST_AUTH
 /* This is used to generate a CRAM-MD5 response message */
 CURLcode Curl_auth_create_cram_md5_message(const struct bufref *chlg,
                                            const char *userp,
@@ -119,7 +119,7 @@
 
 /* This is used to clean up the digest specific data */
 void Curl_auth_digest_cleanup(struct digestdata *digest);
-#endif /* !CURL_DISABLE_CRYPTO_AUTH */
+#endif /* !CURL_DISABLE_DIGEST_AUTH */
 
 #ifdef USE_GSASL
 /* This is used to evaluate if MECH is supported by gsasl */
diff --git a/Utilities/cmcurl/lib/version.c b/Utilities/cmcurl/lib/version.c
index 5800ad3..f99dd38 100644
--- a/Utilities/cmcurl/lib/version.c
+++ b/Utilities/cmcurl/lib/version.c
@@ -300,7 +300,7 @@
    protocol line has its own #if line to make things easier on the eye.
  */
 
-static const char * const protocols[] = {
+static const char * const supported_protocols[] = {
 #ifndef CURL_DISABLE_DICT
   "dict",
 #endif
@@ -535,7 +535,7 @@
   NULL, /* ssl_version */
   0,    /* ssl_version_num, this is kept at zero */
   NULL, /* zlib_version */
-  protocols,
+  supported_protocols,
   NULL, /* c-ares version */
   0,    /* c-ares version numerical */
   NULL, /* libidn version */
diff --git a/Utilities/cmcurl/lib/vquic/curl_msh3.c b/Utilities/cmcurl/lib/vquic/curl_msh3.c
index 1738867..6bd0d23 100644
--- a/Utilities/cmcurl/lib/vquic/curl_msh3.c
+++ b/Utilities/cmcurl/lib/vquic/curl_msh3.c
@@ -30,7 +30,7 @@
 #include "timeval.h"
 #include "multiif.h"
 #include "sendf.h"
-#include "curl_log.h"
+#include "curl_trc.h"
 #include "cfilters.h"
 #include "cf-socket.h"
 #include "connect.h"
@@ -123,6 +123,7 @@
 };
 
 /* How to access `call_data` from a cf_msh3 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
 
@@ -172,7 +173,7 @@
   msh3_lock_initialize(&stream->recv_lock);
   Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data));
+  CURL_TRC_CF(data, cf, "data setup");
   return CURLE_OK;
 }
 
@@ -182,7 +183,7 @@
 
   (void)cf;
   if(stream) {
-    DEBUGF(LOG_CF(data, cf, "easy handle is done"));
+    CURL_TRC_CF(data, cf, "easy handle is done");
     Curl_bufq_free(&stream->recvbuf);
     free(stream);
     H3_STREAM_LCTX(data) = NULL;
@@ -234,7 +235,7 @@
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   (void)Connection;
 
-  DEBUGF(LOG_CF(data, cf, "[MSH3] connected"));
+  CURL_TRC_CF(data, cf, "[MSH3] connected");
   ctx->handshake_succeeded = true;
   ctx->connected = true;
   ctx->handshake_complete = true;
@@ -248,7 +249,7 @@
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
 
   (void)Connection;
-  DEBUGF(LOG_CF(data, cf, "[MSH3] shutdown complete"));
+  CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
   ctx->connected = false;
   ctx->handshake_complete = true;
 }
@@ -473,18 +474,18 @@
   if(stream->reset) {
     failf(data, "HTTP/3 stream reset by server");
     *err = CURLE_PARTIAL_FILE;
-    DEBUGF(LOG_CF(data, cf, "cf_recv, was reset -> %d", *err));
+    CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err);
     goto out;
   }
   else if(stream->error3) {
     failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
           (ssize_t)stream->error3);
     *err = CURLE_HTTP3;
-    DEBUGF(LOG_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err));
+    CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err);
     goto out;
   }
   else {
-    DEBUGF(LOG_CF(data, cf, "cf_recv, closed ok -> %d", *err));
+    CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err);
   }
   *err = CURLE_OK;
   nread = 0;
@@ -522,7 +523,7 @@
     return -1;
   }
   CF_DATA_SAVE(save, cf, data);
-  DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
+  CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len);
 
   msh3_lock_acquire(&stream->recv_lock);
 
@@ -537,8 +538,8 @@
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
-                  len, nread, *err));
+    CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
+                len, nread, *err);
     if(nread < 0)
       goto out;
     if(stream->closed)
@@ -549,7 +550,7 @@
     goto out;
   }
   else {
-    DEBUGF(LOG_CF(data, cf, "req: nothing here, call again"));
+    CURL_TRC_CF(data, cf, "req: nothing here, call again");
     *err = CURLE_AGAIN;
   }
 
@@ -580,7 +581,7 @@
 
   /* Sizes must match for cast below to work" */
   DEBUGASSERT(stream);
-  DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
+  CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
 
   if(!stream->req) {
     /* The first send on the request contains the headers and possibly some
@@ -629,7 +630,7 @@
       break;
     }
 
-    DEBUGF(LOG_CF(data, cf, "req: send %zu headers", nheader));
+    CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
     stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
                                   nva, nheader,
                                   eos ? MSH3_REQUEST_FLAG_FIN :
@@ -645,7 +646,7 @@
   }
   else {
     /* request is open */
-    DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len));
+    CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
     if(len > 0xFFFFFFFF) {
       len = 0xFFFFFFFF;
     }
@@ -693,7 +694,7 @@
       drain_stream(cf, data);
     }
   }
-  DEBUGF(LOG_CF(data, cf, "select_sock -> %d", bitmap));
+  CURL_TRC_CF(data, cf, "select_sock -> %d", bitmap);
   CF_DATA_RESTORE(cf, save);
   return bitmap;
 }
@@ -710,8 +711,8 @@
   (void)cf;
   if(stream && stream->req) {
     msh3_lock_acquire(&stream->recv_lock);
-    DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %zu",
-                  Curl_bufq_len(&stream->recvbuf)));
+    CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
+                Curl_bufq_len(&stream->recvbuf));
     pending = !Curl_bufq_is_empty(&stream->recvbuf);
     msh3_lock_release(&stream->recv_lock);
     if(pending)
@@ -773,7 +774,7 @@
     h3_data_done(cf, data);
     break;
   case CF_CTRL_DATA_DONE_SEND:
-    DEBUGF(LOG_CF(data, cf, "req: send done"));
+    CURL_TRC_CF(data, cf, "req: send done");
     if(stream) {
       stream->upload_done = TRUE;
       if(stream->req) {
@@ -786,7 +787,7 @@
     }
     break;
   case CF_CTRL_CONN_INFO_UPDATE:
-    DEBUGF(LOG_CF(data, cf, "req: update info"));
+    CURL_TRC_CF(data, cf, "req: update info");
     cf_msh3_active(cf, data);
     break;
   default:
@@ -812,17 +813,17 @@
     /* TODO: need a way to provide trust anchors to MSH3 */
 #ifdef DEBUGBUILD
     /* we need this for our test cases to run */
-    DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
-                  "switching off verifypeer in DEBUG mode"));
+    CURL_TRC_CF(data, cf, "non-standard CA not supported, "
+                "switching off verifypeer in DEBUG mode");
     verify = 0;
 #else
-    DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
-                  "attempting with built-in verification"));
+    CURL_TRC_CF(data, cf, "non-standard CA not supported, "
+                "attempting with built-in verification");
 #endif
   }
 
-  DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)",
-                cf->conn->host.name, (int)cf->conn->remote_port, verify));
+  CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
+              cf->conn->host.name, (int)cf->conn->remote_port, verify);
 
   ctx->api = MsH3ApiOpen();
   if(!ctx->api) {
@@ -887,7 +888,7 @@
   if(ctx->handshake_complete) {
     ctx->handshake_at = Curl_now();
     if(ctx->handshake_succeeded) {
-      DEBUGF(LOG_CF(data, cf, "handshake succeeded"));
+      CURL_TRC_CF(data, cf, "handshake succeeded");
       cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
       cf->conn->httpversion = 30;
       cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
@@ -917,7 +918,7 @@
   CF_DATA_SAVE(save, cf, data);
 
   if(ctx) {
-    DEBUGF(LOG_CF(data, cf, "destroying"));
+    CURL_TRC_CF(data, cf, "destroying");
     if(ctx->qconn) {
       MsH3ConnectionClose(ctx->qconn);
       ctx->qconn = NULL;
@@ -934,13 +935,13 @@
        */
       ctx->active = FALSE;
       if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
-        DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active",
-                      (int)ctx->sock[SP_LOCAL]));
+        CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
+                    (int)ctx->sock[SP_LOCAL]);
         cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
       }
       else {
-        DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
-                      "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]));
+        CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
+                    "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
         ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
       }
       if(cf->sockindex == FIRSTSOCKET)
diff --git a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
index 7627940..7d681e5 100644
--- a/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
+++ b/Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
@@ -30,10 +30,10 @@
 
 #ifdef USE_OPENSSL
 #include <openssl/err.h>
-#ifdef OPENSSL_IS_BORINGSSL
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
 #include <ngtcp2/ngtcp2_crypto_boringssl.h>
 #else
-#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#include <ngtcp2/ngtcp2_crypto_quictls.h>
 #endif
 #include "vtls/openssl.h"
 #elif defined(USE_GNUTLS)
@@ -58,6 +58,7 @@
 #include "dynbuf.h"
 #include "http1.h"
 #include "select.h"
+#include "inet_pton.h"
 #include "vquic.h"
 #include "vquic_int.h"
 #include "vtls/keylog.h"
@@ -162,20 +163,26 @@
   size_t max_stream_window;          /* max flow window for one stream */
   int qlogfd;
   BIT(got_first_byte);               /* if first byte was received */
+#ifdef USE_OPENSSL
+  BIT(x509_store_setup);             /* if x509 store has been set up */
+#endif
 };
 
 /* How to access `call_data` from a cf_ngtcp2 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
 
 /**
  * All about the H3 internals of a stream
  */
-struct stream_ctx {
+struct h3_stream_ctx {
   int64_t id; /* HTTP/3 protocol identifier */
   struct bufq sendbuf;   /* h3 request body */
   struct bufq recvbuf;   /* h3 response body */
+  struct h1_req_parser h1; /* h1 request parsing */
   size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
+  size_t upload_blocked_len; /* the amount written last and EGAINed */
   size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
   uint64_t error3; /* HTTP/3 stream error code */
   curl_off_t upload_left; /* number of request bytes left to upload */
@@ -186,18 +193,18 @@
   bool send_closed; /* stream is local closed */
 };
 
-#define H3_STREAM_CTX(d)    ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
-                             ((struct HTTP *)(d)->req.p.http)->h3_ctx \
-                               : NULL))
-#define H3_STREAM_LCTX(d)   ((struct HTTP *)(d)->req.p.http)->h3_ctx
-#define H3_STREAM_ID(d)     (H3_STREAM_CTX(d)? \
-                             H3_STREAM_CTX(d)->id : -2)
+#define H3_STREAM_CTX(d)  ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \
+                           ((struct HTTP *)(d)->req.p.http)->h3_ctx \
+                             : NULL))
+#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
+#define H3_STREAM_ID(d)   (H3_STREAM_CTX(d)? \
+                           H3_STREAM_CTX(d)->id : -2)
 
 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
 
   if(!data || !data->req.p.http) {
     failf(data, "initialization failure, transfer not http initialized");
@@ -221,22 +228,22 @@
   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
   stream->recv_buf_nonflow = 0;
+  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
 
   H3_STREAM_LCTX(data) = stream;
-  DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data));
   return CURLE_OK;
 }
 
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)cf;
   if(stream) {
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done",
-                  stream->id));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
     Curl_bufq_free(&stream->sendbuf);
     Curl_bufq_free(&stream->recvbuf);
+    Curl_h1_req_parse_free(&stream->h1);
     free(stream);
     H3_STREAM_LCTX(data) = NULL;
   }
@@ -246,10 +253,37 @@
    the maximum packet burst to MAX_PKT_BURST packets. */
 #define MAX_PKT_BURST 10
 
-static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data);
-static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
-                                struct Curl_easy *data);
+struct pkt_io_ctx {
+  struct Curl_cfilter *cf;
+  struct Curl_easy *data;
+  ngtcp2_tstamp ts;
+  size_t pkt_count;
+  ngtcp2_path_storage ps;
+};
+
+static ngtcp2_tstamp timestamp(void)
+{
+  struct curltime ct = Curl_now();
+  return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+}
+
+static void pktx_init(struct pkt_io_ctx *pktx,
+                      struct Curl_cfilter *cf,
+                      struct Curl_easy *data)
+{
+  pktx->cf = cf;
+  pktx->data = data;
+  pktx->ts = timestamp();
+  pktx->pkt_count = 0;
+  ngtcp2_path_storage_zero(&pktx->ps);
+}
+
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct pkt_io_ctx *pktx);
+static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct pkt_io_ctx *pktx);
 static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
                                    uint64_t datalen, void *user_data,
                                    void *stream_user_data);
@@ -261,12 +295,6 @@
   return ctx->qconn;
 }
 
-static ngtcp2_tstamp timestamp(void)
-{
-  struct curltime ct = Curl_now();
-  return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
-}
-
 #ifdef DEBUG_NGTCP2
 static void quic_printf(void *user_data, const char *fmt, ...)
 {
@@ -300,7 +328,8 @@
 }
 
 static void quic_settings(struct cf_ngtcp2_ctx *ctx,
-                          struct Curl_easy *data)
+                          struct Curl_easy *data,
+                          struct pkt_io_ctx *pktx)
 {
   ngtcp2_settings *s = &ctx->settings;
   ngtcp2_transport_params *t = &ctx->transport_params;
@@ -314,7 +343,7 @@
 #endif
 
   (void)data;
-  s->initial_ts = timestamp();
+  s->initial_ts = pktx->ts;
   s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
   s->max_window = 100 * ctx->max_stream_window;
   s->max_stream_window = ctx->max_stream_window;
@@ -327,7 +356,7 @@
   t->initial_max_streams_uni = QUIC_MAX_STREAMS;
   t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
   if(ctx->qlogfd != -1) {
-    s->qlog.write = qlog_callback;
+    s->qlog_write = qlog_callback;
   }
 }
 
@@ -368,6 +397,7 @@
 static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
                              struct Curl_cfilter *cf, struct Curl_easy *data)
 {
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct connectdata *conn = cf->conn;
   CURLcode result = CURLE_FAILED_INIT;
   SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
@@ -377,36 +407,38 @@
     goto out;
   }
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
   if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
     failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
     goto out;
   }
 #else
-  if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
-    failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
+  if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
+    failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
     goto out;
   }
 #endif
 
   SSL_CTX_set_default_verify_paths(ssl_ctx);
 
-#ifdef OPENSSL_IS_BORINGSSL
-  if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
-    failf(data, "SSL_CTX_set1_curves_list failed");
-    goto out;
-  }
-#else
-  if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
-    char error_buffer[256];
-    ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
-    failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
-    goto out;
+  {
+    const char *curves = conn->ssl_config.curves ?
+      conn->ssl_config.curves : QUIC_GROUPS;
+    if(!SSL_CTX_set1_curves_list(ssl_ctx, curves)) {
+      failf(data, "failed setting curves list for QUIC: '%s'", curves);
+      return CURLE_SSL_CIPHER;
+    }
   }
 
-  if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
-    failf(data, "SSL_CTX_set1_groups_list failed");
-    goto out;
+#ifndef OPENSSL_IS_BORINGSSL
+  {
+    const char *ciphers13 = conn->ssl_config.cipher_list13 ?
+      conn->ssl_config.cipher_list13 : QUIC_CIPHERS;
+    if(SSL_CTX_set_ciphersuites(ssl_ctx, ciphers13) != 1) {
+      failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
+      return CURLE_SSL_CIPHER;
+    }
+    infof(data, "QUIC cipher selection: %s", ciphers13);
   }
 #endif
 
@@ -416,10 +448,6 @@
     SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
   }
 
-  result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
-  if(result)
-    goto out;
-
   /* OpenSSL always tries to verify the peer, this only says whether it should
    * fail to connect if the verification fails, or if it should continue
    * anyway. In the latter case the result of the verification is checked with
@@ -429,6 +457,15 @@
 
   /* give application a chance to interfere with SSL set up. */
   if(data->set.ssl.fsslctx) {
+    /* When a user callback is installed to modify the SSL_CTX,
+     * we need to do the full initialization before calling it.
+     * See: #11800 */
+    if(!ctx->x509_store_setup) {
+      result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
+      if(result)
+        goto out;
+      ctx->x509_store_setup = TRUE;
+    }
     Curl_set_in_callback(data, true);
     result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
                                       data->set.ssl.fsslctxp);
@@ -477,8 +514,8 @@
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   const uint8_t *alpn = NULL;
   size_t alpnlen = 0;
+  unsigned char checkip[16];
 
-  (void)data;
   DEBUGASSERT(!ctx->ssl);
   ctx->ssl = SSL_new(ctx->sslctx);
 
@@ -492,7 +529,19 @@
     SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
 
   /* set SNI */
-  SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+  if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
+#ifdef ENABLE_IPV6
+     && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
+#endif
+     ) {
+    char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
+    if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
+      failf(data, "Failed set SNI");
+      SSL_free(ctx->ssl);
+      ctx->ssl = NULL;
+      return CURLE_QUIC_CONNECT_ERROR;
+    }
+  }
   return CURLE_OK;
 }
 #elif defined(USE_GNUTLS)
@@ -520,15 +569,15 @@
   gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
 
   if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
-    DEBUGF(LOG_CF(data, cf,
-                  "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
+    CURL_TRC_CF(data, cf,
+                "ngtcp2_crypto_gnutls_configure_client_session failed\n");
     return CURLE_QUIC_CONNECT_ERROR;
   }
 
   rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
   if(rc < 0) {
-    DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
-                  gnutls_strerror(rc)));
+    CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
+                gnutls_strerror(rc));
     return CURLE_QUIC_CONNECT_ERROR;
   }
 
@@ -569,15 +618,19 @@
 
   wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
 
-  if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
+  if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn->ssl_config.cipher_list13 ?
+                                 conn->ssl_config.cipher_list13 :
+                                 QUIC_CIPHERS) != 1) {
     char error_buffer[256];
     ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
-    failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
+    failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
     goto out;
   }
 
-  if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
-    failf(data, "SSL_CTX_set1_groups_list failed");
+  if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn->ssl_config.curves ?
+                                  conn->ssl_config.curves :
+                                  (char *)QUIC_GROUPS) != 1) {
+    failf(data, "wolfSSL failed to set curves");
     goto out;
   }
 
@@ -597,10 +650,13 @@
     const char * const ssl_capath = conn->ssl_config.CApath;
 
     wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
-    if(conn->ssl_config.CAfile || conn->ssl_config.CApath) {
+    if(ssl_cafile || ssl_capath) {
       /* tell wolfSSL where to find CA certificates that are used to verify
          the server's certificate. */
-      if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+      int rc =
+        wolfSSL_CTX_load_verify_locations_ex(ssl_ctx, ssl_cafile, ssl_capath,
+                                             WOLFSSL_LOAD_FLAG_IGNORE_ERR);
+      if(SSL_SUCCESS != rc) {
         /* Fail if we insist on successfully verifying the server. */
         failf(data, "error setting certificate verify locations:"
               "  CAfile: %s CApath: %s",
@@ -686,7 +742,7 @@
                                  struct Curl_easy *data,
                                  size_t consumed)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
 
   if(!stream)
@@ -704,8 +760,8 @@
     }
   }
   if(consumed > 0) {
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes",
-                  stream->id, consumed));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA",
+                stream->id, consumed);
     ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id,
                                          consumed);
     ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
@@ -727,8 +783,8 @@
 
   nconsumed =
     nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin);
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd",
-                stream_id, buflen, nconsumed));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd",
+              stream_id, buflen, nconsumed);
   if(nconsumed < 0) {
     ngtcp2_ccerr_set_application_error(
       &ctx->last_error,
@@ -786,8 +842,8 @@
 
   rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id,
                                  app_error_code);
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%"
-                PRIu64 ") -> %d", stream3_id, app_error_code, rv));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%"
+              PRIu64 ") -> %d", stream3_id, app_error_code, rv);
   if(rv) {
     ngtcp2_ccerr_set_application_error(
       &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
@@ -811,7 +867,7 @@
   (void)data;
 
   rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
   if(rv) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
@@ -902,13 +958,13 @@
   return 0;
 }
 
-static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
+static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level,
                           void *user_data)
 {
   struct Curl_cfilter *cf = user_data;
   (void)tconn;
 
-  if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
+  if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) {
     return 0;
   }
 
@@ -962,6 +1018,61 @@
   NULL, /* early_data_rejected */
 };
 
+/**
+ * Connection maintenance like timeouts on packet ACKs etc. are done by us, not
+ * the OS like for TCP. POLL events on the socket therefore are not
+ * sufficient.
+ * ngtcp2 tells us when it wants to be invoked again. We handle that via
+ * the `Curl_expire()` mechanisms.
+ */
+static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct pkt_io_ctx *pktx)
+{
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
+  struct pkt_io_ctx local_pktx;
+  ngtcp2_tstamp expiry;
+
+  if(!pktx) {
+    pktx_init(&local_pktx, cf, data);
+    pktx = &local_pktx;
+  }
+  else {
+    pktx->ts = timestamp();
+  }
+
+  expiry = ngtcp2_conn_get_expiry(ctx->qconn);
+  if(expiry != UINT64_MAX) {
+    if(expiry <= pktx->ts) {
+      CURLcode result;
+      int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts);
+      if(rv) {
+        failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
+              ngtcp2_strerror(rv));
+        ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0);
+        return CURLE_SEND_ERROR;
+      }
+      result = cf_progress_ingress(cf, data, pktx);
+      if(result)
+        return result;
+      result = cf_progress_egress(cf, data, pktx);
+      if(result)
+        return result;
+      /* ask again, things might have changed */
+      expiry = ngtcp2_conn_get_expiry(ctx->qconn);
+    }
+
+    if(expiry > pktx->ts) {
+      ngtcp2_duration timeout = expiry - pktx->ts;
+      if(timeout % NGTCP2_MILLISECONDS) {
+        timeout += NGTCP2_MILLISECONDS;
+      }
+      Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+    }
+  }
+  return CURLE_OK;
+}
+
 static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
                                       curl_socket_t *socks)
@@ -969,7 +1080,7 @@
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct SingleRequest *k = &data->req;
   int rv = GETSOCK_BLANK;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   struct cf_call_data save;
 
   CF_DATA_SAVE(save, cf, data);
@@ -985,21 +1096,19 @@
      stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id))
     rv |= GETSOCK_WRITESOCK(0);
 
-  /* DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
-                rv, (int)socks[0])); */
   CF_DATA_RESTORE(cf, save);
   return rv;
 }
 
-static void drain_stream(struct Curl_cfilter *cf,
-                         struct Curl_easy *data)
+static void h3_drain_stream(struct Curl_cfilter *cf,
+                            struct Curl_easy *data)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   unsigned char bits;
 
   (void)cf;
   bits = CURL_CSELECT_IN;
-  if(stream && !stream->send_closed && stream->upload_left)
+  if(stream && stream->upload_left && !stream->send_closed)
     bits |= CURL_CSELECT_OUT;
   if(data->state.dselect_bits != bits) {
     data->state.dselect_bits = bits;
@@ -1013,25 +1122,27 @@
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   (void)conn;
   (void)stream_id;
-  (void)app_error_code;
-  (void)cf;
 
   /* we might be called by nghttp3 after we already cleaned up */
   if(!stream)
     return 0;
 
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRId64 ")",
-                stream_id, app_error_code));
   stream->closed = TRUE;
   stream->error3 = app_error_code;
-  if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) {
+  if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
     stream->reset = TRUE;
     stream->send_closed = TRUE;
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64,
+                stream->id, stream->error3);
   }
-  drain_stream(cf, data);
+  else {
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->id);
+  }
+  data->req.keepon &= ~KEEP_SEND_HOLD;
+  h3_drain_stream(cf, data);
   return 0;
 }
 
@@ -1045,7 +1156,7 @@
                                const void *mem, size_t memlen,
                                bool flow)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result = CURLE_OK;
   ssize_t nwritten;
 
@@ -1054,9 +1165,6 @@
     return CURLE_RECV_ERROR;
   }
   nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
-  /* DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] add recvbuf(len=%zu) "
-                "-> %zd, %d", stream->id, memlen, nwritten, result));
-   */
   if(nwritten < 0) {
     return result;
   }
@@ -1079,14 +1187,24 @@
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result;
 
   (void)conn;
   (void)stream3_id;
 
+  if(!stream)
+    return NGHTTP3_ERR_CALLBACK_FAILURE;
+
   result = write_resp_raw(cf, data, buf, buflen, TRUE);
-  drain_stream(cf, data);
-  return result? -1 : 0;
+  if(result) {
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
+                stream->id, buflen, result);
+    return NGHTTP3_ERR_CALLBACK_FAILURE;
+  }
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu", stream->id, buflen);
+  h3_drain_stream(cf, data);
+  return 0;
 }
 
 static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id,
@@ -1110,7 +1228,7 @@
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result = CURLE_OK;
   (void)conn;
   (void)stream_id;
@@ -1125,12 +1243,12 @@
     return -1;
   }
 
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d",
-                stream_id, stream->status_code));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d",
+              stream_id, stream->status_code);
   if(stream->status_code / 100 != 1) {
     stream->resp_hds_complete = TRUE;
   }
-  drain_stream(cf, data);
+  h3_drain_stream(cf, data);
   return 0;
 }
 
@@ -1143,7 +1261,7 @@
   nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
   nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result = CURLE_OK;
   (void)conn;
   (void)stream_id;
@@ -1165,8 +1283,7 @@
       return -1;
     ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
                       stream->status_code);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s",
-                  stream_id, line));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
     result = write_resp_raw(cf, data, line, ncopy, FALSE);
     if(result) {
       return -1;
@@ -1174,9 +1291,9 @@
   }
   else {
     /* store as an HTTP1-style header */
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s",
-                  stream_id, (int)h3name.len, h3name.base,
-                  (int)h3val.len, h3val.base));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
+                stream_id, (int)h3name.len, h3name.base,
+                (int)h3val.len, h3val.base);
     result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
     if(result) {
       return -1;
@@ -1207,7 +1324,8 @@
   (void)conn;
   (void)stream_user_data;
 
-  rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, stream_id, app_error_code);
+  rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id,
+                                        app_error_code);
   if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
@@ -1225,9 +1343,9 @@
   (void)conn;
   (void)data;
 
-  rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id,
+  rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id,
                                          app_error_code);
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
   if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
@@ -1249,7 +1367,8 @@
   cb_h3_stop_sending,
   NULL, /* end_stream */
   cb_h3_reset_stream,
-  NULL /* shutdown */
+  NULL, /* shutdown */
+  NULL /* recv_settings */
 };
 
 static int init_ngh3_conn(struct Curl_cfilter *cf)
@@ -1314,7 +1433,7 @@
 
 static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  struct stream_ctx *stream,
+                                  struct h3_stream_ctx *stream,
                                   CURLcode *err)
 {
   ssize_t nread = -1;
@@ -1323,35 +1442,17 @@
   if(stream->reset) {
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->id);
-    *err = CURLE_PARTIAL_FILE;
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
-                  stream->id, *err));
+    *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3;
     goto out;
   }
-  else if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
-    failf(data,
-          "HTTP/3 stream %" PRId64 " was not closed cleanly: "
-          "(err %"PRId64")", stream->id, stream->error3);
-    *err = CURLE_HTTP3;
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly"
-                  " -> %d", stream->id, *err));
-    goto out;
-  }
-
-  if(!stream->resp_hds_complete) {
+  else if(!stream->resp_hds_complete) {
     failf(data,
           "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
           " all response header fields, treated as error",
           stream->id);
     *err = CURLE_HTTP3;
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
-                  " -> %d", stream->id, *err));
     goto out;
   }
-  else {
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
-                  " -> %d", stream->id, *err));
-  }
   *err = CURLE_OK;
   nread = 0;
 
@@ -1364,9 +1465,10 @@
                               char *buf, size_t len, CURLcode *err)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t nread = -1;
   struct cf_call_data save;
+  struct pkt_io_ctx pktx;
 
   (void)ctx;
 
@@ -1377,6 +1479,8 @@
   DEBUGASSERT(ctx->h3conn);
   *err = CURLE_OK;
 
+  pktx_init(&pktx, cf, data);
+
   if(!stream) {
     *err = CURLE_RECV_ERROR;
     goto out;
@@ -1385,14 +1489,15 @@
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
-                  "-> %zd, %d", stream->id, len, nread, *err));
-    if(nread < 0)
+    if(nread < 0) {
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+                  "-> %zd, %d", stream->id, len, nread, *err);
       goto out;
+    }
     report_consumed_data(cf, data, nread);
   }
 
-  if(cf_process_ingress(cf, data)) {
+  if(cf_progress_ingress(cf, data, &pktx)) {
     *err = CURLE_RECV_ERROR;
     nread = -1;
     goto out;
@@ -1402,15 +1507,16 @@
   if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
-                  "-> %zd, %d", stream->id, len, nread, *err));
-    if(nread < 0)
+    if(nread < 0) {
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+                  "-> %zd, %d", stream->id, len, nread, *err);
       goto out;
+    }
     report_consumed_data(cf, data, nread);
   }
 
   if(nread > 0) {
-    drain_stream(cf, data);
+    h3_drain_stream(cf, data);
   }
   else {
     if(stream->closed) {
@@ -1422,12 +1528,19 @@
   }
 
 out:
-  if(cf_flush_egress(cf, data)) {
+  if(cf_progress_egress(cf, data, &pktx)) {
     *err = CURLE_SEND_ERROR;
     nread = -1;
   }
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
-                stream? stream->id : -1, len, nread, *err));
+  else {
+    CURLcode result2 = check_and_set_expiry(cf, data, &pktx);
+    if(result2) {
+      *err = result2;
+      nread = -1;
+    }
+  }
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
+              stream? stream->id : -1, len, nread, *err);
   CF_DATA_RESTORE(cf, save);
   return nread;
 }
@@ -1438,7 +1551,7 @@
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   size_t skiplen;
 
   (void)cf;
@@ -1454,10 +1567,8 @@
   Curl_bufq_skip(&stream->sendbuf, skiplen);
   stream->sendbuf_len_in_flight -= skiplen;
 
-  /* `sendbuf` *might* now have more room. If so, resume this
-   * possibly paused stream. And also tell our transfer engine that
-   * it may continue KEEP_SEND if told to PAUSE. */
-  if(!Curl_bufq_is_full(&stream->sendbuf)) {
+  /* Everything ACKed, we resume upload processing */
+  if(!stream->sendbuf_len_in_flight) {
     int rv = nghttp3_conn_resume_stream(conn, stream_id);
     if(rv) {
       return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -1465,9 +1576,8 @@
     if((data->req.keepon & KEEP_SEND_HOLD) &&
        (data->req.keepon & KEEP_SEND)) {
       data->req.keepon &= ~KEEP_SEND_HOLD;
-      drain_stream(cf, data);
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] unpausing acks",
-                    stream_id));
+      h3_drain_stream(cf, data);
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks", stream_id);
     }
   }
   return 0;
@@ -1481,7 +1591,7 @@
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t nwritten = 0;
   size_t nvecs = 0;
   (void)cf;
@@ -1524,16 +1634,18 @@
   }
   else if(!nwritten) {
     /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> AGAIN",
-                  stream->id));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
+                stream->id);
     return NGHTTP3_ERR_WOULDBLOCK;
   }
 
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> "
-                "%d vecs%s with %zu (buffered=%zu, left=%zd)", stream->id,
-                (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
-                nwritten, Curl_bufq_len(&stream->sendbuf),
-                stream->upload_left));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
+              "%d vecs%s with %zu (buffered=%zu, left=%"
+              CURL_FORMAT_CURL_OFF_T ")",
+              stream->id, (int)nvecs,
+              *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+              nwritten, Curl_bufq_len(&stream->sendbuf),
+              stream->upload_left);
   return (nghttp3_ssize)nvecs;
 }
 
@@ -1547,8 +1659,7 @@
                               CURLcode *err)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = NULL;
-  struct h1_req_parser h1;
+  struct h3_stream_ctx *stream = NULL;
   struct dynhds h2_headers;
   size_t nheader;
   nghttp3_nv *nva = NULL;
@@ -1558,7 +1669,6 @@
   nghttp3_data_reader reader;
   nghttp3_data_reader *preader = NULL;
 
-  Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
 
   *err = h3_data_setup(cf, data);
@@ -1567,24 +1677,22 @@
   stream = H3_STREAM_CTX(data);
   DEBUGASSERT(stream);
 
-  rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL);
-  if(rc) {
-    failf(data, "can get bidi streams");
-    *err = CURLE_SEND_ERROR;
-    goto out;
-  }
-
-  nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
   if(nwritten < 0)
     goto out;
-  DEBUGASSERT(h1.done);
-  DEBUGASSERT(h1.req);
+  if(!stream->h1.done) {
+    /* need more data */
+    goto out;
+  }
+  DEBUGASSERT(stream->h1.req);
 
-  *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
   if(*err) {
     nwritten = -1;
     goto out;
   }
+  /* no longer needed */
+  Curl_h1_req_parse_free(&stream->h1);
 
   nheader = Curl_dynhds_count(&h2_headers);
   nva = malloc(sizeof(nghttp3_nv) * nheader);
@@ -1603,6 +1711,13 @@
     nva[i].flags = NGHTTP3_NV_FLAG_NONE;
   }
 
+  rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL);
+  if(rc) {
+    failf(data, "can get bidi streams");
+    *err = CURLE_SEND_ERROR;
+    goto out;
+  }
+
   switch(data->state.httpreq) {
   case HTTPREQ_POST:
   case HTTPREQ_POST_FORM:
@@ -1614,27 +1729,30 @@
     else
       /* data sending without specifying the data amount up front */
       stream->upload_left = -1; /* unknown */
-    reader.read_data = cb_h3_read_req_body;
-    preader = &reader;
     break;
   default:
     /* there is not request body */
     stream->upload_left = 0; /* no request body */
-    preader = NULL;
     break;
   }
 
+  stream->send_closed = (stream->upload_left == 0);
+  if(!stream->send_closed) {
+    reader.read_data = cb_h3_read_req_body;
+    preader = &reader;
+  }
+
   rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id,
                                    nva, nheader, preader, data);
   if(rc) {
     switch(rc) {
     case NGHTTP3_ERR_CONN_CLOSING:
-      DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
-                    "connection is closing", stream->id));
+      CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
+                  "connection is closing", stream->id);
       break;
     default:
-      DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
-                    stream->id, rc, ngtcp2_strerror(rc)));
+      CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
+                  stream->id, rc, ngtcp2_strerror(rc));
       break;
     }
     *err = CURLE_SEND_ERROR;
@@ -1642,14 +1760,18 @@
     goto out;
   }
 
-  infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
-        stream->id, (void *)data);
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
-                stream->id, data->state.url));
+  if(Curl_trc_is_verbose(data)) {
+    infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+          stream->id, data->state.url);
+    for(i = 0; i < nheader; ++i) {
+      infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id,
+            (int)nva[i].namelen, nva[i].name,
+            (int)nva[i].valuelen, nva[i].value);
+    }
+  }
 
 out:
   free(nva);
-  Curl_h1_req_parse_free(&h1);
   Curl_dynhds_free(&h2_headers);
   return nwritten;
 }
@@ -1658,55 +1780,105 @@
                               const void *buf, size_t len, CURLcode *err)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t sent = 0;
   struct cf_call_data save;
+  struct pkt_io_ctx pktx;
+  CURLcode result;
 
   CF_DATA_SAVE(save, cf, data);
   DEBUGASSERT(cf->connected);
   DEBUGASSERT(ctx->qconn);
   DEBUGASSERT(ctx->h3conn);
+  pktx_init(&pktx, cf, data);
   *err = CURLE_OK;
 
-  if(stream && stream->closed) {
-    *err = CURLE_HTTP3;
+  result = cf_progress_ingress(cf, data, &pktx);
+  if(result) {
+    *err = result;
     sent = -1;
-    goto out;
   }
 
   if(!stream || stream->id < 0) {
     sent = h3_stream_open(cf, data, buf, len, err);
     if(sent < 0) {
-      DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", *err));
+      CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
       goto out;
     }
+    stream = H3_STREAM_CTX(data);
+  }
+  else if(stream->upload_blocked_len) {
+    /* the data in `buf` has already been submitted or added to the
+     * buffers, but have been EAGAINed on the last invocation. */
+    DEBUGASSERT(len >= stream->upload_blocked_len);
+    if(len < stream->upload_blocked_len) {
+      /* Did we get called again with a smaller `len`? This should not
+       * happen. We are not prepared to handle that. */
+      failf(data, "HTTP/3 send again with decreased length");
+      *err = CURLE_HTTP3;
+      sent = -1;
+      goto out;
+    }
+    sent = (ssize_t)stream->upload_blocked_len;
+    stream->upload_blocked_len = 0;
+  }
+  else if(stream->closed) {
+    if(stream->resp_hds_complete) {
+      /* Server decided to close the stream after having sent us a final
+       * response. This is valid if it is not interested in the request
+       * body. This happens on 30x or 40x responses.
+       * We silently discard the data sent, since this is not a transport
+       * error situation. */
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
+                  "on closed stream with response", stream->id);
+      *err = CURLE_OK;
+      sent = (ssize_t)len;
+      goto out;
+    }
+    *err = CURLE_HTTP3;
+    sent = -1;
+    goto out;
   }
   else {
     sent = Curl_bufq_write(&stream->sendbuf, buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send, add to "
-                  "sendbuf(len=%zu) -> %zd, %d",
-                  stream->id, len, sent, *err));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
+                "sendbuf(len=%zu) -> %zd, %d",
+                stream->id, len, sent, *err);
     if(sent < 0) {
-      if(*err == CURLE_AGAIN) {
-        /* Can't add more to the send buf, needs to drain first.
-         * Pause the sending to avoid a busy loop. */
-        data->req.keepon |= KEEP_SEND_HOLD;
-        DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] pause send",
-                      stream->id));
-      }
       goto out;
     }
 
     (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
   }
 
-  if(cf_flush_egress(cf, data)) {
-    *err = CURLE_SEND_ERROR;
+  result = cf_progress_egress(cf, data, &pktx);
+  if(result) {
+    *err = result;
     sent = -1;
-    goto out;
+  }
+
+  if(stream && sent > 0 && stream->sendbuf_len_in_flight) {
+    /* We have unacknowledged DATA and cannot report success to our
+     * caller. Instead we EAGAIN and remember how much we have already
+     * "written" into our various internal connection buffers.
+     * We put the stream upload on HOLD, until this gets ACKed. */
+    stream->upload_blocked_len = sent;
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
+                "%zu bytes in flight -> EGAIN", stream->id, len,
+                stream->sendbuf_len_in_flight);
+    *err = CURLE_AGAIN;
+    sent = -1;
+    data->req.keepon |= KEEP_SEND_HOLD;
   }
 
 out:
+  result = check_and_set_expiry(cf, data, &pktx);
+  if(result) {
+    *err = result;
+    sent = -1;
+  }
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+              stream? stream->id : -1, len, sent, *err);
   CF_DATA_RESTORE(cf, save);
   return sent;
 }
@@ -1763,35 +1935,28 @@
   return result;
 }
 
-struct recv_ctx {
-  struct Curl_cfilter *cf;
-  struct Curl_easy *data;
-  ngtcp2_tstamp ts;
-  size_t pkt_count;
-};
-
 static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
                          struct sockaddr_storage *remote_addr,
                          socklen_t remote_addrlen, int ecn,
                          void *userp)
 {
-  struct recv_ctx *r = userp;
-  struct cf_ngtcp2_ctx *ctx = r->cf->ctx;
+  struct pkt_io_ctx *pktx = userp;
+  struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx;
   ngtcp2_pkt_info pi;
   ngtcp2_path path;
   int rv;
 
-  ++r->pkt_count;
+  ++pktx->pkt_count;
   ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
                    ctx->q.local_addrlen);
   ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr,
                    remote_addrlen);
-  pi.ecn = (uint32_t)ecn;
+  pi.ecn = (uint8_t)ecn;
 
-  rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, r->ts);
+  rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts);
   if(rv) {
-    DEBUGF(LOG_CF(r->data, r->cf, "ingress, read_pkt -> %s",
-                  ngtcp2_strerror(rv)));
+    CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s",
+                ngtcp2_strerror(rv));
     if(!ctx->last_error.error_code) {
       if(rv == NGTCP2_ERR_CRYPTO) {
         ngtcp2_ccerr_set_tls_alert(&ctx->last_error,
@@ -1813,41 +1978,49 @@
   return CURLE_OK;
 }
 
-static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data)
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct pkt_io_ctx *pktx)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct recv_ctx rctx;
+  struct pkt_io_ctx local_pktx;
   size_t pkts_chunk = 128, i;
   size_t pkts_max = 10 * pkts_chunk;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
-  rctx.cf = cf;
-  rctx.data = data;
-  rctx.ts = timestamp();
-  rctx.pkt_count = 0;
+  if(!pktx) {
+    pktx_init(&local_pktx, cf, data);
+    pktx = &local_pktx;
+  }
+  else {
+    pktx->ts = timestamp();
+  }
+
+#ifdef USE_OPENSSL
+  if(!ctx->x509_store_setup) {
+    result = Curl_ssl_setup_x509_store(cf, data, ctx->sslctx);
+    if(result)
+      return result;
+    ctx->x509_store_setup = TRUE;
+  }
+#endif
 
   for(i = 0; i < pkts_max; i += pkts_chunk) {
-    rctx.pkt_count = 0;
+    pktx->pkt_count = 0;
     result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk,
-                                recv_pkt, &rctx);
+                                recv_pkt, pktx);
     if(result) /* error */
       break;
-    if(rctx.pkt_count < pkts_chunk) /* got less than we could */
+    if(pktx->pkt_count < pkts_chunk) /* got less than we could */
       break;
     /* give egress a chance before we receive more */
-    result = cf_flush_egress(cf, data);
+    result = cf_progress_egress(cf, data, pktx);
+    if(result) /* error */
+      break;
   }
   return result;
 }
 
-struct read_ctx {
-  struct Curl_cfilter *cf;
-  struct Curl_easy *data;
-  ngtcp2_tstamp ts;
-  ngtcp2_path_storage *ps;
-};
-
 /**
  * Read a network packet to send from ngtcp2 into `buf`.
  * Return number of bytes written or -1 with *err set.
@@ -1856,7 +2029,7 @@
                                 unsigned char *buf, size_t buflen,
                                 CURLcode *err)
 {
-  struct read_ctx *x = userp;
+  struct pkt_io_ctx *x = userp;
   struct cf_ngtcp2_ctx *ctx = x->cf->ctx;
   nghttp3_vec vec[16];
   nghttp3_ssize veccnt;
@@ -1896,7 +2069,7 @@
 
     flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
             (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
-    n = ngtcp2_conn_writev_stream(ctx->qconn, x->ps? &x->ps->path : NULL,
+    n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path,
                                   NULL, buf, buflen,
                                   &ndatalen, flags, stream_id,
                                   (const ngtcp2_vec *)vec, veccnt, x->ts);
@@ -1955,28 +2128,25 @@
   return nwritten;
 }
 
-static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
+static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct pkt_io_ctx *pktx)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  int rv;
   ssize_t nread;
   size_t max_payload_size, path_max_payload_size, max_pktcnt;
   size_t pktcnt = 0;
   size_t gsolen = 0;  /* this disables gso until we have a clue */
-  ngtcp2_path_storage ps;
-  ngtcp2_tstamp ts = timestamp();
-  ngtcp2_tstamp expiry;
-  ngtcp2_duration timeout;
   CURLcode curlcode;
-  struct read_ctx readx;
+  struct pkt_io_ctx local_pktx;
 
-  rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts);
-  if(rv) {
-    failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
-          ngtcp2_strerror(rv));
-    ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0);
-    return CURLE_SEND_ERROR;
+  if(!pktx) {
+    pktx_init(&local_pktx, cf, data);
+    pktx = &local_pktx;
+  }
+  else {
+    pktx->ts = timestamp();
+    ngtcp2_path_storage_zero(&pktx->ps);
   }
 
   curlcode = vquic_flush(cf, data, &ctx->q);
@@ -1988,8 +2158,6 @@
     return curlcode;
   }
 
-  ngtcp2_path_storage_zero(&ps);
-
   /* In UDP, there is a maximum theoretical packet paload length and
    * a minimum payload length that is "guarantueed" to work.
    * To detect if this minimum payload can be increased, ngtcp2 sends
@@ -2008,17 +2176,10 @@
   max_pktcnt = CURLMIN(MAX_PKT_BURST,
                        ctx->q.sendbuf.chunk_size / max_payload_size);
 
-  readx.cf = cf;
-  readx.data = data;
-  readx.ts = ts;
-  readx.ps = &ps;
-
   for(;;) {
     /* add the next packet to send, if any, to our buffer */
     nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size,
-                           read_pkt_to_send, &readx, &curlcode);
-    /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d",
-                  max_payload_size, nread, curlcode)); */
+                           read_pkt_to_send, pktx, &curlcode);
     if(nread < 0) {
       if(curlcode != CURLE_AGAIN)
         return curlcode;
@@ -2076,21 +2237,6 @@
   }
 
 out:
-  /* non-errored exit. check when we should run again. */
-  expiry = ngtcp2_conn_get_expiry(ctx->qconn);
-  if(expiry != UINT64_MAX) {
-    if(expiry <= ts) {
-      timeout = 0;
-    }
-    else {
-      timeout = expiry - ts;
-      if(timeout % NGTCP2_MILLISECONDS) {
-        timeout += NGTCP2_MILLISECONDS;
-      }
-    }
-    Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
-  }
-
   return CURLE_OK;
 }
 
@@ -2101,7 +2247,7 @@
 static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
                                    const struct Curl_easy *data)
 {
-  const struct stream_ctx *stream = H3_STREAM_CTX(data);
+  const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   (void)cf;
   return stream && !Curl_bufq_is_empty(&stream->recvbuf);
 }
@@ -2113,7 +2259,7 @@
   /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge
    * the streams windows. As we do in HTTP/2. */
   if(!pause) {
-    drain_stream(cf, data);
+    h3_drain_stream(cf, data);
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
   return CURLE_OK;
@@ -2141,7 +2287,7 @@
     break;
   }
   case CF_CTRL_DATA_DONE_SEND: {
-    struct stream_ctx *stream = H3_STREAM_CTX(data);
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
     if(stream && !stream->send_closed) {
       stream->send_closed = TRUE;
       stream->upload_left = Curl_bufq_len(&stream->sendbuf);
@@ -2149,13 +2295,16 @@
     }
     break;
   }
-  case CF_CTRL_DATA_IDLE:
-    if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) {
-      if(cf_flush_egress(cf, data)) {
-        result = CURLE_SEND_ERROR;
-      }
+  case CF_CTRL_DATA_IDLE: {
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
+    CURL_TRC_CF(data, cf, "data idle");
+    if(stream && !stream->closed) {
+      result = check_and_set_expiry(cf, data, NULL);
+      if(result)
+        CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result);
     }
     break;
+  }
   default:
     break;
   }
@@ -2212,7 +2361,7 @@
     ngtcp2_tstamp ts;
     ngtcp2_ssize rc;
 
-    DEBUGF(LOG_CF(data, cf, "close"));
+    CURL_TRC_CF(data, cf, "close");
     ts = timestamp();
     rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
                                             NULL, /* pkt_info */
@@ -2236,7 +2385,7 @@
   struct cf_call_data save;
 
   CF_DATA_SAVE(save, cf, data);
-  DEBUGF(LOG_CF(data, cf, "destroy"));
+  CURL_TRC_CF(data, cf, "destroy");
   if(ctx) {
     cf_ngtcp2_ctx_clear(ctx);
     free(ctx);
@@ -2250,13 +2399,14 @@
  * Might be called twice for happy eyeballs.
  */
 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data)
+                                 struct Curl_easy *data,
+                                 struct pkt_io_ctx *pktx)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   int rc;
   int rv;
   CURLcode result;
-  const struct Curl_sockaddr_ex *sockaddr;
+  const struct Curl_sockaddr_ex *sockaddr = NULL;
   int qfd;
 
   ctx->version = NGTCP2_PROTO_VER_MAX;
@@ -2294,7 +2444,7 @@
 
   (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
   ctx->qlogfd = qfd; /* -1 if failure above */
-  quic_settings(ctx, data);
+  quic_settings(ctx, data, pktx);
 
   result = vquic_ctx_init(&ctx->q);
   if(result)
@@ -2302,6 +2452,8 @@
 
   Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
                       &sockaddr, NULL, NULL, NULL, NULL);
+  if(!sockaddr)
+    return CURLE_QUIC_CONNECT_ERROR;
   ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
   rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
                    &ctx->q.local_addrlen);
@@ -2344,6 +2496,7 @@
   CURLcode result = CURLE_OK;
   struct cf_call_data save;
   struct curltime now;
+  struct pkt_io_ctx pktx;
 
   if(cf->connected) {
     *done = TRUE;
@@ -2359,40 +2512,41 @@
 
   *done = FALSE;
   now = Curl_now();
+  pktx_init(&pktx, cf, data);
 
   CF_DATA_SAVE(save, cf, data);
 
   if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
     /* Not time yet to attempt the next connect */
-    DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+    CURL_TRC_CF(data, cf, "waiting for reconnect time");
     goto out;
   }
 
   if(!ctx->qconn) {
     ctx->started_at = now;
-    result = cf_connect_start(cf, data);
+    result = cf_connect_start(cf, data, &pktx);
     if(result)
       goto out;
-    result = cf_flush_egress(cf, data);
+    result = cf_progress_egress(cf, data, &pktx);
     /* we do not expect to be able to recv anything yet */
     goto out;
   }
 
-  result = cf_process_ingress(cf, data);
+  result = cf_progress_ingress(cf, data, &pktx);
   if(result)
     goto out;
 
-  result = cf_flush_egress(cf, data);
+  result = cf_progress_egress(cf, data, &pktx);
   if(result)
     goto out;
 
   if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
     ctx->handshake_at = now;
-    DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
-           (int)Curl_timediff(now, ctx->started_at)));
+    CURL_TRC_CF(data, cf, "handshake complete after %dms",
+               (int)Curl_timediff(now, ctx->started_at));
     result = qng_verify_peer(cf, data);
     if(!result) {
-      DEBUGF(LOG_CF(data, cf, "peer verified"));
+      CURL_TRC_CF(data, cf, "peer verified");
       cf->connected = TRUE;
       cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
@@ -2402,7 +2556,7 @@
 
 out:
   if(result == CURLE_RECV_ERROR && ctx->qconn &&
-     ngtcp2_conn_is_in_draining_period(ctx->qconn)) {
+     ngtcp2_conn_in_draining_period(ctx->qconn)) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
      * state.
@@ -2414,8 +2568,8 @@
      */
     int reconn_delay_ms = 200;
 
-    DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
-                  reconn_delay_ms));
+    CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
+                reconn_delay_ms);
     Curl_conn_cf_close(cf->next, data);
     cf_ngtcp2_ctx_clear(ctx);
     result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
@@ -2430,8 +2584,8 @@
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   if(result) {
-    const char *r_ip;
-    int r_port;
+    const char *r_ip = NULL;
+    int r_port = 0;
 
     Curl_cf_socket_peek(cf->next, data, NULL, NULL,
                         &r_ip, &r_port, NULL, NULL);
@@ -2439,7 +2593,11 @@
           r_ip, r_port, curl_easy_strerror(result));
   }
 #endif
-  DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
+  if(!result && ctx->qconn) {
+    result = check_and_set_expiry(cf, data, &pktx);
+  }
+  if(result || *done)
+    CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
   CF_DATA_RESTORE(cf, save);
   return result;
 }
@@ -2463,7 +2621,7 @@
                  INT_MAX : (int)rp->initial_max_streams_bidi;
     else  /* not arrived yet? */
       *pres1 = Curl_multi_max_concurrent_streams(data->multi);
-    DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1));
+    CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
     CF_DATA_RESTORE(cf, save);
     return CURLE_OK;
   }
@@ -2510,13 +2668,11 @@
        not in use by any other transfer, there shouldn't be any data here,
        only "protocol frames" */
     *input_pending = FALSE;
-    Curl_attach_connection(data, cf->conn);
-    if(cf_process_ingress(cf, data))
+    if(cf_progress_ingress(cf, data, NULL))
       alive = FALSE;
     else {
       alive = TRUE;
     }
-    Curl_detach_connection(data);
   }
 
   return alive;
diff --git a/Utilities/cmcurl/lib/vquic/curl_quiche.c b/Utilities/cmcurl/lib/vquic/curl_quiche.c
index 3a4f9f9..3f5d327 100644
--- a/Utilities/cmcurl/lib/vquic/curl_quiche.c
+++ b/Utilities/cmcurl/lib/vquic/curl_quiche.c
@@ -45,8 +45,10 @@
 #include "vquic_int.h"
 #include "curl_quiche.h"
 #include "transfer.h"
+#include "inet_pton.h"
 #include "vtls/openssl.h"
 #include "vtls/keylog.h"
+#include "vtls/vtls.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -56,7 +58,7 @@
 /* #define DEBUG_QUICHE */
 
 #define QUIC_MAX_STREAMS              (100)
-#define QUIC_IDLE_TIMEOUT        (5 * 1000) /* milliseconds */
+#define QUIC_IDLE_TIMEOUT        (60 * 1000) /* milliseconds */
 
 #define H3_STREAM_WINDOW_SIZE  (128 * 1024)
 #define H3_STREAM_CHUNK_SIZE    (16 * 1024)
@@ -88,54 +90,6 @@
   Curl_tls_keylog_write_line(line);
 }
 
-static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
-{
-  SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
-
-  SSL_CTX_set_alpn_protos(ssl_ctx,
-                          (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
-                          sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
-
-  SSL_CTX_set_default_verify_paths(ssl_ctx);
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-    SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
-  }
-
-  {
-    struct connectdata *conn = data->conn;
-    if(conn->ssl_config.verifypeer) {
-      const char * const ssl_cafile = conn->ssl_config.CAfile;
-      const char * const ssl_capath = conn->ssl_config.CApath;
-      if(ssl_cafile || ssl_capath) {
-        SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
-        /* tell OpenSSL where to find CA certificates that are used to verify
-           the server's certificate. */
-        if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
-          /* Fail if we insist on successfully verifying the server. */
-          failf(data, "error setting certificate verify locations:"
-                "  CAfile: %s CApath: %s",
-                ssl_cafile ? ssl_cafile : "none",
-                ssl_capath ? ssl_capath : "none");
-          return NULL;
-        }
-        infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
-        infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
-      }
-#ifdef CURL_CA_FALLBACK
-      else {
-        /* verifying the peer without any CA certificates won't work so
-           use openssl's built-in default as fallback */
-        SSL_CTX_set_default_verify_paths(ssl_ctx);
-      }
-#endif
-    }
-  }
-  return ssl_ctx;
-}
-
 struct cf_quiche_ctx {
   struct cf_quic_ctx q;
   quiche_conn *qconn;
@@ -154,6 +108,7 @@
   size_t sends_on_hold;              /* # of streams with SEND_HOLD set */
   BIT(goaway);                       /* got GOAWAY from server */
   BIT(got_first_byte);               /* if first byte was received */
+  BIT(x509_store_setup);             /* if x509 store has been set up */
 };
 
 #ifdef DEBUG_QUICHE
@@ -181,12 +136,103 @@
   }
 }
 
+static CURLcode quic_x509_store_setup(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
+{
+  struct cf_quiche_ctx *ctx = cf->ctx;
+
+  if(!ctx->x509_store_setup) {
+    if(cf->conn->ssl_config.verifypeer) {
+      const char * const ssl_cafile = cf->conn->ssl_config.CAfile;
+      const char * const ssl_capath = cf->conn->ssl_config.CApath;
+      if(ssl_cafile || ssl_capath) {
+        SSL_CTX_set_verify(ctx->sslctx, SSL_VERIFY_PEER, NULL);
+        /* tell OpenSSL where to find CA certificates that are used to verify
+           the server's certificate. */
+        if(!SSL_CTX_load_verify_locations(ctx->sslctx, ssl_cafile,
+                                          ssl_capath)) {
+          /* Fail if we insist on successfully verifying the server. */
+          failf(data, "error setting certificate verify locations:"
+                "  CAfile: %s CApath: %s",
+                ssl_cafile ? ssl_cafile : "none",
+                ssl_capath ? ssl_capath : "none");
+          return CURLE_SSL_CACERT_BADFILE;
+        }
+        infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+        infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+      }
+#ifdef CURL_CA_FALLBACK
+      else {
+        /* verifying the peer without any CA certificates won't work so
+           use openssl's built-in default as fallback */
+        SSL_CTX_set_default_verify_paths(ctx->sslctx);
+      }
+#endif
+    }
+    ctx->x509_store_setup = TRUE;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode quic_ssl_setup(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct cf_quiche_ctx *ctx = cf->ctx;
+  unsigned char checkip[16];
+  struct connectdata *conn = data->conn;
+  const char *curves = conn->ssl_config.curves;
+
+  DEBUGASSERT(!ctx->sslctx);
+  ctx->sslctx = SSL_CTX_new(TLS_method());
+  if(!ctx->sslctx)
+    return CURLE_OUT_OF_MEMORY;
+
+  SSL_CTX_set_alpn_protos(ctx->sslctx,
+                          (const uint8_t *)QUICHE_H3_APPLICATION_PROTOCOL,
+                          sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1);
+
+  SSL_CTX_set_default_verify_paths(ctx->sslctx);
+
+  /* Open the file if a TLS or QUIC backend has not done this before. */
+  Curl_tls_keylog_open();
+  if(Curl_tls_keylog_enabled()) {
+    SSL_CTX_set_keylog_callback(ctx->sslctx, keylog_callback);
+  }
+
+  if(curves && !SSL_CTX_set1_curves_list(ctx->sslctx, curves)) {
+    failf(data, "failed setting curves list for QUIC: '%s'", curves);
+    return CURLE_SSL_CIPHER;
+  }
+
+  ctx->ssl = SSL_new(ctx->sslctx);
+  if(!ctx->ssl)
+    return CURLE_QUIC_CONNECT_ERROR;
+
+  SSL_set_app_data(ctx->ssl, cf);
+
+  if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip))
+#ifdef ENABLE_IPV6
+     && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip))
+#endif
+     ) {
+    char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL);
+    if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) {
+      failf(data, "Failed set SNI");
+      SSL_free(ctx->ssl);
+      ctx->ssl = NULL;
+      return CURLE_QUIC_CONNECT_ERROR;
+    }
+  }
+
+  return CURLE_OK;
+}
+
 /**
  * All about the H3 internals of a stream
  */
 struct stream_ctx {
   int64_t id; /* HTTP/3 protocol stream identifier */
   struct bufq recvbuf; /* h3 response */
+  struct h1_req_parser h1; /* h1 request parsing */
   uint64_t error3; /* HTTP/3 stream error code */
   curl_off_t upload_left; /* number of request bytes left to upload */
   bool closed; /* TRUE on stream close */
@@ -217,11 +263,10 @@
     data->req.keepon |= KEEP_SEND_HOLD;
     ++ctx->sends_on_hold;
     if(H3_STREAM_ID(data) >= 0)
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] suspend sending",
-                    H3_STREAM_ID(data)));
+      CURL_TRC_CF(data, cf, "[%"PRId64"] suspend sending",
+                  H3_STREAM_ID(data));
     else
-      DEBUGF(LOG_CF(data, cf, "[%s] suspend sending",
-                    data->state.url));
+      CURL_TRC_CF(data, cf, "[%s] suspend sending", data->state.url);
   }
 }
 
@@ -234,11 +279,10 @@
     data->req.keepon &= ~KEEP_SEND_HOLD;
     --ctx->sends_on_hold;
     if(H3_STREAM_ID(data) >= 0)
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] resume sending",
-                    H3_STREAM_ID(data)));
+      CURL_TRC_CF(data, cf, "[%"PRId64"] resume sending",
+                  H3_STREAM_ID(data));
     else
-      DEBUGF(LOG_CF(data, cf, "[%s] resume sending",
-                    data->state.url));
+      CURL_TRC_CF(data, cf, "[%s] resume sending", data->state.url);
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
 }
@@ -277,7 +321,7 @@
   stream->id = -1;
   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data));
+  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
   return CURLE_OK;
 }
 
@@ -288,13 +332,13 @@
 
   (void)cf;
   if(stream) {
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] easy handle is done",
-                  stream->id));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->id);
     if(stream_send_is_suspended(data)) {
       data->req.keepon &= ~KEEP_SEND_HOLD;
       --ctx->sends_on_hold;
     }
     Curl_bufq_free(&stream->recvbuf);
+    Curl_h1_req_parse_free(&stream->h1);
     free(stream);
     H3_STREAM_LCTX(data) = NULL;
   }
@@ -329,7 +373,7 @@
   else {
     DEBUGASSERT(data->multi);
     for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
-      if(H3_STREAM_ID(sdata) == stream3_id) {
+      if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) {
         return sdata;
       }
     }
@@ -379,8 +423,12 @@
   struct stream_ctx *stream = H3_STREAM_CTX(x->data);
   CURLcode result;
 
-  (void)stream;
+  if(!stream)
+    return CURLE_OK;
+
   if((name_len == 7) && !strncmp(HTTP_PSEUDO_STATUS, (char *)name, 7)) {
+    CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] status: %.*s",
+                stream->id, (int)value_len, value);
     result = write_resp_raw(x->cf, x->data, "HTTP/3 ", sizeof("HTTP/3 ") - 1);
     if(!result)
       result = write_resp_raw(x->cf, x->data, value, value_len);
@@ -388,6 +436,9 @@
       result = write_resp_raw(x->cf, x->data, " \r\n", 3);
   }
   else {
+    CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] header: %.*s: %.*s",
+                stream->id, (int)name_len, name,
+                (int)value_len, value);
     result = write_resp_raw(x->cf, x->data, name, name_len);
     if(!result)
       result = write_resp_raw(x->cf, x->data, ": ", 2);
@@ -397,10 +448,8 @@
       result = write_resp_raw(x->cf, x->data, "\r\n", 2);
   }
   if(result) {
-    DEBUGF(LOG_CF(x->data, x->cf,
-                  "[h3sid=%"PRId64"][HEADERS][%.*s: %.*s] error %d",
-                  stream? stream->id : -1, (int)name_len, name,
-                  (int)value_len, value, result));
+    CURL_TRC_CF(x->data, x->cf, "[%"PRId64"] on header error %d",
+                stream->id, result);
   }
   return result;
 }
@@ -425,12 +474,8 @@
     *err = CURLE_OK;
     return nread;
   }
-  else if(nread < 0) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
   else {
-    *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+    *err = CURLE_AGAIN;
     return -1;
   }
 }
@@ -459,10 +504,10 @@
                              stream_resp_read, &cb_ctx, &result);
 
   if(nwritten < 0 && result != CURLE_AGAIN) {
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv_body error %zd",
-                  stream->id, nwritten));
-    failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]",
-          nwritten, stream->id);
+    CURL_TRC_CF(data, cf, "[%"PRId64"] recv_body error %zd",
+                stream->id, nwritten);
+    failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]",
+          result, stream->id);
     stream->closed = TRUE;
     stream->reset = TRUE;
     stream->send_closed = TRUE;
@@ -518,7 +563,7 @@
             rc, stream3_id);
       return CURLE_RECV_ERROR;
     }
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][HEADERS]", stream3_id));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] <- [HEADERS]", stream3_id);
     break;
 
   case QUICHE_H3_EVENT_DATA:
@@ -528,7 +573,7 @@
     break;
 
   case QUICHE_H3_EVENT_RESET:
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][RESET]", stream3_id));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] RESET", stream3_id);
     stream->closed = TRUE;
     stream->reset = TRUE;
     stream->send_closed = TRUE;
@@ -536,7 +581,7 @@
     break;
 
   case QUICHE_H3_EVENT_FINISHED:
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][FINISHED]", stream3_id));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] CLOSED", stream3_id);
     if(!stream->resp_hds_complete) {
       result = write_resp_raw(cf, data, "\r\n", 2);
       if(result)
@@ -545,15 +590,16 @@
     }
     stream->closed = TRUE;
     streamclose(cf->conn, "End of stream");
+    data->req.keepon &= ~KEEP_SEND_HOLD;
     break;
 
   case QUICHE_H3_EVENT_GOAWAY:
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"][GOAWAY]", stream3_id));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] <- [GOAWAY]", stream3_id);
     break;
 
   default:
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv, unhandled event %d",
-                  stream3_id, quiche_h3_event_type(ev)));
+    CURL_TRC_CF(data, cf, "[%"PRId64"] recv, unhandled event %d",
+                stream3_id, quiche_h3_event_type(ev));
     break;
   }
   return result;
@@ -575,28 +621,32 @@
       break;
     }
     else if(stream3_id < 0) {
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error poll: %"PRId64,
-                    stream? stream->id : -1, stream3_id));
+      CURL_TRC_CF(data, cf, "[%"PRId64"] error poll: %"PRId64,
+                  stream? stream->id : -1, stream3_id);
       return CURLE_HTTP3;
     }
 
     sdata = get_stream_easy(cf, data, stream3_id);
     if(!sdata) {
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] discard event %s for "
-                    "unknown [h3sid=%"PRId64"]",
-                    stream? stream->id : -1, cf_ev_name(ev),
-                    stream3_id));
+      CURL_TRC_CF(data, cf, "[%"PRId64"] discard event %s for "
+                  "unknown [%"PRId64"]",
+                  stream? stream->id : -1, cf_ev_name(ev), stream3_id);
     }
     else {
       result = h3_process_event(cf, sdata, stream3_id, ev);
       drain_stream(cf, sdata);
       if(result) {
-        DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] error processing event %s "
-                      "for [h3sid=%"PRId64"] -> %d",
-                      stream? stream->id : -1, cf_ev_name(ev),
-                      stream3_id, result));
-        quiche_h3_event_free(ev);
-        return result;
+        CURL_TRC_CF(data, cf, "[%"PRId64"] error processing event %s "
+                    "for [%"PRId64"] -> %d",
+                    stream? stream->id : -1, cf_ev_name(ev),
+                    stream3_id, result);
+        if(data == sdata) {
+          /* Only report this error to the caller if it is about the
+           * transfer we were called with. Otherwise we fail a transfer
+           * due to a problem in another one. */
+          quiche_h3_event_free(ev);
+          return result;
+        }
       }
       quiche_h3_event_free(ev);
     }
@@ -632,7 +682,7 @@
                            &recv_info);
   if(nread < 0) {
     if(QUICHE_ERR_DONE == nread) {
-      DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche is DONE"));
+      CURL_TRC_CF(r->data, r->cf, "ingress, quiche is DONE");
       return CURLE_OK;
     }
     else if(QUICHE_ERR_TLS_FAIL == nread) {
@@ -649,8 +699,8 @@
     }
   }
   else if((size_t)nread < pktlen) {
-    DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zd bytes",
-                  nread, pktlen));
+    CURL_TRC_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes",
+                nread, pktlen);
   }
 
   return CURLE_OK;
@@ -664,6 +714,10 @@
   CURLcode result;
 
   DEBUGASSERT(ctx->qconn);
+  result = quic_x509_store_setup(cf, data);
+  if(result)
+    return result;
+
   rctx.cf = cf;
   rctx.data = data;
   rctx.pkts = 0;
@@ -719,10 +773,20 @@
   struct cf_quiche_ctx *ctx = cf->ctx;
   ssize_t nread;
   CURLcode result;
+  int64_t expiry_ns;
   int64_t timeout_ns;
   struct read_ctx readx;
   size_t pkt_count, gsolen;
 
+  expiry_ns = quiche_conn_timeout_as_nanos(ctx->qconn);
+  if(!expiry_ns) {
+    quiche_conn_on_timeout(ctx->qconn);
+    if(quiche_conn_is_closed(ctx->qconn)) {
+      failf(data, "quiche_conn_on_timeout closed the connection");
+      return CURLE_SEND_ERROR;
+    }
+  }
+
   result = vquic_flush(cf, data, &ctx->q);
   if(result) {
     if(result == CURLE_AGAIN) {
@@ -741,9 +805,6 @@
     /* add the next packet to send, if any, to our buffer */
     nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0,
                            read_pkt_to_send, &readx, &result);
-    /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d",
-                  (size_t)0, nread, result)); */
-
     if(nread < 0) {
       if(result != CURLE_AGAIN)
         return result;
@@ -794,8 +855,8 @@
     failf(data,
           "HTTP/3 stream %" PRId64 " reset by server", stream->id);
     *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
-                  stream->id, *err));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, was reset -> %d",
+                stream->id, *err);
   }
   else if(!stream->resp_got_header) {
     failf(data,
@@ -804,14 +865,12 @@
           stream->id);
     /* *err = CURLE_PARTIAL_FILE; */
     *err = CURLE_RECV_ERROR;
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
-                  " -> %d", stream->id, *err));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv, closed incomplete"
+                " -> %d", stream->id, *err);
   }
   else {
     *err = CURLE_OK;
     nread = 0;
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
-                  " -> %d", stream->id, *err));
   }
   return nread;
 }
@@ -826,20 +885,20 @@
 
   if(!stream) {
     *err = CURLE_RECV_ERROR;
-    goto out;
+    return -1;
   }
 
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
-                  "-> %zd, %d", stream->id, len, nread, *err));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+                "-> %zd, %d", stream->id, len, nread, *err);
     if(nread < 0)
       goto out;
   }
 
   if(cf_process_ingress(cf, data)) {
-    DEBUGF(LOG_CF(data, cf, "cf_recv, error on ingress"));
+    CURL_TRC_CF(data, cf, "cf_recv, error on ingress");
     *err = CURLE_RECV_ERROR;
     nread = -1;
     goto out;
@@ -849,8 +908,8 @@
   if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
     nread = Curl_bufq_read(&stream->recvbuf,
                            (unsigned char *)buf, len, err);
-    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read recvbuf(len=%zu) "
-                  "-> %zd, %d", stream->id, len, nread, *err));
+    CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
+                "-> %zd, %d", stream->id, len, nread, *err);
     if(nread < 0)
       goto out;
   }
@@ -877,14 +936,15 @@
 out:
   result = cf_flush_egress(cf, data);
   if(result) {
-    DEBUGF(LOG_CF(data, cf, "cf_recv, flush egress failed"));
+    CURL_TRC_CF(data, cf, "cf_recv, flush egress failed");
     *err = result;
     nread = -1;
   }
   if(nread > 0)
     ctx->data_recvd += nread;
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%zd) -> %zd, %d",
-                stream->id, ctx->data_recvd, nread, *err));
+  CURL_TRC_CF(data, cf, "[%"PRId64"] cf_recv(total=%"
+              CURL_FORMAT_CURL_OFF_T ") -> %zd, %d",
+              stream->id, ctx->data_recvd, nread, *err);
   return nread;
 }
 
@@ -901,7 +961,6 @@
   struct stream_ctx *stream = H3_STREAM_CTX(data);
   size_t nheader, i;
   int64_t stream3_id;
-  struct h1_req_parser h1;
   struct dynhds h2_headers;
   quiche_h3_header *nva = NULL;
   ssize_t nwritten;
@@ -909,28 +968,31 @@
   if(!stream) {
     *err = h3_data_setup(cf, data);
     if(*err) {
-      nwritten = -1;
-      goto out;
+      return -1;
     }
     stream = H3_STREAM_CTX(data);
     DEBUGASSERT(stream);
   }
 
-  Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
 
   DEBUGASSERT(stream);
-  nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
+  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
   if(nwritten < 0)
     goto out;
-  DEBUGASSERT(h1.done);
-  DEBUGASSERT(h1.req);
+  if(!stream->h1.done) {
+    /* need more data */
+    goto out;
+  }
+  DEBUGASSERT(stream->h1.req);
 
-  *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
+  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
   if(*err) {
     nwritten = -1;
     goto out;
   }
+  /* no longer needed */
+  Curl_h1_req_parse_free(&stream->h1);
 
   nheader = Curl_dynhds_count(&h2_headers);
   nva = malloc(sizeof(quiche_h3_header) * nheader);
@@ -973,16 +1035,16 @@
     if(QUICHE_H3_ERR_STREAM_BLOCKED == stream3_id) {
       /* quiche seems to report this error if the connection window is
        * exhausted. Which happens frequently and intermittent. */
-      DEBUGF(LOG_CF(data, cf, "send_request(%s) rejected with BLOCKED",
-                    data->state.url));
+      CURL_TRC_CF(data, cf, "send_request(%s) rejected with BLOCKED",
+                  data->state.url);
       stream_send_suspend(cf, data);
       *err = CURLE_AGAIN;
       nwritten = -1;
       goto out;
     }
     else {
-      DEBUGF(LOG_CF(data, cf, "send_request(%s) -> %" PRId64,
-                    data->state.url, stream3_id));
+      CURL_TRC_CF(data, cf, "send_request(%s) -> %" PRId64,
+                  data->state.url, stream3_id);
     }
     *err = CURLE_SEND_ERROR;
     nwritten = -1;
@@ -995,14 +1057,18 @@
   stream->closed = FALSE;
   stream->reset = FALSE;
 
-  infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
-        stream3_id, (void *)data);
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
-                stream3_id, data->state.url));
+  if(Curl_trc_is_verbose(data)) {
+    infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
+          stream->id, data->state.url);
+    for(i = 0; i < nheader; ++i) {
+      infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->id,
+            (int)nva[i].name_len, nva[i].name,
+            (int)nva[i].value_len, nva[i].value);
+    }
+  }
 
 out:
   free(nva);
-  Curl_h1_req_parse_free(&h1);
   Curl_dynhds_free(&h2_headers);
   return nwritten;
 }
@@ -1036,24 +1102,40 @@
       /* TODO: we seem to be blocked on flow control and should HOLD
        * sending. But when do we open again? */
       if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
-        DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) "
-                      "-> window exhausted", stream->id, len));
+        CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                    "-> window exhausted", stream->id, len);
         stream_send_suspend(cf, data);
       }
       *err = CURLE_AGAIN;
       nwritten = -1;
       goto out;
     }
+    else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE &&
+            stream->closed && stream->resp_hds_complete) {
+      /* sending request body on a stream that has been closed by the
+       * server. If the server has send us a final response, we should
+       * silently discard the send data.
+       * This happens for example on redirects where the server, instead
+       * of reading the full request body just closed the stream after
+       * sending the 30x response.
+       * This is sort of a race: had the transfer loop called recv first,
+       * it would see the response and stop/discard sending on its own- */
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
+                  "on closed stream with response", stream->id);
+      *err = CURLE_OK;
+      nwritten = (ssize_t)len;
+      goto out;
+    }
     else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) {
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) "
-                    "-> exceeds size", stream->id, len));
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                  "-> exceeds size", stream->id, len);
       *err = CURLE_SEND_ERROR;
       nwritten = -1;
       goto out;
     }
     else if(nwritten < 0) {
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send_body(len=%zu) "
-                    "-> quiche err %zd", stream->id, len, nwritten));
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
+                  "-> quiche err %zd", stream->id, len, nwritten);
       *err = CURLE_SEND_ERROR;
       nwritten = -1;
       goto out;
@@ -1067,9 +1149,9 @@
       if(stream->upload_left == 0)
         stream->send_closed = TRUE;
 
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu, "
-                    "left=%zd) -> %zd",
-                    stream->id, len, stream->upload_left, nwritten));
+      CURL_TRC_CF(data, cf, "[%" PRId64 "] send body(len=%zu, "
+                  "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd",
+                  stream->id, len, stream->upload_left, nwritten);
       *err = CURLE_OK;
     }
   }
@@ -1080,8 +1162,8 @@
     *err = result;
     nwritten = -1;
   }
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
-                stream? stream->id : -1, len, nwritten, *err));
+  CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+              stream? stream->id : -1, len, nwritten, *err);
   return nwritten;
 }
 
@@ -1151,10 +1233,8 @@
   (void)arg1;
   (void)arg2;
   switch(event) {
-  case CF_CTRL_DATA_SETUP: {
-    result = h3_data_setup(cf, data);
+  case CF_CTRL_DATA_SETUP:
     break;
-  }
   case CF_CTRL_DATA_PAUSE:
     result = h3_data_pause(cf, data, (arg1 != 0));
     break;
@@ -1172,16 +1252,20 @@
       stream->upload_left = 0;
       body[0] = 'X';
       sent = cf_quiche_send(cf, data, body, 0, &result);
-      DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] DONE_SEND -> %zd, %d",
-                    stream->id, sent, result));
+      CURL_TRC_CF(data, cf, "[%"PRId64"] DONE_SEND -> %zd, %d",
+                  stream->id, sent, result);
     }
     break;
   }
-  case CF_CTRL_DATA_IDLE:
-    result = cf_flush_egress(cf, data);
-    if(result)
-      DEBUGF(LOG_CF(data, cf, "data idle, flush egress -> %d", result));
+  case CF_CTRL_DATA_IDLE: {
+    struct stream_ctx *stream = H3_STREAM_CTX(data);
+    if(stream && !stream->closed) {
+      result = cf_flush_egress(cf, data);
+      if(result)
+        CURL_TRC_CF(data, cf, "data idle, flush egress -> %d", result);
+    }
     break;
+  }
   default:
     break;
   }
@@ -1211,7 +1295,7 @@
       goto out;
   }
   else
-    DEBUGF(LOG_CF(data, cf, "Skipped certificate verification"));
+    CURL_TRC_CF(data, cf, "Skipped certificate verification");
 
   ctx->h3config = quiche_h3_config_new();
   if(!ctx->h3config) {
@@ -1299,15 +1383,9 @@
 
   DEBUGASSERT(!ctx->ssl);
   DEBUGASSERT(!ctx->sslctx);
-  ctx->sslctx = quic_ssl_ctx(data);
-  if(!ctx->sslctx)
-    return CURLE_QUIC_CONNECT_ERROR;
-  ctx->ssl = SSL_new(ctx->sslctx);
-  if(!ctx->ssl)
-    return CURLE_QUIC_CONNECT_ERROR;
-
-  SSL_set_app_data(ctx->ssl, cf);
-  SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
+  result = quic_ssl_setup(cf, data);
+  if(result)
+    return result;
 
   result = Curl_rand(data, ctx->scid, sizeof(ctx->scid));
   if(result)
@@ -1343,11 +1421,6 @@
   }
 #endif
 
-  /* we do not get a setup event for the initial transfer */
-  result = h3_data_setup(cf, data);
-  if(result)
-    return result;
-
   result = cf_flush_egress(cf, data);
   if(result)
     return result;
@@ -1363,8 +1436,8 @@
       offset += 1 + alpn_len;
     }
 
-    DEBUGF(LOG_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
-                   alpn_protocols + 1));
+    CURL_TRC_CF(data, cf, "Sent QUIC client Initial, ALPN: %s",
+                alpn_protocols + 1);
   }
 
   return CURLE_OK;
@@ -1395,7 +1468,7 @@
 
   if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
     /* Not time yet to attempt the next connect */
-    DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
+    CURL_TRC_CF(data, cf, "waiting for reconnect time");
     goto out;
   }
 
@@ -1418,12 +1491,12 @@
     goto out;
 
   if(quiche_conn_is_established(ctx->qconn)) {
-    DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
-           (int)Curl_timediff(now, ctx->started_at)));
+    CURL_TRC_CF(data, cf, "handshake complete after %dms",
+                (int)Curl_timediff(now, ctx->started_at));
     ctx->handshake_at = now;
     result = cf_verify_peer(cf, data);
     if(!result) {
-      DEBUGF(LOG_CF(data, cf, "peer verified"));
+      CURL_TRC_CF(data, cf, "peer verified");
       cf->connected = TRUE;
       cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
@@ -1442,8 +1515,8 @@
      */
     int reconn_delay_ms = 200;
 
-    DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
-                  reconn_delay_ms));
+    CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
+                reconn_delay_ms);
     Curl_conn_cf_close(cf->next, data);
     cf_quiche_ctx_clear(ctx);
     result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
@@ -1509,7 +1582,7 @@
       max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
     }
     *pres1 = (max_streams > INT_MAX)? INT_MAX : (int)max_streams;
-    DEBUGF(LOG_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1));
+    CURL_TRC_CF(data, cf, "query: MAX_CONCURRENT -> %d", *pres1);
     return CURLE_OK;
   }
   case CF_QUERY_CONNECT_REPLY_MS:
@@ -1555,13 +1628,11 @@
        not in use by any other transfer, there shouldn't be any data here,
        only "protocol frames" */
     *input_pending = FALSE;
-    Curl_attach_connection(data, cf->conn);
     if(cf_process_ingress(cf, data))
       alive = FALSE;
     else {
       alive = TRUE;
     }
-    Curl_detach_connection(data);
   }
 
   return alive;
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c
index f850029..9a1a1bb 100644
--- a/Utilities/cmcurl/lib/vquic/vquic.c
+++ b/Utilities/cmcurl/lib/vquic/vquic.c
@@ -43,12 +43,14 @@
 #include "bufq.h"
 #include "dynbuf.h"
 #include "cfilters.h"
-#include "curl_log.h"
+#include "curl_trc.h"
 #include "curl_msh3.h"
 #include "curl_ngtcp2.h"
 #include "curl_quiche.h"
+#include "rand.h"
 #include "vquic.h"
 #include "vquic_int.h"
+#include "strerror.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -88,6 +90,16 @@
 #else
   qctx->no_gso = TRUE;
 #endif
+#ifdef DEBUGBUILD
+  {
+    char *p = getenv("CURL_DBG_QUIC_WBLOCK");
+    if(p) {
+      long l = strtol(p, NULL, 10);
+      if(l >= 0 && l <= 100)
+        qctx->wblock_percent = (int)l;
+    }
+  }
+#endif
 
   return CURLE_OK;
 }
@@ -230,6 +242,17 @@
                                    const uint8_t *pkt, size_t pktlen,
                                    size_t gsolen, size_t *psent)
 {
+#ifdef DEBUGBUILD
+  /* simulate network blocking/partial writes */
+  if(qctx->wblock_percent > 0) {
+    unsigned char c;
+    Curl_rand(data, &c, 1);
+    if(c >= ((100-qctx->wblock_percent)*256/100)) {
+      CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK");
+      return CURLE_AGAIN;
+    }
+  }
+#endif
   if(qctx->no_gso && pktlen > gsolen) {
     return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
   }
@@ -253,11 +276,9 @@
         blen = qctx->split_len;
     }
 
-    DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)",
-                  blen, gsolen));
     result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent);
-    DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
-                  blen, gsolen, result, sent));
+    CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
+                blen, gsolen, result, sent);
     if(result) {
       if(result == CURLE_AGAIN) {
         Curl_bufq_skip(&qctx->sendbuf, sent);
@@ -288,9 +309,9 @@
   qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
   qctx->split_gsolen = gsolen;
   qctx->gsolen = tail_gsolen;
-  DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
-                qctx->split_len, qctx->split_gsolen,
-                tail_len, qctx->gsolen));
+  CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
+              qctx->split_len, qctx->split_gsolen,
+              tail_len, qctx->gsolen);
   return vquic_flush(cf, data, qctx);
 }
 
@@ -308,6 +329,7 @@
   struct sockaddr_storage remote_addr[MMSG_NUM];
   size_t total_nread, pkts;
   int mcount, i, n;
+  char errstr[STRERROR_LEN];
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(max_pkts > 0);
@@ -330,12 +352,12 @@
       ;
     if(mcount == -1) {
       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
-        DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN"));
+        CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
         goto out;
       }
       if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
-        const char *r_ip;
-        int r_port;
+        const char *r_ip = NULL;
+        int r_port = 0;
         Curl_cf_socket_peek(cf->next, data, NULL, NULL,
                             &r_ip, &r_port, NULL, NULL);
         failf(data, "QUIC: connection to %s port %u refused",
@@ -343,13 +365,14 @@
         result = CURLE_COULDNT_CONNECT;
         goto out;
       }
-      failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)",
-                  mcount, SOCKERRNO);
+      Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
+      failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)",
+                  mcount, SOCKERRNO, errstr);
       result = CURLE_RECV_ERROR;
       goto out;
     }
 
-    DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount));
+    CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount);
     pkts += mcount;
     for(i = 0; i < mcount; ++i) {
       total_nread += mmsg[i].msg_len;
@@ -362,8 +385,9 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d",
-                pkts, total_nread, result));
+  if(total_nread || result)
+    CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
+                pkts, total_nread, result);
   return result;
 }
 
@@ -380,6 +404,7 @@
   struct sockaddr_storage remote_addr;
   size_t total_nread, pkts;
   ssize_t nread;
+  char errstr[STRERROR_LEN];
   CURLcode result = CURLE_OK;
 
   msg_iov.iov_base = buf;
@@ -401,8 +426,8 @@
         goto out;
       }
       if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
-        const char *r_ip;
-        int r_port;
+        const char *r_ip = NULL;
+        int r_port = 0;
         Curl_cf_socket_peek(cf->next, data, NULL, NULL,
                             &r_ip, &r_port, NULL, NULL);
         failf(data, "QUIC: connection to %s port %u refused",
@@ -410,8 +435,9 @@
         result = CURLE_COULDNT_CONNECT;
         goto out;
       }
-      failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)",
-                  nread, SOCKERRNO);
+      Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
+      failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
+                  nread, SOCKERRNO, errstr);
       result = CURLE_RECV_ERROR;
       goto out;
     }
@@ -425,8 +451,9 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d",
-                pkts, total_nread, result));
+  if(total_nread || result)
+    CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
+                pkts, total_nread, result);
   return result;
 }
 
@@ -443,6 +470,7 @@
   socklen_t remote_addrlen = sizeof(remote_addr);
   size_t total_nread, pkts;
   ssize_t nread;
+  char errstr[STRERROR_LEN];
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(max_pkts > 0);
@@ -454,12 +482,12 @@
       ;
     if(nread == -1) {
       if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
-        DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN"));
+        CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
         goto out;
       }
       if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
-        const char *r_ip;
-        int r_port;
+        const char *r_ip = NULL;
+        int r_port = 0;
         Curl_cf_socket_peek(cf->next, data, NULL, NULL,
                             &r_ip, &r_port, NULL, NULL);
         failf(data, "QUIC: connection to %s port %u refused",
@@ -467,8 +495,9 @@
         result = CURLE_COULDNT_CONNECT;
         goto out;
       }
-      failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)",
-                  nread, SOCKERRNO);
+      Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
+      failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
+                  nread, SOCKERRNO, errstr);
       result = CURLE_RECV_ERROR;
       goto out;
     }
@@ -482,8 +511,9 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d",
-                pkts, total_nread, result));
+  if(total_nread || result)
+    CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
+                pkts, total_nread, result);
   return result;
 }
 #endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */
diff --git a/Utilities/cmcurl/lib/vquic/vquic_int.h b/Utilities/cmcurl/lib/vquic/vquic_int.h
index 8e08784..dbcd009 100644
--- a/Utilities/cmcurl/lib/vquic/vquic_int.h
+++ b/Utilities/cmcurl/lib/vquic/vquic_int.h
@@ -41,6 +41,9 @@
   size_t gsolen; /* length of individual packets in send buf */
   size_t split_len; /* if != 0, buffer length after which GSO differs */
   size_t split_gsolen; /* length of individual packets after split_len */
+#ifdef DEBUGBUILD
+  int wblock_percent; /* percent of writes doing EAGAIN */
+#endif
   bool no_gso; /* do not use gso on sending */
 };
 
diff --git a/Utilities/cmcurl/lib/vssh/libssh.c b/Utilities/cmcurl/lib/vssh/libssh.c
index 1cecb64..b0f49d6 100644
--- a/Utilities/cmcurl/lib/vssh/libssh.c
+++ b/Utilities/cmcurl/lib/vssh/libssh.c
@@ -40,9 +40,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -2186,7 +2183,7 @@
     myssh_setup_connection(data, conn);
 
   /* We default to persistent connections. We set this already in this connect
-     function to make the re-use checks properly be able to check this bit. */
+     function to make the reuse checks properly be able to check this bit. */
   connkeep(conn, "SSH default");
 
   if(conn->handler->protocol & CURLPROTO_SCP) {
@@ -2570,6 +2567,12 @@
   struct connectdata *conn = data->conn;
   (void)sockindex;
 
+  /* limit the writes to the maximum specified in Section 3 of
+   * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
+   */
+  if(len > 32768)
+    len = 32768;
+
   nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
 
   myssh_block2waitfor(conn, FALSE);
@@ -2657,7 +2660,7 @@
   /* if a command starts with an asterisk, which a legal SFTP command never
      can, the command will be allowed to fail without it causing any
      aborts or cancels etc. It will cause libcurl to act as if the command
-     is successful, whatever the server reponds. */
+     is successful, whatever the server responds. */
 
   if(cmd[0] == '*') {
     cmd++;
@@ -2831,7 +2834,7 @@
   /* if a command starts with an asterisk, which a legal SFTP command never
      can, the command will be allowed to fail without it causing any
      aborts or cancels etc. It will cause libcurl to act as if the command
-     is successful, whatever the server reponds. */
+     is successful, whatever the server responds. */
 
   if(cmd[0] == '*') {
     cmd++;
diff --git a/Utilities/cmcurl/lib/vssh/libssh2.c b/Utilities/cmcurl/lib/vssh/libssh2.c
index 14c2784..f539b39 100644
--- a/Utilities/cmcurl/lib/vssh/libssh2.c
+++ b/Utilities/cmcurl/lib/vssh/libssh2.c
@@ -43,9 +43,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_UTSNAME_H
-#include <sys/utsname.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
@@ -100,11 +97,9 @@
 
 /* Local functions: */
 static const char *sftp_libssh2_strerror(unsigned long err);
-#ifdef CURL_LIBSSH2_DEBUG
 static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
 static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
 static LIBSSH2_FREE_FUNC(my_libssh2_free);
-#endif
 static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
 static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
 static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
@@ -284,8 +279,6 @@
   return CURLE_SSH;
 }
 
-#ifdef CURL_LIBSSH2_DEBUG
-
 static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
 {
   (void)abstract; /* arg not used */
@@ -305,8 +298,6 @@
     free(ptr);
 }
 
-#endif
-
 /*
  * SSH State machine related code
  */
@@ -895,6 +886,7 @@
     }
 
     if(found) {
+      int rc;
       infof(data, "Found host %s in %s",
             conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
 
@@ -944,9 +936,15 @@
       }
 
       infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method);
-      result = libssh2_session_error_to_CURLE(
-        libssh2_session_method_pref(
-          sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method));
+      rc = libssh2_session_method_pref(sshc->ssh_session,
+                                       LIBSSH2_METHOD_HOSTKEY, hostkey_method);
+      if(rc) {
+        char *errmsg = NULL;
+        int errlen;
+        libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0);
+        failf(data, "libssh2: %s", errmsg);
+        result = libssh2_session_error_to_CURLE(rc);
+      }
     }
     else {
       infof(data, "Did not find host %s in %s",
@@ -1180,8 +1178,16 @@
       }
       else {
         char *err_msg = NULL;
-        (void)libssh2_session_last_error(sshc->ssh_session,
-                                         &err_msg, NULL, 0);
+        char unknown[] = "Reason unknown (-1)";
+        if(rc == -1) {
+          /* No error message has been set and the last set error message, if
+             any, is from a previous error so ignore it. #11837 */
+          err_msg = unknown;
+        }
+        else {
+          (void)libssh2_session_last_error(sshc->ssh_session,
+                                           &err_msg, NULL, 0);
+        }
         infof(data, "SSH public key authentication failed: %s", err_msg);
         state(data, SSH_AUTH_PASS_INIT);
         rc = 0; /* clear rc and continue */
@@ -1497,7 +1503,7 @@
       /* if a command starts with an asterisk, which a legal SFTP command never
          can, the command will be allowed to fail without it causing any
          aborts or cancels etc. It will cause libcurl to act as if the command
-         is successful, whatever the server reponds. */
+         is successful, whatever the server responds. */
 
       if(cmd[0] == '*') {
         cmd++;
@@ -1693,7 +1699,7 @@
       /* if a command starts with an asterisk, which a legal SFTP command never
          can, the command will be allowed to fail without it causing any
          aborts or cancels etc. It will cause libcurl to act as if the command
-         is successful, whatever the server reponds. */
+         is successful, whatever the server responds. */
 
       if(cmd[0] == '*') {
         cmd++;
@@ -2536,7 +2542,8 @@
           if(from > size) {
             failf(data, "Offset (%"
                   CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
-                  CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize);
+                  CURL_FORMAT_CURL_OFF_T ")", from,
+                  (curl_off_t)attrs.filesize);
             return CURLE_BAD_DOWNLOAD_RESUME;
           }
           if(from > to) {
@@ -2562,7 +2569,7 @@
             failf(data, "Offset (%"
                   CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
                   CURL_FORMAT_CURL_OFF_T ")",
-                  data->state.resume_from, attrs.filesize);
+                  data->state.resume_from, (curl_off_t)attrs.filesize);
             return CURLE_BAD_DOWNLOAD_RESUME;
           }
           /* download from where? */
@@ -2572,7 +2579,7 @@
           if((curl_off_t)attrs.filesize < data->state.resume_from) {
             failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
                   ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
-                  data->state.resume_from, attrs.filesize);
+                  data->state.resume_from, (curl_off_t)attrs.filesize);
             return CURLE_BAD_DOWNLOAD_RESUME;
           }
         }
@@ -3253,7 +3260,7 @@
   }
 
   /* We default to persistent connections. We set this already in this connect
-     function to make the re-use checks properly be able to check this bit. */
+     function to make the reuse checks properly be able to check this bit. */
   connkeep(conn, "SSH default");
 
   sshc = &conn->proto.sshc;
@@ -3268,13 +3275,12 @@
   sock = conn->sock[FIRSTSOCKET];
 #endif /* CURL_LIBSSH2_DEBUG */
 
-#ifdef CURL_LIBSSH2_DEBUG
+  /* libcurl MUST to set custom memory functions so that the kbd_callback
+     funciton's memory allocations can be properled freed */
   sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
                                               my_libssh2_free,
                                               my_libssh2_realloc, data);
-#else
-  sshc->ssh_session = libssh2_session_init_ex(NULL, NULL, NULL, data);
-#endif
+
   if(!sshc->ssh_session) {
     failf(data, "Failure initialising ssh session");
     return CURLE_FAILED_INIT;
diff --git a/Utilities/cmcurl/lib/vssh/wolfssh.c b/Utilities/cmcurl/lib/vssh/wolfssh.c
index 780b612..39cee50 100644
--- a/Utilities/cmcurl/lib/vssh/wolfssh.c
+++ b/Utilities/cmcurl/lib/vssh/wolfssh.c
@@ -277,7 +277,7 @@
     return -1;
   }
   DEBUGASSERT(rc == (int)len);
-  infof(data, "sent %zd bytes SFTP from offset %zd",
+  infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T,
         len, sshc->offset);
   sshc->offset += len;
   return (ssize_t)rc;
@@ -374,7 +374,7 @@
     wssh_setup_connection(data, conn);
 
   /* We default to persistent connections. We set this already in this connect
-     function to make the re-use checks properly be able to check this bit. */
+     function to make the reuse checks properly be able to check this bit. */
   connkeep(conn, "SSH default");
 
   if(conn->handler->protocol & CURLPROTO_SCP) {
@@ -1168,6 +1168,7 @@
 }
 void Curl_ssh_cleanup(void)
 {
+  (void)wolfSSH_Cleanup();
 }
 
 #endif /* USE_WOLFSSH */
diff --git a/Utilities/cmcurl/lib/vtls/bearssl.c b/Utilities/cmcurl/lib/vtls/bearssl.c
index 2b666ca..934149c 100644
--- a/Utilities/cmcurl/lib/vtls/bearssl.c
+++ b/Utilities/cmcurl/lib/vtls/bearssl.c
@@ -52,7 +52,7 @@
   int cert_num;
 };
 
-struct ssl_backend_data {
+struct bearssl_ssl_backend_data {
   br_ssl_client_context ctx;
   struct x509_context x509;
   unsigned char buf[BR_SSL_BUFSIZE_BIDI];
@@ -574,7 +574,8 @@
                                       struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
@@ -586,6 +587,7 @@
   const bool verifyhost = conn_config->verifyhost;
   CURLcode ret;
   unsigned version_min, version_max;
+  int session_set = 0;
 #ifdef ENABLE_IPV6
   struct in6_addr addr;
 #else
@@ -593,6 +595,7 @@
 #endif
 
   DEBUGASSERT(backend);
+  CURL_TRC_CF(data, cf, "connect_step1");
 
   switch(conn_config->version) {
   case CURL_SSLVERSION_SSLv2:
@@ -623,38 +626,34 @@
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if(ca_info_blob) {
-    struct cafile_source source;
-    source.type = CAFILE_SOURCE_BLOB;
-    source.data = ca_info_blob->data;
-    source.len = ca_info_blob->len;
+  if(verifypeer) {
+    if(ca_info_blob) {
+      struct cafile_source source;
+      source.type = CAFILE_SOURCE_BLOB;
+      source.data = ca_info_blob->data;
+      source.len = ca_info_blob->len;
 
-    ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
-    if(ret != CURLE_OK) {
-      if(verifypeer) {
+      CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob");
+      ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+      if(ret != CURLE_OK) {
         failf(data, "error importing CA certificate blob");
         return ret;
       }
-      /* Only warn if no certificate verification is required. */
-      infof(data, "error importing CA certificate blob, continuing anyway");
     }
-  }
 
-  if(ssl_cafile) {
-    struct cafile_source source;
-    source.type = CAFILE_SOURCE_PATH;
-    source.data = ssl_cafile;
-    source.len = 0;
+    if(ssl_cafile) {
+      struct cafile_source source;
+      source.type = CAFILE_SOURCE_PATH;
+      source.data = ssl_cafile;
+      source.len = 0;
 
-    ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
-    if(ret != CURLE_OK) {
-      if(verifypeer) {
+      CURL_TRC_CF(data, cf, "connect_step1, load cafile");
+      ret = load_cafile(&source, &backend->anchors, &backend->anchors_len);
+      if(ret != CURLE_OK) {
         failf(data, "error setting certificate verify locations."
               " CAfile: %s", ssl_cafile);
         return ret;
       }
-      infof(data, "error setting certificate verify locations,"
-            " continuing anyway:");
     }
   }
 
@@ -668,6 +667,7 @@
   if(conn_config->cipher_list) {
     /* Override the ciphers as specified. For the default cipher list see the
        BearSSL source code of br_ssl_client_init_full() */
+    CURL_TRC_CF(data, cf, "connect_step1, set ciphers");
     ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng,
                                        conn_config->cipher_list);
     if(ret)
@@ -683,10 +683,12 @@
   if(ssl_config->primary.sessionid) {
     void *session;
 
+    CURL_TRC_CF(data, cf, "connect_step1, check session cache");
     Curl_ssl_sessionid_lock(data);
     if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) {
       br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
-      infof(data, "BearSSL: re-using session ID");
+      session_set = 1;
+      infof(data, "BearSSL: reusing session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -723,6 +725,7 @@
       return CURLE_SSL_CONNECT_ERROR;
     }
     hostname = snihost;
+    CURL_TRC_CF(data, cf, "connect_step1, SNI set");
   }
 
   /* give application a chance to interfere with SSL set up. */
@@ -737,7 +740,7 @@
     }
   }
 
-  if(!br_ssl_client_reset(&backend->ctx, hostname, 1))
+  if(!br_ssl_client_reset(&backend->ctx, hostname, session_set))
     return CURLE_FAILED_INIT;
   backend->active = TRUE;
 
@@ -746,12 +749,35 @@
   return CURLE_OK;
 }
 
+static int bearssl_get_select_socks(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    curl_socket_t *socks)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
+
+  if(sock == CURL_SOCKET_BAD)
+    return GETSOCK_BLANK;
+  else {
+    struct bearssl_ssl_backend_data *backend =
+      (struct bearssl_ssl_backend_data *)connssl->backend;
+    unsigned state = br_ssl_engine_current_state(&backend->ctx.eng);
+    if(state & BR_SSL_SENDREC) {
+      socks[0] = sock;
+      return GETSOCK_WRITESOCK(0);
+    }
+  }
+  socks[0] = sock;
+  return GETSOCK_READSOCK(0);
+}
+
 static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   unsigned target)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned state;
   unsigned char *buf;
   size_t len;
@@ -796,6 +822,7 @@
     if(state & BR_SSL_SENDREC) {
       buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
       ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, &result);
+      CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
       if(ret <= 0) {
         return result;
       }
@@ -804,6 +831,7 @@
     else if(state & BR_SSL_RECVREC) {
       buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len);
       ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result);
+      CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result);
       if(ret == 0) {
         failf(data, "SSL: EOF without close notify");
         return CURLE_READ_ERROR;
@@ -820,20 +848,31 @@
                                       struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   CURLcode ret;
 
   DEBUGASSERT(backend);
+  CURL_TRC_CF(data, cf, "connect_step2");
 
   ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
   if(ret == CURLE_AGAIN)
     return CURLE_OK;
   if(ret == CURLE_OK) {
+    unsigned int tver;
     if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) {
       failf(data, "SSL: connection closed during handshake");
       return CURLE_SSL_CONNECT_ERROR;
     }
     connssl->connecting_state = ssl_connect_3;
+    /* Informational message */
+    tver = br_ssl_engine_get_version(&backend->ctx.eng);
+    if(tver == 0x0303)
+      infof(data, "SSL connection using TLSv1.2");
+    else if(tver == 0x0304)
+      infof(data, "SSL connection using TLSv1.3");
+    else
+      infof(data, "SSL connection using TLS 0x%x", tver);
   }
   return ret;
 }
@@ -842,12 +881,14 @@
                                       struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   CURLcode ret;
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
   DEBUGASSERT(backend);
+  CURL_TRC_CF(data, cf, "connect_step3");
 
   if(connssl->alpn) {
     const char *proto;
@@ -889,7 +930,8 @@
                             const void *buf, size_t len, CURLcode *err)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned char *app;
   size_t applen;
 
@@ -923,7 +965,8 @@
                             char *buf, size_t len, CURLcode *err)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned char *app;
   size_t applen;
 
@@ -954,8 +997,10 @@
   timediff_t timeout_ms;
   int what;
 
+  CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking);
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
+    CURL_TRC_CF(data, cf, "connect_common, connected");
     *done = TRUE;
     return CURLE_OK;
   }
@@ -987,8 +1032,10 @@
       curl_socket_t readfd = ssl_connect_2_reading ==
         connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
 
+      CURL_TRC_CF(data, cf, "connect_common, check socket");
       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
                                nonblocking?0:timeout_ms);
+      CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what);
       if(what < 0) {
         /* fatal error */
         failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -1050,10 +1097,12 @@
                                  const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct bearssl_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  return br_ssl_engine_current_state(&ctx->backend->ctx.eng) & BR_SSL_RECVAPP;
+  backend = (struct bearssl_ssl_backend_data *)ctx->backend;
+  return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP;
 }
 
 static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
@@ -1101,7 +1150,8 @@
 static void *bearssl_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   DEBUGASSERT(backend);
   return &backend->ctx;
 }
@@ -1109,7 +1159,8 @@
 static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   size_t i;
 
   DEBUGASSERT(backend);
@@ -1147,7 +1198,7 @@
 const struct Curl_ssl Curl_ssl_bearssl = {
   { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
   SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY,
-  sizeof(struct ssl_backend_data),
+  sizeof(struct bearssl_ssl_backend_data),
 
   Curl_none_init,                  /* init */
   Curl_none_cleanup,               /* cleanup */
@@ -1159,7 +1210,7 @@
   Curl_none_cert_status_request,   /* cert_status_request */
   bearssl_connect,                 /* connect */
   bearssl_connect_nonblocking,     /* connect_nonblocking */
-  Curl_ssl_get_select_socks,                /* getsock */
+  bearssl_get_select_socks,        /* getsock */
   bearssl_get_internals,           /* get_internals */
   bearssl_close,                   /* close_one */
   Curl_none_close_all,             /* close_all */
diff --git a/Utilities/cmcurl/lib/vtls/gskit.c b/Utilities/cmcurl/lib/vtls/gskit.c
deleted file mode 100644
index 749dc91..0000000
--- a/Utilities/cmcurl/lib/vtls/gskit.c
+++ /dev/null
@@ -1,1327 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_GSKIT
-
-#include <gskssl.h>
-#include <qsoasync.h>
-#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */
-#include "socketpair.h"
-#include "strerror.h"
-
-/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */
-#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST
-#define GSK_SSL_EXTN_SERVERNAME_REQUEST         230
-#endif
-
-#ifndef GSK_TLSV10_CIPHER_SPECS
-#define GSK_TLSV10_CIPHER_SPECS                 236
-#endif
-
-#ifndef GSK_TLSV11_CIPHER_SPECS
-#define GSK_TLSV11_CIPHER_SPECS                 237
-#endif
-
-#ifndef GSK_TLSV12_CIPHER_SPECS
-#define GSK_TLSV12_CIPHER_SPECS                 238
-#endif
-
-#ifndef GSK_PROTOCOL_TLSV11
-#define GSK_PROTOCOL_TLSV11                     437
-#endif
-
-#ifndef GSK_PROTOCOL_TLSV12
-#define GSK_PROTOCOL_TLSV12                     438
-#endif
-
-#ifndef GSK_FALSE
-#define GSK_FALSE                               0
-#endif
-
-#ifndef GSK_TRUE
-#define GSK_TRUE                                1
-#endif
-
-
-#include <limits.h>
-
-#include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "gskit.h"
-#include "vtls.h"
-#include "vtls_int.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "strcase.h"
-#include "timediff.h"
-#include "x509asn1.h"
-#include "curl_printf.h"
-
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-
-/* Directions. */
-#define SOS_READ        0x01
-#define SOS_WRITE       0x02
-
-/* SSL version flags. */
-#define CURL_GSKPROTO_SSLV2     0
-#define CURL_GSKPROTO_SSLV2_MASK        (1 << CURL_GSKPROTO_SSLV2)
-#define CURL_GSKPROTO_SSLV3     1
-#define CURL_GSKPROTO_SSLV3_MASK        (1 << CURL_GSKPROTO_SSLV3)
-#define CURL_GSKPROTO_TLSV10    2
-#define CURL_GSKPROTO_TLSV10_MASK        (1 << CURL_GSKPROTO_TLSV10)
-#define CURL_GSKPROTO_TLSV11    3
-#define CURL_GSKPROTO_TLSV11_MASK        (1 << CURL_GSKPROTO_TLSV11)
-#define CURL_GSKPROTO_TLSV12    4
-#define CURL_GSKPROTO_TLSV12_MASK        (1 << CURL_GSKPROTO_TLSV12)
-#define CURL_GSKPROTO_LAST      5
-
-struct ssl_backend_data {
-  gsk_handle handle;
-  int iocport;
-  int localfd;
-  int remotefd;
-};
-
-#define BACKEND connssl->backend
-
-/* Supported ciphers. */
-struct gskit_cipher {
-  const char *name;            /* Cipher name. */
-  const char *gsktoken;        /* Corresponding token for GSKit String. */
-  unsigned int versions;       /* SSL version flags. */
-};
-
-static const struct gskit_cipher  ciphertable[] = {
-  { "null-md5",         "01",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
-      CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
-  { "null-sha",         "02",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
-      CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
-  { "exp-rc4-md5",      "03",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK },
-  { "rc4-md5",          "04",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
-      CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
-  { "rc4-sha",          "05",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
-      CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
-  { "exp-rc2-cbc-md5",  "06",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK },
-  { "exp-des-cbc-sha",  "09",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
-      CURL_GSKPROTO_TLSV11_MASK },
-  { "des-cbc3-sha",     "0A",
-      CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK |
-      CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK },
-  { "aes128-sha",       "2F",
-      CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
-      CURL_GSKPROTO_TLSV12_MASK },
-  { "aes256-sha",       "35",
-      CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK |
-      CURL_GSKPROTO_TLSV12_MASK },
-  { "null-sha256",      "3B",   CURL_GSKPROTO_TLSV12_MASK },
-  { "aes128-sha256",    "3C",   CURL_GSKPROTO_TLSV12_MASK },
-  { "aes256-sha256",    "3D",   CURL_GSKPROTO_TLSV12_MASK },
-  { "aes128-gcm-sha256",
-                        "9C",   CURL_GSKPROTO_TLSV12_MASK },
-  { "aes256-gcm-sha384",
-                        "9D",   CURL_GSKPROTO_TLSV12_MASK },
-  { "rc4-md5",          "1",    CURL_GSKPROTO_SSLV2_MASK },
-  { "exp-rc4-md5",      "2",    CURL_GSKPROTO_SSLV2_MASK },
-  { "rc2-md5",          "3",    CURL_GSKPROTO_SSLV2_MASK },
-  { "exp-rc2-md5",      "4",    CURL_GSKPROTO_SSLV2_MASK },
-  { "des-cbc-md5",      "6",    CURL_GSKPROTO_SSLV2_MASK },
-  { "des-cbc3-md5",     "7",    CURL_GSKPROTO_SSLV2_MASK },
-  { (const char *) NULL, (const char *) NULL, 0       }
-};
-
-
-static bool is_separator(char c)
-{
-  /* Return whether character is a cipher list separator. */
-  switch(c) {
-  case ' ':
-  case '\t':
-  case ':':
-  case ',':
-  case ';':
-    return true;
-  }
-  return false;
-}
-
-
-static CURLcode gskit_status(struct Curl_easy *data, int rc,
-                             const char *procname, CURLcode defcode)
-{
-  char buffer[STRERROR_LEN];
-  /* Process GSKit status and map it to a CURLcode. */
-  switch(rc) {
-  case GSK_OK:
-  case GSK_OS400_ASYNCHRONOUS_SOC_INIT:
-    return CURLE_OK;
-  case GSK_KEYRING_OPEN_ERROR:
-  case GSK_OS400_ERROR_NO_ACCESS:
-    return CURLE_SSL_CACERT_BADFILE;
-  case GSK_INSUFFICIENT_STORAGE:
-    return CURLE_OUT_OF_MEMORY;
-  case GSK_ERROR_BAD_V2_CIPHER:
-  case GSK_ERROR_BAD_V3_CIPHER:
-  case GSK_ERROR_NO_CIPHERS:
-    return CURLE_SSL_CIPHER;
-  case GSK_OS400_ERROR_NOT_TRUSTED_ROOT:
-  case GSK_ERROR_CERT_VALIDATION:
-    return CURLE_PEER_FAILED_VERIFICATION;
-  case GSK_OS400_ERROR_TIMED_OUT:
-    return CURLE_OPERATION_TIMEDOUT;
-  case GSK_WOULD_BLOCK:
-    return CURLE_AGAIN;
-  case GSK_OS400_ERROR_NOT_REGISTERED:
-    break;
-  case GSK_ERROR_IO:
-    switch(errno) {
-    case ENOMEM:
-      return CURLE_OUT_OF_MEMORY;
-    default:
-      failf(data, "%s I/O error: %s", procname,
-            Curl_strerror(errno, buffer, sizeof(buffer)));
-      break;
-    }
-    break;
-  default:
-    failf(data, "%s: %s", procname, gsk_strerror(rc));
-    break;
-  }
-  return defcode;
-}
-
-
-static CURLcode set_enum(struct Curl_easy *data, gsk_handle h,
-                GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok)
-{
-  char buffer[STRERROR_LEN];
-  int rc = gsk_attribute_set_enum(h, id, value);
-
-  switch(rc) {
-  case GSK_OK:
-    return CURLE_OK;
-  case GSK_ERROR_IO:
-    failf(data, "gsk_attribute_set_enum() I/O error: %s",
-          Curl_strerror(errno, buffer, sizeof(buffer)));
-    break;
-  case GSK_ATTRIBUTE_INVALID_ID:
-    if(unsupported_ok)
-      return CURLE_UNSUPPORTED_PROTOCOL;
-  default:
-    failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc));
-    break;
-  }
-  return CURLE_SSL_CONNECT_ERROR;
-}
-
-
-static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h,
-                        GSK_BUF_ID id, const char *buf, bool unsupported_ok)
-{
-  char buffer[STRERROR_LEN];
-  int rc = gsk_attribute_set_buffer(h, id, buf, 0);
-
-  switch(rc) {
-  case GSK_OK:
-    return CURLE_OK;
-  case GSK_ERROR_IO:
-    failf(data, "gsk_attribute_set_buffer() I/O error: %s",
-          Curl_strerror(errno, buffer, sizeof(buffer)));
-    break;
-  case GSK_ATTRIBUTE_INVALID_ID:
-    if(unsupported_ok)
-      return CURLE_UNSUPPORTED_PROTOCOL;
-  default:
-    failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc));
-    break;
-  }
-  return CURLE_SSL_CONNECT_ERROR;
-}
-
-
-static CURLcode set_numeric(struct Curl_easy *data,
-                            gsk_handle h, GSK_NUM_ID id, int value)
-{
-  char buffer[STRERROR_LEN];
-  int rc = gsk_attribute_set_numeric_value(h, id, value);
-
-  switch(rc) {
-  case GSK_OK:
-    return CURLE_OK;
-  case GSK_ERROR_IO:
-    failf(data, "gsk_attribute_set_numeric_value() I/O error: %s",
-          Curl_strerror(errno, buffer, sizeof(buffer)));
-    break;
-  default:
-    failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc));
-    break;
-  }
-  return CURLE_SSL_CONNECT_ERROR;
-}
-
-
-static CURLcode set_ciphers(struct Curl_cfilter *cf, struct Curl_easy *data,
-                            gsk_handle h, unsigned int *protoflags)
-{
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct connectdata *conn = data->conn;
-  const char *cipherlist = conn_config->cipher_list;
-  const char *clp;
-  const struct gskit_cipher *ctp;
-  int i;
-  int l;
-  bool unsupported;
-  CURLcode result;
-  struct {
-    char *buf;
-    char *ptr;
-  } ciphers[CURL_GSKPROTO_LAST];
-
-  /* Compile cipher list into GSKit-compatible cipher lists. */
-
-  if(!cipherlist)
-    return CURLE_OK;
-  while(is_separator(*cipherlist))     /* Skip initial separators. */
-    cipherlist++;
-  if(!*cipherlist)
-    return CURLE_OK;
-
-  /* We allocate GSKit buffers of the same size as the input string: since
-     GSKit tokens are always shorter than their cipher names, allocated buffers
-     will always be large enough to accommodate the result. */
-  l = strlen(cipherlist) + 1;
-  memset(ciphers, 0, sizeof(ciphers));
-  for(i = 0; i < CURL_GSKPROTO_LAST; i++) {
-    ciphers[i].buf = malloc(l);
-    if(!ciphers[i].buf) {
-      while(i--)
-        free(ciphers[i].buf);
-      return CURLE_OUT_OF_MEMORY;
-    }
-    ciphers[i].ptr = ciphers[i].buf;
-    *ciphers[i].ptr = '\0';
-  }
-
-  /* Process each cipher in input string. */
-  unsupported = FALSE;
-  result = CURLE_OK;
-  for(;;) {
-    for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);)
-      cipherlist++;
-    l = cipherlist - clp;
-    if(!l)
-      break;
-    /* Search the cipher in our table. */
-    for(ctp = ciphertable; ctp->name; ctp++)
-      if(strncasecompare(ctp->name, clp, l) && !ctp->name[l])
-        break;
-    if(!ctp->name) {
-      failf(data, "Unknown cipher %.*s", l, clp);
-      result = CURLE_SSL_CIPHER;
-    }
-    else {
-      unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK |
-                        CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK));
-      for(i = 0; i < CURL_GSKPROTO_LAST; i++) {
-        if(ctp->versions & (1 << i)) {
-          strcpy(ciphers[i].ptr, ctp->gsktoken);
-          ciphers[i].ptr += strlen(ctp->gsktoken);
-        }
-      }
-    }
-
-   /* Advance to next cipher name or end of string. */
-    while(is_separator(*cipherlist))
-      cipherlist++;
-  }
-
-  /* Disable protocols with empty cipher lists. */
-  for(i = 0; i < CURL_GSKPROTO_LAST; i++) {
-    if(!(*protoflags & (1 << i)) || !ciphers[i].buf[0]) {
-      *protoflags &= ~(1 << i);
-      ciphers[i].buf[0] = '\0';
-    }
-  }
-
-  /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */
-  if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) {
-    result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS,
-                        ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE);
-    if(result == CURLE_UNSUPPORTED_PROTOCOL) {
-      result = CURLE_OK;
-      if(unsupported) {
-        failf(data, "TLSv1.1-only ciphers are not yet supported");
-        result = CURLE_SSL_CIPHER;
-      }
-    }
-  }
-  if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) {
-    result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS,
-                        ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE);
-    if(result == CURLE_UNSUPPORTED_PROTOCOL) {
-      result = CURLE_OK;
-      if(unsupported) {
-        failf(data, "TLSv1.2-only ciphers are not yet supported");
-        result = CURLE_SSL_CIPHER;
-      }
-    }
-  }
-
-  /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to
-     the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */
-  if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) {
-    result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS,
-                        ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE);
-    if(result == CURLE_UNSUPPORTED_PROTOCOL) {
-      result = CURLE_OK;
-      strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr,
-             ciphers[CURL_GSKPROTO_TLSV10].ptr);
-    }
-  }
-
-  /* Set-up other ciphers. */
-  if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK))
-    result = set_buffer(data, h, GSK_V3_CIPHER_SPECS,
-                        ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE);
-  if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK))
-    result = set_buffer(data, h, GSK_V2_CIPHER_SPECS,
-                        ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE);
-
-  /* Clean-up. */
-  for(i = 0; i < CURL_GSKPROTO_LAST; i++)
-    free(ciphers[i].buf);
-
-  return result;
-}
-
-
-static int gskit_init(void)
-{
-  /* No initialization needed. */
-  return 1;
-}
-
-
-static void gskit_cleanup(void)
-{
-  /* Nothing to do. */
-}
-
-
-static CURLcode init_environment(struct Curl_easy *data,
-                                 gsk_handle *envir, const char *appid,
-                                 const char *file, const char *label,
-                                 const char *password)
-{
-  int rc;
-  CURLcode result;
-  gsk_handle h;
-
-  /* Creates the GSKit environment. */
-
-  rc = gsk_environment_open(&h);
-  switch(rc) {
-  case GSK_OK:
-    break;
-  case GSK_INSUFFICIENT_STORAGE:
-    return CURLE_OUT_OF_MEMORY;
-  default:
-    failf(data, "gsk_environment_open(): %s", gsk_strerror(rc));
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE);
-  if(!result && appid)
-    result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE);
-  if(!result && file)
-    result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE);
-  if(!result && label)
-    result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE);
-  if(!result && password)
-    result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE);
-
-  if(!result) {
-    /* Locate CAs, Client certificate and key according to our settings.
-       Note: this call may be blocking for some tenths of seconds. */
-    result = gskit_status(data, gsk_environment_init(h),
-                          "gsk_environment_init()", CURLE_SSL_CERTPROBLEM);
-    if(!result) {
-      *envir = h;
-      return result;
-    }
-  }
-  /* Error: rollback. */
-  gsk_environment_close(&h);
-  return result;
-}
-
-
-static void cancel_async_handshake(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  Qso_OverlappedIO_t cstat;
-
-  (void)data;
-  DEBUGASSERT(BACKEND);
-
-  if(QsoCancelOperation(Curl_conn_cf_get_socket(cf, data), 0) > 0)
-    QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL);
-}
-
-
-static void close_async_handshake(struct ssl_connect_data *connssl)
-{
-  DEBUGASSERT(BACKEND);
-  QsoDestroyIOCompletionPort(BACKEND->iocport);
-  BACKEND->iocport = -1;
-}
-
-static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data,
-                           int directions)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
-  struct ssl_connect_data *connssl_next = cf_ssl_next?
-                                            cf_ssl_next->ctx : NULL;
-  struct pollfd fds[2];
-  int n;
-  int m;
-  int i;
-  int ret = 0;
-  char buf[CURL_MAX_WRITE_SIZE];
-
-  DEBUGASSERT(BACKEND);
-
-  if(!connssl_next)
-    return 0;   /* No SSL over SSL: OK. */
-
-  DEBUGASSERT(connssl_next->backend);
-  n = 1;
-  fds[0].fd = BACKEND->remotefd;
-  fds[1].fd = Curl_conn_cf_get_socket(cf, data);
-
-  if(directions & SOS_READ) {
-    fds[0].events |= POLLOUT;
-  }
-  if(directions & SOS_WRITE) {
-    n = 2;
-    fds[0].events |= POLLIN;
-    fds[1].events |= POLLOUT;
-  }
-  i = Curl_poll(fds, n, 0);
-  if(i < 0)
-    return -1;  /* Select error. */
-
-  if(fds[0].revents & POLLOUT) {
-    /* Try getting data from HTTPS proxy and pipe it upstream. */
-    n = 0;
-    i = gsk_secure_soc_read(connssl_next->backend->handle,
-                            buf, sizeof(buf), &n);
-    switch(i) {
-    case GSK_OK:
-      if(n) {
-        i = write(BACKEND->remotefd, buf, n);
-        if(i < 0)
-          return -1;
-        ret = 1;
-      }
-      break;
-    case GSK_OS400_ERROR_TIMED_OUT:
-    case GSK_WOULD_BLOCK:
-      break;
-    default:
-      return -1;
-    }
-  }
-
-  if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) {
-    /* Pipe data to HTTPS proxy. */
-    n = read(BACKEND->remotefd, buf, sizeof(buf));
-    if(n < 0)
-      return -1;
-    if(n) {
-      i = gsk_secure_soc_write(connssl_next->backend->handle, buf, n, &m);
-      if(i != GSK_OK || n != m)
-        return -1;
-      ret = 1;
-    }
-  }
-
-  return ret;  /* OK */
-}
-
-
-static void close_one(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-
-  DEBUGASSERT(BACKEND);
-  if(BACKEND->handle) {
-    gskit_status(data, gsk_secure_soc_close(&BACKEND->handle),
-              "gsk_secure_soc_close()", 0);
-    /* Last chance to drain output. */
-    while(pipe_ssloverssl(cf, data, SOS_WRITE) > 0)
-      ;
-    BACKEND->handle = (gsk_handle) NULL;
-    if(BACKEND->localfd >= 0) {
-      close(BACKEND->localfd);
-      BACKEND->localfd = -1;
-    }
-    if(BACKEND->remotefd >= 0) {
-      close(BACKEND->remotefd);
-      BACKEND->remotefd = -1;
-    }
-  }
-  if(BACKEND->iocport >= 0)
-    close_async_handshake(connssl);
-}
-
-
-static ssize_t gskit_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          const void *mem, size_t len, CURLcode *curlcode)
-{
-  struct connectdata *conn = cf->conn;
-  struct ssl_connect_data *connssl = cf->ctx;
-  CURLcode cc = CURLE_SEND_ERROR;
-  int written;
-
-  DEBUGASSERT(BACKEND);
-
-  if(pipe_ssloverssl(cf, data, SOS_WRITE) >= 0) {
-    cc = gskit_status(data,
-                      gsk_secure_soc_write(BACKEND->handle,
-                                           (char *) mem, (int) len, &written),
-                      "gsk_secure_soc_write()", CURLE_SEND_ERROR);
-    if(cc == CURLE_OK)
-      if(pipe_ssloverssl(cf, data, SOS_WRITE) < 0)
-        cc = CURLE_SEND_ERROR;
-  }
-  if(cc != CURLE_OK) {
-    *curlcode = cc;
-    written = -1;
-  }
-  return (ssize_t) written; /* number of bytes */
-}
-
-
-static ssize_t gskit_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          char *buf, size_t buffersize, CURLcode *curlcode)
-{
-  struct connectdata *conn = cf->conn;
-  struct ssl_connect_data *connssl = cf->ctx;
-  int nread;
-  CURLcode cc = CURLE_RECV_ERROR;
-
-  (void)data;
-  DEBUGASSERT(BACKEND);
-
-  if(pipe_ssloverssl(cf, data, SOS_READ) >= 0) {
-    int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize;
-    cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle,
-                                                buf, buffsize, &nread),
-                      "gsk_secure_soc_read()", CURLE_RECV_ERROR);
-  }
-  switch(cc) {
-  case CURLE_OK:
-    break;
-  case CURLE_OPERATION_TIMEDOUT:
-    cc = CURLE_AGAIN;
-  default:
-    *curlcode = cc;
-    nread = -1;
-    break;
-  }
-  return (ssize_t) nread;
-}
-
-static CURLcode
-set_ssl_version_min_max(unsigned int *protoflags,
-                        struct Curl_cfilter *cf,
-                        struct Curl_easy *data)
-{
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct connectdata *conn = data->conn;
-  long ssl_version = conn_config->version;
-  long ssl_version_max = conn_config->version_max;
-  long i = ssl_version;
-  switch(ssl_version_max) {
-    case CURL_SSLVERSION_MAX_NONE:
-    case CURL_SSLVERSION_MAX_DEFAULT:
-      ssl_version_max = CURL_SSLVERSION_TLSv1_2;
-      break;
-  }
-  for(; i <= (ssl_version_max >> 16); ++i) {
-    switch(i) {
-      case CURL_SSLVERSION_TLSv1_0:
-        *protoflags |= CURL_GSKPROTO_TLSV10_MASK;
-        break;
-      case CURL_SSLVERSION_TLSv1_1:
-        *protoflags |= CURL_GSKPROTO_TLSV11_MASK;
-        break;
-      case CURL_SSLVERSION_TLSv1_2:
-        *protoflags |= CURL_GSKPROTO_TLSV11_MASK;
-        break;
-      case CURL_SSLVERSION_TLSv1_3:
-        failf(data, "GSKit: TLS 1.3 is not yet supported");
-        return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
-  return CURLE_OK;
-}
-
-static CURLcode gskit_connect_step1(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
-  struct ssl_connect_data *connssl_next = cf_ssl_next?
-                                            cf_ssl_next->ctx : NULL;
-  gsk_handle envir;
-  CURLcode result;
-  const char * const keyringfile = conn_config->CAfile;
-  const char * const keyringpwd = ssl_config->key_passwd;
-  const char * const keyringlabel = ssl_config->primary.clientcert;
-  const long int ssl_version = conn_config->version;
-  const bool verifypeer = conn_config->verifypeer;
-  const char *hostname = connssl->hostname;
-  const char *sni;
-  unsigned int protoflags = 0;
-  Qso_OverlappedIO_t commarea;
-  int sockpair[2];
-  static const int sobufsize = CURL_MAX_WRITE_SIZE;
-
-  /* Create SSL environment, start (preferably asynchronous) handshake. */
-  DEBUGASSERT(BACKEND);
-
-  BACKEND->handle = (gsk_handle) NULL;
-  BACKEND->iocport = -1;
-  BACKEND->localfd = -1;
-  BACKEND->remotefd = -1;
-
-  /* GSKit supports two ways of specifying an SSL context: either by
-   *  application identifier (that should have been defined at the system
-   *  level) or by keyring file, password and certificate label.
-   * Local certificate name (CURLOPT_SSLCERT) is used to hold either the
-   *  application identifier of the certificate label.
-   * Key password (CURLOPT_KEYPASSWD) holds the keyring password.
-   * It is not possible to have different keyrings for the CAs and the
-   *  local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify
-   *  the keyring file.
-   * If no key password is given and the keyring is the system keyring,
-   *  application identifier mode is tried first, as recommended in IBM doc.
-   */
-
-  envir = (gsk_handle) NULL;
-
-  if(keyringlabel && *keyringlabel && !keyringpwd &&
-      !strcmp(keyringfile, CURL_CA_BUNDLE)) {
-    /* Try application identifier mode. */
-    init_environment(data, &envir, keyringlabel, (const char *) NULL,
-                     (const char *) NULL, (const char *) NULL);
-  }
-
-  if(!envir) {
-    /* Use keyring mode. */
-    result = init_environment(data, &envir, (const char *) NULL,
-                              keyringfile, keyringlabel, keyringpwd);
-    if(result)
-      return result;
-  }
-
-  /* Create secure session. */
-  result = gskit_status(data, gsk_secure_soc_open(envir, &BACKEND->handle),
-                        "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR);
-  gsk_environment_close(&envir);
-  if(result)
-    return result;
-
-  /* Establish a pipelining socket pair for SSL over SSL. */
-  if(connssl_next) {
-    if(Curl_socketpair(0, 0, 0, sockpair))
-      return CURLE_SSL_CONNECT_ERROR;
-    BACKEND->localfd = sockpair[0];
-    BACKEND->remotefd = sockpair[1];
-    setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF,
-               (void *) &sobufsize, sizeof(sobufsize));
-    setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF,
-               (void *) &sobufsize, sizeof(sobufsize));
-    setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF,
-               (void *) &sobufsize, sizeof(sobufsize));
-    setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF,
-               (void *) &sobufsize, sizeof(sobufsize));
-    curlx_nonblock(BACKEND->localfd, TRUE);
-    curlx_nonblock(BACKEND->remotefd, TRUE);
-  }
-
-  /* Determine which SSL/TLS version should be enabled. */
-  sni = hostname;
-  switch(ssl_version) {
-  case CURL_SSLVERSION_SSLv2:
-    protoflags = CURL_GSKPROTO_SSLV2_MASK;
-    sni = NULL;
-    break;
-  case CURL_SSLVERSION_SSLv3:
-    protoflags = CURL_GSKPROTO_SSLV3_MASK;
-    sni = NULL;
-    break;
-  case CURL_SSLVERSION_DEFAULT:
-  case CURL_SSLVERSION_TLSv1:
-    protoflags = CURL_GSKPROTO_TLSV10_MASK |
-                 CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK;
-    break;
-  case CURL_SSLVERSION_TLSv1_0:
-  case CURL_SSLVERSION_TLSv1_1:
-  case CURL_SSLVERSION_TLSv1_2:
-  case CURL_SSLVERSION_TLSv1_3:
-    result = set_ssl_version_min_max(&protoflags, cf, data);
-    if(result != CURLE_OK)
-      return result;
-    break;
-  default:
-    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  /* Process SNI. Ignore if not supported (on OS400 < V7R1). */
-  if(sni) {
-    char *snihost = Curl_ssl_snihost(data, sni, NULL);
-    if(!snihost) {
-      failf(data, "Failed to set SNI");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    result = set_buffer(data, BACKEND->handle,
-                        GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE);
-    if(result == CURLE_UNSUPPORTED_PROTOCOL)
-      result = CURLE_OK;
-  }
-
-  /* Set session parameters. */
-  if(!result) {
-    /* Compute the handshake timeout. Since GSKit granularity is 1 second,
-       we round up the required value. */
-    timediff_t timeout = Curl_timeleft(data, NULL, TRUE);
-    if(timeout < 0)
-      result = CURLE_OPERATION_TIMEDOUT;
-    else
-      result = set_numeric(data, BACKEND->handle, GSK_HANDSHAKE_TIMEOUT,
-                           (timeout + 999) / 1000);
-  }
-  if(!result)
-    result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1);
-  if(!result)
-    result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0?
-                         BACKEND->localfd: Curl_conn_cf_get_socket(cf, data));
-  if(!result)
-    result = set_ciphers(cf, data, BACKEND->handle, &protoflags);
-  if(!protoflags) {
-    failf(data, "No SSL protocol/cipher combination enabled");
-    result = CURLE_SSL_CIPHER;
-  }
-  if(!result)
-    result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV2,
-                      (protoflags & CURL_GSKPROTO_SSLV2_MASK)?
-                      GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE);
-  if(!result)
-    result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV3,
-                      (protoflags & CURL_GSKPROTO_SSLV3_MASK)?
-                      GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE);
-  if(!result)
-    result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV1,
-                      (protoflags & CURL_GSKPROTO_TLSV10_MASK)?
-                      GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE);
-  if(!result) {
-    result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV11,
-                      (protoflags & CURL_GSKPROTO_TLSV11_MASK)?
-                      GSK_TRUE: GSK_FALSE, TRUE);
-    if(result == CURLE_UNSUPPORTED_PROTOCOL) {
-      result = CURLE_OK;
-      if(protoflags == CURL_GSKPROTO_TLSV11_MASK) {
-        failf(data, "TLS 1.1 not yet supported");
-        result = CURLE_SSL_CIPHER;
-      }
-    }
-  }
-  if(!result) {
-    result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV12,
-                      (protoflags & CURL_GSKPROTO_TLSV12_MASK)?
-                      GSK_TRUE: GSK_FALSE, TRUE);
-    if(result == CURLE_UNSUPPORTED_PROTOCOL) {
-      result = CURLE_OK;
-      if(protoflags == CURL_GSKPROTO_TLSV12_MASK) {
-        failf(data, "TLS 1.2 not yet supported");
-        result = CURLE_SSL_CIPHER;
-      }
-    }
-  }
-  if(!result)
-    result = set_enum(data, BACKEND->handle, GSK_SERVER_AUTH_TYPE,
-                      verifypeer? GSK_SERVER_AUTH_FULL:
-                      GSK_SERVER_AUTH_PASSTHRU, FALSE);
-
-  if(!result) {
-    /* Start handshake. Try asynchronous first. */
-    memset(&commarea, 0, sizeof(commarea));
-    BACKEND->iocport = QsoCreateIOCompletionPort();
-    if(BACKEND->iocport != -1) {
-      result = gskit_status(data,
-                            gsk_secure_soc_startInit(BACKEND->handle,
-                                                     BACKEND->iocport,
-                                                     &commarea),
-                            "gsk_secure_soc_startInit()",
-                            CURLE_SSL_CONNECT_ERROR);
-      if(!result) {
-        connssl->connecting_state = ssl_connect_2;
-        return CURLE_OK;
-      }
-      else
-        close_async_handshake(connssl);
-    }
-    else if(errno != ENOBUFS)
-      result = gskit_status(data, GSK_ERROR_IO,
-                            "QsoCreateIOCompletionPort()", 0);
-    else if(connssl_next) {
-      /* Cannot pipeline while handshaking synchronously. */
-      result = CURLE_SSL_CONNECT_ERROR;
-    }
-    else {
-      /* No more completion port available. Use synchronous IO. */
-      result = gskit_status(data, gsk_secure_soc_init(BACKEND->handle),
-                            "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR);
-      if(!result) {
-        connssl->connecting_state = ssl_connect_3;
-        return CURLE_OK;
-      }
-    }
-  }
-
-  /* Error: rollback. */
-  close_one(cf, data);
-  return result;
-}
-
-
-static CURLcode gskit_connect_step2(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    bool nonblocking)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  Qso_OverlappedIO_t cstat;
-  struct timeval stmv;
-  CURLcode result;
-
-  /* Poll or wait for end of SSL asynchronous handshake. */
-  DEBUGASSERT(BACKEND);
-
-  for(;;) {
-    timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE);
-    stmv.tv_sec = 0;
-    stmv.tv_usec = 0;
-    if(timeout_ms < 0)
-      timeout_ms = 0;
-    switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat,
-                                  curlx_mstotv(&stmv, timeout_ms))) {
-    case 1:             /* Operation complete. */
-      break;
-    case -1:            /* An error occurred: handshake still in progress. */
-      if(errno == EINTR) {
-        if(nonblocking)
-          return CURLE_OK;
-        continue;       /* Retry. */
-      }
-      if(errno != ETIME) {
-        char buffer[STRERROR_LEN];
-        failf(data, "QsoWaitForIOCompletion() I/O error: %s",
-              Curl_strerror(errno, buffer, sizeof(buffer)));
-        cancel_async_handshake(cf, data);
-        close_async_handshake(connssl);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      /* FALL INTO... */
-    case 0:             /* Handshake in progress, timeout occurred. */
-      if(nonblocking)
-        return CURLE_OK;
-      cancel_async_handshake(cf, data);
-      close_async_handshake(connssl);
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    break;
-  }
-  result = gskit_status(data, cstat.returnValue, "SSL handshake",
-                        CURLE_SSL_CONNECT_ERROR);
-  if(!result)
-    connssl->connecting_state = ssl_connect_3;
-  close_async_handshake(connssl);
-  return result;
-}
-
-
-static CURLcode gskit_connect_step3(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  const gsk_cert_data_elem *cdev;
-  int cdec;
-  const gsk_cert_data_elem *p;
-  const char *cert = (const char *) NULL;
-  const char *certend = (const char *) NULL;
-  const char *ptr;
-  CURLcode result;
-
-  /* SSL handshake done: gather certificate info and verify host. */
-  DEBUGASSERT(BACKEND);
-
-  if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle,
-                                                    GSK_PARTNER_CERT_INFO,
-                                                    &cdev, &cdec),
-                  "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) ==
-     CURLE_OK) {
-    int i;
-
-    infof(data, "Server certificate:");
-    p = cdev;
-    for(i = 0; i++ < cdec; p++)
-      switch(p->cert_data_id) {
-      case CERT_BODY_DER:
-        cert = p->cert_data_p;
-        certend = cert + cdev->cert_data_l;
-        break;
-      case CERT_DN_PRINTABLE:
-        infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p);
-        break;
-      case CERT_ISSUER_DN_PRINTABLE:
-        infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p);
-        break;
-      case CERT_VALID_FROM:
-        infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p);
-        break;
-      case CERT_VALID_TO:
-        infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p);
-        break;
-    }
-  }
-
-  /* Verify host. */
-  result = Curl_verifyhost(cf, data, cert, certend);
-  if(result)
-    return result;
-
-  /* The only place GSKit can get the whole CA chain is a validation
-     callback where no user data pointer is available. Therefore it's not
-     possible to copy this chain into our structures for CAINFO.
-     However the server certificate may be available, thus we can return
-     info about it. */
-  if(data->set.ssl.certinfo) {
-    result = Curl_ssl_init_certinfo(data, 1);
-    if(result)
-      return result;
-
-    if(cert) {
-      result = Curl_extract_certinfo(data, 0, cert, certend);
-      if(result)
-        return result;
-    }
-  }
-
-  /* Check pinned public key. */
-  ptr = Curl_ssl_cf_is_proxy(cf)?
-    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
-    data->set.str[STRING_SSL_PINNEDPUBLICKEY];
-  if(!result && ptr) {
-    struct Curl_X509certificate x509;
-    struct Curl_asn1Element *p;
-
-    memset(&x509, 0, sizeof(x509));
-    if(Curl_parseX509(&x509, cert, certend))
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-    p = &x509.subjectPublicKeyInfo;
-    result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header);
-    if(result) {
-      failf(data, "SSL: public key does not match pinned public key");
-      return result;
-    }
-  }
-
-  connssl->connecting_state = ssl_connect_done;
-  return CURLE_OK;
-}
-
-
-static CURLcode gskit_connect_common(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data,
-                                     bool nonblocking, bool *done)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  timediff_t timeout_ms;
-  CURLcode result = CURLE_OK;
-
-  *done = connssl->state == ssl_connection_complete;
-  if(*done)
-    return CURLE_OK;
-
-  /* Step 1: create session, start handshake. */
-  if(connssl->connecting_state == ssl_connect_1) {
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      result = CURLE_OPERATION_TIMEDOUT;
-    }
-    else
-      result = gskit_connect_step1(cf, data);
-  }
-
-  /* Handle handshake pipelining. */
-  if(!result)
-    if(pipe_ssloverssl(cf, data, SOS_READ | SOS_WRITE) < 0)
-      result = CURLE_SSL_CONNECT_ERROR;
-
-  /* Step 2: check if handshake is over. */
-  if(!result && connssl->connecting_state == ssl_connect_2) {
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      result = CURLE_OPERATION_TIMEDOUT;
-    }
-    else
-      result = gskit_connect_step2(cf, data, nonblocking);
-  }
-
-  /* Handle handshake pipelining. */
-  if(!result)
-    if(pipe_ssloverssl(cf, data, SOS_READ | SOS_WRITE) < 0)
-      result = CURLE_SSL_CONNECT_ERROR;
-
-  /* Step 3: gather certificate info, verify host. */
-  if(!result && connssl->connecting_state == ssl_connect_3)
-    result = gskit_connect_step3(cf, data);
-
-  if(result)
-    close_one(cf, data);
-  else if(connssl->connecting_state == ssl_connect_done) {
-    connssl->state = ssl_connection_complete;
-    connssl->connecting_state = ssl_connect_1;
-    *done = TRUE;
-  }
-
-  return result;
-}
-
-
-static CURLcode gskit_connect_nonblocking(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data,
-                                          bool *done)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  CURLcode result;
-
-  result = gskit_connect_common(cf, data, TRUE, done);
-  if(*done || result)
-    connssl->connecting_state = ssl_connect_1;
-  return result;
-}
-
-
-static CURLcode gskit_connect(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  CURLcode result;
-  bool done;
-
-  connssl->connecting_state = ssl_connect_1;
-  result = gskit_connect_common(cf, data, FALSE, &done);
-  if(result)
-    return result;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-
-static void gskit_close(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  close_one(cf, data);
-}
-
-
-static int gskit_shutdown(struct Curl_cfilter *cf,
-                          struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  int what;
-  int rc;
-  char buf[120];
-  int loop = 10; /* don't get stuck */
-
-  DEBUGASSERT(BACKEND);
-
-  if(!BACKEND->handle)
-    return 0;
-
-#ifndef CURL_DISABLE_FTP
-  if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
-    return 0;
-#endif
-
-  close_one(cf, data);
-  rc = 0;
-  what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
-                         SSL_SHUTDOWN_TIMEOUT);
-
-  while(loop--) {
-    ssize_t nread;
-
-    if(what < 0) {
-      /* anything that gets here is fatally bad */
-      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-      rc = -1;
-      break;
-    }
-
-    if(!what) {                                /* timeout */
-      failf(data, "SSL shutdown timeout");
-      break;
-    }
-
-    /* Something to read, let's do it and hope that it is the close
-       notify alert from the server. No way to gsk_secure_soc_read() now, so
-       use read(). */
-
-    nread = read(Curl_conn_cf_get_socket(cf, data), buf, sizeof(buf));
-
-    if(nread < 0) {
-      char buffer[STRERROR_LEN];
-      failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer)));
-      rc = -1;
-    }
-
-    if(nread <= 0)
-      break;
-
-    what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), 0);
-  }
-
-  return rc;
-}
-
-
-static size_t gskit_version(char *buffer, size_t size)
-{
-  return msnprintf(buffer, size, "GSKit");
-}
-
-
-static int gskit_check_cxn(struct Curl_cfilter *cf,
-                           struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  int err;
-  int errlen;
-
-  (void)data;
-  /* The only thing that can be tested here is at the socket level. */
-  DEBUGASSERT(BACKEND);
-
-  if(!BACKEND->handle)
-    return 0; /* connection has been closed */
-
-  err = 0;
-  errlen = sizeof(err);
-
-  if(getsockopt(Curl_conn_cf_get_socket(cf, data), SOL_SOCKET, SO_ERROR,
-                 (unsigned char *) &err, &errlen) ||
-     errlen != sizeof(err) || err)
-    return 0; /* connection has been closed */
-
-  return -1;  /* connection status unknown */
-}
-
-static void *gskit_get_internals(struct ssl_connect_data *connssl,
-                                 CURLINFO info UNUSED_PARAM)
-{
-  (void)info;
-  DEBUGASSERT(BACKEND);
-  return BACKEND->handle;
-}
-
-const struct Curl_ssl Curl_ssl_gskit = {
-  { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */
-
-  SSLSUPP_CERTINFO |
-  SSLSUPP_PINNEDPUBKEY,
-
-  sizeof(struct ssl_backend_data),
-
-  gskit_init,                     /* init */
-  gskit_cleanup,                  /* cleanup */
-  gskit_version,                  /* version */
-  gskit_check_cxn,                /* check_cxn */
-  gskit_shutdown,                 /* shutdown */
-  Curl_none_data_pending,         /* data_pending */
-  Curl_none_random,               /* random */
-  Curl_none_cert_status_request,  /* cert_status_request */
-  gskit_connect,                  /* connect */
-  gskit_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,               /* getsock */
-  gskit_get_internals,            /* get_internals */
-  gskit_close,                    /* close_one */
-  Curl_none_close_all,            /* close_all */
-  /* No session handling for GSKit */
-  Curl_none_session_free,         /* session_free */
-  Curl_none_set_engine,           /* set_engine */
-  Curl_none_set_engine_default,   /* set_engine_default */
-  Curl_none_engines_list,         /* engines_list */
-  Curl_none_false_start,          /* false_start */
-  NULL,                           /* sha256sum */
-  NULL,                           /* associate_connection */
-  NULL,                           /* disassociate_connection */
-  NULL,                           /* free_multi_ssl_backend_data */
-  gskit_recv,                     /* recv decrypted data */
-  gskit_send,                     /* send data to encrypt */
-};
-
-#endif /* USE_GSKIT */
diff --git a/Utilities/cmcurl/lib/vtls/gskit.h b/Utilities/cmcurl/lib/vtls/gskit.h
deleted file mode 100644
index c71e6a0..0000000
--- a/Utilities/cmcurl/lib/vtls/gskit.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef HEADER_CURL_GSKIT_H
-#define HEADER_CURL_GSKIT_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-/*
- * This header should only be needed to get included by vtls.c and gskit.c
- */
-
-#include "urldata.h"
-
-#ifdef USE_GSKIT
-
-extern const struct Curl_ssl Curl_ssl_gskit;
-
-#endif /* USE_GSKIT */
-
-#endif /* HEADER_CURL_GSKIT_H */
diff --git a/Utilities/cmcurl/lib/vtls/gtls.c b/Utilities/cmcurl/lib/vtls/gtls.c
index 3d1906e..c538a96 100644
--- a/Utilities/cmcurl/lib/vtls/gtls.c
+++ b/Utilities/cmcurl/lib/vtls/gtls.c
@@ -76,7 +76,7 @@
 
 # include <gnutls/ocsp.h>
 
-struct ssl_backend_data {
+struct gtls_ssl_backend_data {
   struct gtls_instance gtls;
 };
 
@@ -91,7 +91,9 @@
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
   if(nwritten < 0) {
-    gnutls_transport_set_errno(connssl->backend->gtls.session,
+    struct gtls_ssl_backend_data *backend =
+      (struct gtls_ssl_backend_data *)connssl->backend;
+    gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nwritten = -1;
   }
@@ -109,7 +111,9 @@
   DEBUGASSERT(data);
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
   if(nread < 0) {
-    gnutls_transport_set_errno(connssl->backend->gtls.session,
+    struct gtls_ssl_backend_data *backend =
+      (struct gtls_ssl_backend_data *)connssl->backend;
+    gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nread = -1;
   }
@@ -212,7 +216,8 @@
                           bool nonblocking)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   gnutls_session_t session;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
 
@@ -679,7 +684,8 @@
 gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   long * const pverifyresult = &ssl_config->certverifyresult;
@@ -729,7 +735,7 @@
                               ssl_sessionid, ssl_idsize);
 
       /* Informational message */
-      infof(data, "SSL re-using session ID");
+      infof(data, "SSL reusing session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -1346,7 +1352,8 @@
 
   /* Finish connecting once the handshake is done */
   if(ssl_connect_1 == connssl->connecting_state) {
-    struct ssl_backend_data *backend = connssl->backend;
+    struct gtls_ssl_backend_data *backend =
+      (struct gtls_ssl_backend_data *)connssl->backend;
     gnutls_session_t session;
     DEBUGASSERT(backend);
     session = backend->gtls.session;
@@ -1390,11 +1397,13 @@
                               const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct gtls_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->gtls.session &&
-     0 != gnutls_record_check_pending(ctx->backend->gtls.session))
+  backend = (struct gtls_ssl_backend_data *)ctx->backend;
+  if(backend->gtls.session &&
+     0 != gnutls_record_check_pending(backend->gtls.session))
     return TRUE;
   return FALSE;
 }
@@ -1406,7 +1415,8 @@
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   ssize_t rc;
 
   (void)data;
@@ -1428,7 +1438,8 @@
                        struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
 
   (void) data;
   DEBUGASSERT(backend);
@@ -1462,8 +1473,8 @@
                          struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   int retval = 0;
 
   DEBUGASSERT(backend);
@@ -1524,8 +1535,11 @@
   gnutls_certificate_free_credentials(backend->gtls.cred);
 
 #ifdef USE_GNUTLS_SRP
-  if(ssl_config->primary.username)
-    gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
+  {
+    struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+    if(ssl_config->primary.username)
+      gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
+  }
 #endif
 
   backend->gtls.cred = NULL;
@@ -1541,7 +1555,8 @@
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   ssize_t ret;
 
   (void)data;
@@ -1620,7 +1635,8 @@
 static void *gtls_get_internals(struct ssl_connect_data *connssl,
                                 CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->gtls.session;
@@ -1634,7 +1650,7 @@
   SSLSUPP_PINNEDPUBKEY |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct gtls_ssl_backend_data),
 
   gtls_init,                     /* init */
   gtls_cleanup,                  /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/hostcheck.c b/Utilities/cmcurl/lib/vtls/hostcheck.c
index d061c63..2726dca 100644
--- a/Utilities/cmcurl/lib/vtls/hostcheck.c
+++ b/Utilities/cmcurl/lib/vtls/hostcheck.c
@@ -24,8 +24,7 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_OPENSSL)                                \
-  || defined(USE_GSKIT)                                 \
+#if defined(USE_OPENSSL)                        \
   || defined(USE_SCHANNEL)
 /* these backends use functions from this file */
 
@@ -133,4 +132,4 @@
   return FALSE;
 }
 
-#endif /* OPENSSL, GSKIT or schannel+wince */
+#endif /* OPENSSL or SCHANNEL */
diff --git a/Utilities/cmcurl/lib/vtls/mbedtls.c b/Utilities/cmcurl/lib/vtls/mbedtls.c
index d95888c..2f994d7 100644
--- a/Utilities/cmcurl/lib/vtls/mbedtls.c
+++ b/Utilities/cmcurl/lib/vtls/mbedtls.c
@@ -81,7 +81,7 @@
 #  endif
 #endif
 
-struct ssl_backend_data {
+struct mbed_ssl_backend_data {
   mbedtls_ctr_drbg_context ctr_drbg;
   mbedtls_entropy_context entropy;
   mbedtls_ssl_context ssl;
@@ -156,7 +156,8 @@
 #else
 #endif
 
-static int bio_cf_write(void *bio, const unsigned char *buf, size_t blen)
+static int mbedtls_bio_cf_write(void *bio,
+                                const unsigned char *buf, size_t blen)
 {
   struct Curl_cfilter *cf = bio;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
@@ -165,15 +166,15 @@
 
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, &result);
-  DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%zu) -> %zd, err=%d",
-                blen, nwritten, result));
+  CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
+              blen, nwritten, result);
   if(nwritten < 0 && CURLE_AGAIN == result) {
     nwritten = MBEDTLS_ERR_SSL_WANT_WRITE;
   }
   return (int)nwritten;
 }
 
-static int bio_cf_read(void *bio, unsigned char *buf, size_t blen)
+static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen)
 {
   struct Curl_cfilter *cf = bio;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
@@ -186,8 +187,8 @@
     return 0;
 
   nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result);
-  DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%zu) -> %zd, err=%d",
-                blen, nread, result));
+  CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %zd, err=%d",
+              blen, nread, result);
   if(nread < 0 && CURLE_AGAIN == result) {
     nread = MBEDTLS_ERR_SSL_WANT_READ;
   }
@@ -255,7 +256,8 @@
 set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
 #if MBEDTLS_VERSION_NUMBER >= 0x03000000
   int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3;
@@ -307,7 +309,8 @@
 mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -589,7 +592,9 @@
 
   mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random,
                        &backend->ctr_drbg);
-  mbedtls_ssl_set_bio(&backend->ssl, cf, bio_cf_write, bio_cf_read,
+  mbedtls_ssl_set_bio(&backend->ssl, cf,
+                      mbedtls_bio_cf_write,
+                      mbedtls_bio_cf_read,
                       NULL /*  rev_timeout() */);
 
   mbedtls_ssl_conf_ciphersuites(&backend->config,
@@ -617,7 +622,7 @@
         failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret);
         return CURLE_SSL_CONNECT_ERROR;
       }
-      infof(data, "mbedTLS re-using session");
+      infof(data, "mbedTLS reusing session");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -697,7 +702,8 @@
 {
   int ret;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const mbedtls_x509_crt *peercert;
   const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
@@ -860,7 +866,8 @@
 {
   CURLcode retcode = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -915,7 +922,8 @@
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   int ret = -1;
 
   (void)data;
@@ -939,7 +947,8 @@
 static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   char buf[32];
 
   (void)data;
@@ -968,7 +977,8 @@
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   int ret = -1;
   ssize_t len = -1;
 
@@ -1204,10 +1214,12 @@
                                  const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct mbed_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  return mbedtls_ssl_get_bytes_avail(&ctx->backend->ssl) != 0;
+  backend = (struct mbed_ssl_backend_data *)ctx->backend;
+  return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0;
 }
 
 static CURLcode mbedtls_sha256sum(const unsigned char *input,
@@ -1234,7 +1246,8 @@
 static void *mbedtls_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return &backend->ssl;
@@ -1249,7 +1262,7 @@
   SSLSUPP_SSL_CTX |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct mbed_ssl_backend_data),
 
   mbedtls_init,                     /* init */
   mbedtls_cleanup,                  /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/nss.c b/Utilities/cmcurl/lib/vtls/nss.c
deleted file mode 100644
index 5e5dbb7..0000000
--- a/Utilities/cmcurl/lib/vtls/nss.c
+++ /dev/null
@@ -1,2522 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-/*
- * Source file for all NSS-specific code for the TLS/SSL layer. No code
- * but vtls.c should ever call or use these functions.
- */
-
-#include "curl_setup.h"
-
-#ifdef USE_NSS
-
-#include "urldata.h"
-#include "sendf.h"
-#include "formdata.h" /* for the boundary function */
-#include "url.h" /* for the ssl config check function */
-#include "connect.h"
-#include "strcase.h"
-#include "select.h"
-#include "vtls.h"
-#include "vtls_int.h"
-#include "llist.h"
-#include "multiif.h"
-#include "curl_printf.h"
-#include "nssg.h"
-#include <nspr.h>
-#include <nss.h>
-#include <ssl.h>
-#include <sslerr.h>
-#include <secerr.h>
-#include <secmod.h>
-#include <sslproto.h>
-#include <prtypes.h>
-#include <pk11pub.h>
-#include <prio.h>
-#include <secitem.h>
-#include <secport.h>
-#include <certdb.h>
-#include <base64.h>
-#include <cert.h>
-#include <prerror.h>
-#include <keyhi.h>         /* for SECKEY_DestroyPublicKey() */
-#include <private/pprio.h> /* for PR_ImportTCPSocket */
-
-#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH)
-
-#if NSSVERNUM >= 0x030f00 /* 3.15.0 */
-#include <ocsp.h>
-#endif
-
-#include "warnless.h"
-#include "x509asn1.h"
-
-/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define SSL_DIR "/etc/pki/nssdb"
-
-/* enough to fit the string "PEM Token #[0|1]" */
-#define SLOTSIZE 13
-
-struct ssl_backend_data {
-  PRFileDesc *handle;
-  char *client_nickname;
-  struct Curl_easy *data;
-  struct Curl_llist obj_list;
-  PK11GenericObject *obj_clicert;
-};
-
-static PRLock *nss_initlock = NULL;
-static PRLock *nss_crllock = NULL;
-static PRLock *nss_findslot_lock = NULL;
-static PRLock *nss_trustload_lock = NULL;
-static struct Curl_llist nss_crl_list;
-static NSSInitContext *nss_context = NULL;
-static volatile int initialized = 0;
-
-/* type used to wrap pointers as list nodes */
-struct ptr_list_wrap {
-  void *ptr;
-  struct Curl_llist_element node;
-};
-
-struct cipher_s {
-  const char *name;
-  int num;
-};
-
-#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do {  \
-  CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++);                 \
-  ptr->type = (_type);                                      \
-  ptr->pValue = (_val);                                     \
-  ptr->ulValueLen = (_len);                                 \
-} while(0)
-
-#define CERT_NewTempCertificate __CERT_NewTempCertificate
-
-#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0])
-static const struct cipher_s cipherlist[] = {
-  /* SSL2 cipher suites */
-  {"rc4",                        SSL_EN_RC4_128_WITH_MD5},
-  {"rc4-md5",                    SSL_EN_RC4_128_WITH_MD5},
-  {"rc4export",                  SSL_EN_RC4_128_EXPORT40_WITH_MD5},
-  {"rc2",                        SSL_EN_RC2_128_CBC_WITH_MD5},
-  {"rc2export",                  SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5},
-  {"des",                        SSL_EN_DES_64_CBC_WITH_MD5},
-  {"desede3",                    SSL_EN_DES_192_EDE3_CBC_WITH_MD5},
-  /* SSL3/TLS cipher suites */
-  {"rsa_rc4_128_md5",            SSL_RSA_WITH_RC4_128_MD5},
-  {"rsa_rc4_128_sha",            SSL_RSA_WITH_RC4_128_SHA},
-  {"rsa_3des_sha",               SSL_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"rsa_des_sha",                SSL_RSA_WITH_DES_CBC_SHA},
-  {"rsa_rc4_40_md5",             SSL_RSA_EXPORT_WITH_RC4_40_MD5},
-  {"rsa_rc2_40_md5",             SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5},
-  {"rsa_null_md5",               SSL_RSA_WITH_NULL_MD5},
-  {"rsa_null_sha",               SSL_RSA_WITH_NULL_SHA},
-  {"fips_3des_sha",              SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA},
-  {"fips_des_sha",               SSL_RSA_FIPS_WITH_DES_CBC_SHA},
-  {"fortezza",                   SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
-  {"fortezza_rc4_128_sha",       SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
-  {"fortezza_null",              SSL_FORTEZZA_DMS_WITH_NULL_SHA},
-  {"dhe_rsa_3des_sha",           SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"dhe_dss_3des_sha",           SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA},
-  {"dhe_rsa_des_sha",            SSL_DHE_RSA_WITH_DES_CBC_SHA},
-  {"dhe_dss_des_sha",            SSL_DHE_DSS_WITH_DES_CBC_SHA},
-  /* TLS 1.0: Exportable 56-bit Cipher Suites. */
-  {"rsa_des_56_sha",             TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA},
-  {"rsa_rc4_56_sha",             TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
-  /* Ephemeral DH with RC4 bulk encryption */
-  {"dhe_dss_rc4_128_sha",    TLS_DHE_DSS_WITH_RC4_128_SHA},
-  /* AES ciphers. */
-  {"dhe_dss_aes_128_cbc_sha",    TLS_DHE_DSS_WITH_AES_128_CBC_SHA},
-  {"dhe_dss_aes_256_cbc_sha",    TLS_DHE_DSS_WITH_AES_256_CBC_SHA},
-  {"dhe_rsa_aes_128_cbc_sha",    TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
-  {"dhe_rsa_aes_256_cbc_sha",    TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
-  {"rsa_aes_128_sha",            TLS_RSA_WITH_AES_128_CBC_SHA},
-  {"rsa_aes_256_sha",            TLS_RSA_WITH_AES_256_CBC_SHA},
-  /* ECC ciphers. */
-  {"ecdh_ecdsa_null_sha",        TLS_ECDH_ECDSA_WITH_NULL_SHA},
-  {"ecdh_ecdsa_rc4_128_sha",     TLS_ECDH_ECDSA_WITH_RC4_128_SHA},
-  {"ecdh_ecdsa_3des_sha",        TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdh_ecdsa_aes_128_sha",     TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA},
-  {"ecdh_ecdsa_aes_256_sha",     TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA},
-  {"ecdhe_ecdsa_null_sha",       TLS_ECDHE_ECDSA_WITH_NULL_SHA},
-  {"ecdhe_ecdsa_rc4_128_sha",    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
-  {"ecdhe_ecdsa_3des_sha",       TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdhe_ecdsa_aes_128_sha",    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA},
-  {"ecdhe_ecdsa_aes_256_sha",    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
-  {"ecdh_rsa_null_sha",          TLS_ECDH_RSA_WITH_NULL_SHA},
-  {"ecdh_rsa_128_sha",           TLS_ECDH_RSA_WITH_RC4_128_SHA},
-  {"ecdh_rsa_3des_sha",          TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdh_rsa_aes_128_sha",       TLS_ECDH_RSA_WITH_AES_128_CBC_SHA},
-  {"ecdh_rsa_aes_256_sha",       TLS_ECDH_RSA_WITH_AES_256_CBC_SHA},
-  {"ecdhe_rsa_null",             TLS_ECDHE_RSA_WITH_NULL_SHA},
-  {"ecdhe_rsa_rc4_128_sha",      TLS_ECDHE_RSA_WITH_RC4_128_SHA},
-  {"ecdhe_rsa_3des_sha",         TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA},
-  {"ecdhe_rsa_aes_128_sha",      TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
-  {"ecdhe_rsa_aes_256_sha",      TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
-  {"ecdh_anon_null_sha",         TLS_ECDH_anon_WITH_NULL_SHA},
-  {"ecdh_anon_rc4_128sha",       TLS_ECDH_anon_WITH_RC4_128_SHA},
-  {"ecdh_anon_3des_sha",         TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA},
-  {"ecdh_anon_aes_128_sha",      TLS_ECDH_anon_WITH_AES_128_CBC_SHA},
-  {"ecdh_anon_aes_256_sha",      TLS_ECDH_anon_WITH_AES_256_CBC_SHA},
-#ifdef TLS_RSA_WITH_NULL_SHA256
-  /* new HMAC-SHA256 cipher suites specified in RFC */
-  {"rsa_null_sha_256",                TLS_RSA_WITH_NULL_SHA256},
-  {"rsa_aes_128_cbc_sha_256",         TLS_RSA_WITH_AES_128_CBC_SHA256},
-  {"rsa_aes_256_cbc_sha_256",         TLS_RSA_WITH_AES_256_CBC_SHA256},
-  {"dhe_rsa_aes_128_cbc_sha_256",     TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
-  {"dhe_rsa_aes_256_cbc_sha_256",     TLS_DHE_RSA_WITH_AES_256_CBC_SHA256},
-  {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
-  {"ecdhe_rsa_aes_128_cbc_sha_256",   TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
-#endif
-#ifdef TLS_RSA_WITH_AES_128_GCM_SHA256
-  /* AES GCM cipher suites in RFC 5288 and RFC 5289 */
-  {"rsa_aes_128_gcm_sha_256",         TLS_RSA_WITH_AES_128_GCM_SHA256},
-  {"dhe_rsa_aes_128_gcm_sha_256",     TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
-  {"dhe_dss_aes_128_gcm_sha_256",     TLS_DHE_DSS_WITH_AES_128_GCM_SHA256},
-  {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
-  {"ecdh_ecdsa_aes_128_gcm_sha_256",  TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256},
-  {"ecdhe_rsa_aes_128_gcm_sha_256",   TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
-  {"ecdh_rsa_aes_128_gcm_sha_256",    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256},
-#endif
-#ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
-  /* cipher suites using SHA384 */
-  {"rsa_aes_256_gcm_sha_384",         TLS_RSA_WITH_AES_256_GCM_SHA384},
-  {"dhe_rsa_aes_256_gcm_sha_384",     TLS_DHE_RSA_WITH_AES_256_GCM_SHA384},
-  {"dhe_dss_aes_256_gcm_sha_384",     TLS_DHE_DSS_WITH_AES_256_GCM_SHA384},
-  {"ecdhe_ecdsa_aes_256_sha_384",     TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
-  {"ecdhe_rsa_aes_256_sha_384",       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
-  {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
-  {"ecdhe_rsa_aes_256_gcm_sha_384",   TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
-#endif
-#ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
-  /* chacha20-poly1305 cipher suites */
- {"ecdhe_rsa_chacha20_poly1305_sha_256",
-     TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
- {"ecdhe_ecdsa_chacha20_poly1305_sha_256",
-     TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
- {"dhe_rsa_chacha20_poly1305_sha_256",
-     TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
-#endif
-#ifdef TLS_AES_256_GCM_SHA384
- {"aes_128_gcm_sha_256",              TLS_AES_128_GCM_SHA256},
- {"aes_256_gcm_sha_384",              TLS_AES_256_GCM_SHA384},
- {"chacha20_poly1305_sha_256",        TLS_CHACHA20_POLY1305_SHA256},
-#endif
-#ifdef TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
-  /* AES CBC cipher suites in RFC 5246. Introduced in NSS release 3.20 */
-  {"dhe_dss_aes_128_sha_256",         TLS_DHE_DSS_WITH_AES_128_CBC_SHA256},
-  {"dhe_dss_aes_256_sha_256",         TLS_DHE_DSS_WITH_AES_256_CBC_SHA256},
-#endif
-#ifdef TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
-  /* Camellia cipher suites in RFC 4132/5932.
-     Introduced in NSS release 3.12 */
-  {"dhe_rsa_camellia_128_sha",        TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA},
-  {"dhe_dss_camellia_128_sha",        TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA},
-  {"dhe_rsa_camellia_256_sha",        TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA},
-  {"dhe_dss_camellia_256_sha",        TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA},
-  {"rsa_camellia_128_sha",            TLS_RSA_WITH_CAMELLIA_128_CBC_SHA},
-  {"rsa_camellia_256_sha",            TLS_RSA_WITH_CAMELLIA_256_CBC_SHA},
-#endif
-#ifdef TLS_RSA_WITH_SEED_CBC_SHA
-  /* SEED cipher suite in RFC 4162. Introduced in NSS release 3.12.3 */
-  {"rsa_seed_sha",                    TLS_RSA_WITH_SEED_CBC_SHA},
-#endif
-};
-
-#if defined(WIN32)
-static const char *pem_library = "nsspem.dll";
-static const char *trust_library = "nssckbi.dll";
-#elif defined(__APPLE__)
-static const char *pem_library = "libnsspem.dylib";
-static const char *trust_library = "libnssckbi.dylib";
-#else
-static const char *pem_library = "libnsspem.so";
-static const char *trust_library = "libnssckbi.so";
-#endif
-
-static SECMODModule *pem_module = NULL;
-static SECMODModule *trust_module = NULL;
-
-/* NSPR I/O layer we use to detect blocking direction during SSL handshake */
-static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER;
-static PRIOMethods nspr_io_methods;
-
-static const char *nss_error_to_name(PRErrorCode code)
-{
-  const char *name = PR_ErrorToName(code);
-  if(name)
-    return name;
-
-  return "unknown error";
-}
-
-static void nss_print_error_message(struct Curl_easy *data, PRUint32 err)
-{
-  failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT));
-}
-
-static char *nss_sslver_to_name(PRUint16 nssver)
-{
-  switch(nssver) {
-  case SSL_LIBRARY_VERSION_2:
-    return strdup("SSLv2");
-  case SSL_LIBRARY_VERSION_3_0:
-    return strdup("SSLv3");
-  case SSL_LIBRARY_VERSION_TLS_1_0:
-    return strdup("TLSv1.0");
-#ifdef SSL_LIBRARY_VERSION_TLS_1_1
-  case SSL_LIBRARY_VERSION_TLS_1_1:
-    return strdup("TLSv1.1");
-#endif
-#ifdef SSL_LIBRARY_VERSION_TLS_1_2
-  case SSL_LIBRARY_VERSION_TLS_1_2:
-    return strdup("TLSv1.2");
-#endif
-#ifdef SSL_LIBRARY_VERSION_TLS_1_3
-  case SSL_LIBRARY_VERSION_TLS_1_3:
-    return strdup("TLSv1.3");
-#endif
-  default:
-    return curl_maprintf("0x%04x", nssver);
-  }
-}
-
-/* the longest cipher name this supports */
-#define MAX_CIPHER_LENGTH 128
-
-static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model,
-                             const char *cipher_list)
-{
-  unsigned int i;
-  const char *cipher;
-
-  /* use accessors to avoid dynamic linking issues after an update of NSS */
-  const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers();
-  const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers();
-  if(!implemented_ciphers)
-    return SECFailure;
-
-  /* First disable all ciphers. This uses a different max value in case
-   * NSS adds more ciphers later we don't want them available by
-   * accident
-   */
-  for(i = 0; i < num_implemented_ciphers; i++) {
-    SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE);
-  }
-
-  cipher = cipher_list;
-
-  while(cipher && cipher[0]) {
-    const char *end;
-    char name[MAX_CIPHER_LENGTH + 1];
-    size_t len;
-    bool found = FALSE;
-    while((*cipher) && (ISBLANK(*cipher)))
-      ++cipher;
-
-    end = strpbrk(cipher, ":, ");
-    if(end)
-      len = end - cipher;
-    else
-      len = strlen(cipher);
-
-    if(len > MAX_CIPHER_LENGTH) {
-      failf(data, "Bad cipher list");
-      return SECFailure;
-    }
-    else if(len) {
-      memcpy(name, cipher, len);
-      name[len] = 0;
-
-      for(i = 0; i<NUM_OF_CIPHERS; i++) {
-        if(strcasecompare(name, cipherlist[i].name)) {
-          /* Enable the selected cipher */
-          if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) !=
-             SECSuccess) {
-            failf(data, "cipher-suite not supported by NSS: %s", name);
-            return SECFailure;
-          }
-          found = TRUE;
-          break;
-        }
-      }
-    }
-
-    if(!found && len) {
-      failf(data, "Unknown cipher: %s", name);
-      return SECFailure;
-    }
-    if(end)
-      cipher = ++end;
-    else
-      break;
-  }
-
-  return SECSuccess;
-}
-
-/*
- * Return true if at least one cipher-suite is enabled. Used to determine
- * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers.
- */
-static bool any_cipher_enabled(void)
-{
-  unsigned int i;
-
-  for(i = 0; i<NUM_OF_CIPHERS; i++) {
-    PRInt32 policy = 0;
-    SSL_CipherPolicyGet(cipherlist[i].num, &policy);
-    if(policy)
-      return TRUE;
-  }
-
-  return FALSE;
-}
-
-/*
- * Determine whether the nickname passed in is a filename that needs to
- * be loaded as a PEM or a regular NSS nickname.
- *
- * returns 1 for a file
- * returns 0 for not a file (NSS nickname)
- */
-static int is_file(const char *filename)
-{
-  struct_stat st;
-
-  if(!filename)
-    return 0;
-
-  if(stat(filename, &st) == 0)
-    if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode))
-      return 1;
-
-  return 0;
-}
-
-/* Check if the given string is filename or nickname of a certificate.  If the
- * given string is recognized as filename, return NULL.  If the given string is
- * recognized as nickname, return a duplicated string.  The returned string
- * should be later deallocated using free().  If the OOM failure occurs, we
- * return NULL, too.
- */
-static char *dup_nickname(struct Curl_easy *data, const char *str)
-{
-  const char *n;
-
-  if(!is_file(str))
-    /* no such file exists, use the string as nickname */
-    return strdup(str);
-
-  /* search the first slash; we require at least one slash in a file name */
-  n = strchr(str, '/');
-  if(!n) {
-    infof(data, "WARNING: certificate file name \"%s\" handled as nickname; "
-          "please use \"./%s\" to force file name", str, str);
-    return strdup(str);
-  }
-
-  /* we'll use the PEM reader to read the certificate from file */
-  return NULL;
-}
-
-/* Lock/unlock wrapper for PK11_FindSlotByName() to work around race condition
- * in nssSlot_IsTokenPresent() causing spurious SEC_ERROR_NO_TOKEN.  For more
- * details, go to <https://bugzilla.mozilla.org/1297397>.
- */
-static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name)
-{
-  PK11SlotInfo *slot;
-  PR_Lock(nss_findslot_lock);
-  slot = PK11_FindSlotByName(slot_name);
-  PR_Unlock(nss_findslot_lock);
-  return slot;
-}
-
-/* wrap 'ptr' as list node and tail-insert into 'list' */
-static CURLcode insert_wrapped_ptr(struct Curl_llist *list, void *ptr)
-{
-  struct ptr_list_wrap *wrap = malloc(sizeof(*wrap));
-  if(!wrap)
-    return CURLE_OUT_OF_MEMORY;
-
-  wrap->ptr = ptr;
-  Curl_llist_insert_next(list, list->tail, wrap, &wrap->node);
-  return CURLE_OK;
-}
-
-/* Call PK11_CreateGenericObject() with the given obj_class and filename.  If
- * the call succeeds, append the object handle to the list of objects so that
- * the object can be destroyed in nss_close(). */
-static CURLcode nss_create_object(struct ssl_connect_data *connssl,
-                                  CK_OBJECT_CLASS obj_class,
-                                  const char *filename, bool cacert)
-{
-  PK11SlotInfo *slot;
-  PK11GenericObject *obj;
-  CK_BBOOL cktrue = CK_TRUE;
-  CK_BBOOL ckfalse = CK_FALSE;
-  CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
-  int attr_cnt = 0;
-  CURLcode result = (cacert)
-    ? CURLE_SSL_CACERT_BADFILE
-    : CURLE_SSL_CERTPROBLEM;
-
-  const int slot_id = (cacert) ? 0 : 1;
-  char *slot_name = aprintf("PEM Token #%d", slot_id);
-  struct ssl_backend_data *backend = connssl->backend;
-
-  DEBUGASSERT(backend);
-
-  if(!slot_name)
-    return CURLE_OUT_OF_MEMORY;
-
-  slot = nss_find_slot_by_name(slot_name);
-  free(slot_name);
-  if(!slot)
-    return result;
-
-  PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
-  PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
-  PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename,
-                (CK_ULONG)strlen(filename) + 1);
-
-  if(CKO_CERTIFICATE == obj_class) {
-    CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse);
-    PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval));
-  }
-
-  /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because
-   * PK11_DestroyGenericObject() does not release resources allocated by
-   * PK11_CreateGenericObject() early enough.  */
-  obj =
-#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT
-    PK11_CreateManagedGenericObject
-#else
-    PK11_CreateGenericObject
-#endif
-    (slot, attrs, attr_cnt, PR_FALSE);
-
-  PK11_FreeSlot(slot);
-  if(!obj)
-    return result;
-
-  if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) {
-    PK11_DestroyGenericObject(obj);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  if(!cacert && CKO_CERTIFICATE == obj_class)
-    /* store reference to a client certificate */
-    backend->obj_clicert = obj;
-
-  return CURLE_OK;
-}
-
-/* Destroy the NSS object whose handle is given by ptr.  This function is
- * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy
- * NSS objects in nss_close() */
-static void nss_destroy_object(void *user, void *ptr)
-{
-  struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr;
-  PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr;
-  (void) user;
-  PK11_DestroyGenericObject(obj);
-  free(wrap);
-}
-
-/* same as nss_destroy_object() but for CRL items */
-static void nss_destroy_crl_item(void *user, void *ptr)
-{
-  struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr;
-  SECItem *crl_der = (SECItem *) wrap->ptr;
-  (void) user;
-  SECITEM_FreeItem(crl_der, PR_TRUE);
-  free(wrap);
-}
-
-static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
-                              const char *filename, PRBool cacert)
-{
-  CURLcode result = (cacert)
-    ? CURLE_SSL_CACERT_BADFILE
-    : CURLE_SSL_CERTPROBLEM;
-
-  /* libnsspem.so leaks memory if the requested file does not exist.  For more
-   * details, go to <https://bugzilla.redhat.com/734760>. */
-  if(is_file(filename))
-    result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
-
-  if(!result && !cacert) {
-    /* we have successfully loaded a client certificate */
-    char *nickname = NULL;
-    char *n = strrchr(filename, '/');
-    if(n)
-      n++;
-
-    /* The following undocumented magic helps to avoid a SIGSEGV on call
-     * of PK11_ReadRawAttribute() from SelectClientCert() when using an
-     * immature version of libnsspem.so.  For more details, go to
-     * <https://bugzilla.redhat.com/733685>. */
-    nickname = aprintf("PEM Token #1:%s", n);
-    if(nickname) {
-      CERTCertificate *cert = PK11_FindCertFromNickname(nickname, NULL);
-      if(cert)
-        CERT_DestroyCertificate(cert);
-
-      free(nickname);
-    }
-  }
-
-  return result;
-}
-
-/* add given CRL to cache if it is not already there */
-static CURLcode nss_cache_crl(SECItem *crl_der)
-{
-  CERTCertDBHandle *db = CERT_GetDefaultCertDB();
-  CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0);
-  if(crl) {
-    /* CRL already cached */
-    SEC_DestroyCrl(crl);
-    SECITEM_FreeItem(crl_der, PR_TRUE);
-    return CURLE_OK;
-  }
-
-  /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */
-  PR_Lock(nss_crllock);
-
-  if(SECSuccess != CERT_CacheCRL(db, crl_der)) {
-    /* unable to cache CRL */
-    SECITEM_FreeItem(crl_der, PR_TRUE);
-    PR_Unlock(nss_crllock);
-    return CURLE_SSL_CRL_BADFILE;
-  }
-
-  /* store the CRL item so that we can free it in nss_cleanup() */
-  if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) {
-    if(SECSuccess == CERT_UncacheCRL(db, crl_der))
-      SECITEM_FreeItem(crl_der, PR_TRUE);
-    PR_Unlock(nss_crllock);
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  /* we need to clear session cache, so that the CRL could take effect */
-  SSL_ClearSessionCache();
-  PR_Unlock(nss_crllock);
-  return CURLE_OK;
-}
-
-static CURLcode nss_load_crl(const char *crlfilename)
-{
-  PRFileDesc *infile;
-  PRFileInfo  info;
-  SECItem filedata = { 0, NULL, 0 };
-  SECItem *crl_der = NULL;
-  char *body;
-
-  infile = PR_Open(crlfilename, PR_RDONLY, 0);
-  if(!infile)
-    return CURLE_SSL_CRL_BADFILE;
-
-  if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info))
-    goto fail;
-
-  if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1))
-    goto fail;
-
-  if(info.size != PR_Read(infile, filedata.data, info.size))
-    goto fail;
-
-  crl_der = SECITEM_AllocItem(NULL, NULL, 0U);
-  if(!crl_der)
-    goto fail;
-
-  /* place a trailing zero right after the visible data */
-  body = (char *)filedata.data;
-  body[--filedata.len] = '\0';
-
-  body = strstr(body, "-----BEGIN");
-  if(body) {
-    /* assume ASCII */
-    char *trailer;
-    char *begin = PORT_Strchr(body, '\n');
-    if(!begin)
-      begin = PORT_Strchr(body, '\r');
-    if(!begin)
-      goto fail;
-
-    trailer = strstr(++begin, "-----END");
-    if(!trailer)
-      goto fail;
-
-    /* retrieve DER from ASCII */
-    *trailer = '\0';
-    if(ATOB_ConvertAsciiToItem(crl_der, begin))
-      goto fail;
-
-    SECITEM_FreeItem(&filedata, PR_FALSE);
-  }
-  else
-    /* assume DER */
-    *crl_der = filedata;
-
-  PR_Close(infile);
-  return nss_cache_crl(crl_der);
-
-fail:
-  PR_Close(infile);
-  SECITEM_FreeItem(crl_der, PR_TRUE);
-  SECITEM_FreeItem(&filedata, PR_FALSE);
-  return CURLE_SSL_CRL_BADFILE;
-}
-
-static CURLcode nss_load_key(struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             char *key_file)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  PK11SlotInfo *slot, *tmp;
-  SECStatus status;
-  CURLcode result;
-
-  (void)data;
-  result = nss_create_object(connssl, CKO_PRIVATE_KEY, key_file, FALSE);
-  if(result) {
-    PR_SetError(SEC_ERROR_BAD_KEY, 0);
-    return result;
-  }
-
-  slot = nss_find_slot_by_name("PEM Token #1");
-  if(!slot)
-    return CURLE_SSL_CERTPROBLEM;
-
-  /* This will force the token to be seen as re-inserted */
-  tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0);
-  if(tmp)
-    PK11_FreeSlot(tmp);
-  if(!PK11_IsPresent(slot)) {
-    PK11_FreeSlot(slot);
-    return CURLE_SSL_CERTPROBLEM;
-  }
-
-  status = PK11_Authenticate(slot, PR_TRUE, ssl_config->key_passwd);
-  PK11_FreeSlot(slot);
-
-  return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM;
-}
-
-static int display_error(struct Curl_easy *data, PRInt32 err,
-                         const char *filename)
-{
-  switch(err) {
-  case SEC_ERROR_BAD_PASSWORD:
-    failf(data, "Unable to load client key: Incorrect password");
-    return 1;
-  case SEC_ERROR_UNKNOWN_CERT:
-    failf(data, "Unable to load certificate %s", filename);
-    return 1;
-  default:
-    break;
-  }
-  return 0; /* The caller will print a generic error */
-}
-
-static CURLcode cert_stuff(struct Curl_cfilter *cf,
-                           struct Curl_easy *data,
-                           char *cert_file, char *key_file)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  CURLcode result;
-
-  if(cert_file) {
-    result = nss_load_cert(connssl, cert_file, PR_FALSE);
-    if(result) {
-      const PRErrorCode err = PR_GetError();
-      if(!display_error(data, err, cert_file)) {
-        const char *err_name = nss_error_to_name(err);
-        failf(data, "unable to load client cert: %d (%s)", err, err_name);
-      }
-
-      return result;
-    }
-  }
-
-  if(key_file || (is_file(cert_file))) {
-    if(key_file)
-      result = nss_load_key(cf, data, key_file);
-    else
-      /* In case the cert file also has the key */
-      result = nss_load_key(cf, data, cert_file);
-    if(result) {
-      const PRErrorCode err = PR_GetError();
-      if(!display_error(data, err, key_file)) {
-        const char *err_name = nss_error_to_name(err);
-        failf(data, "unable to load client key: %d (%s)", err, err_name);
-      }
-
-      return result;
-    }
-  }
-
-  return CURLE_OK;
-}
-
-static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg)
-{
-  (void)slot; /* unused */
-
-  if(retry || !arg)
-    return NULL;
-  else
-    return (char *)PORT_Strdup((char *)arg);
-}
-
-/* bypass the default SSL_AuthCertificate() hook in case we do not want to
- * verify peer */
-static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
-                                    PRBool isServer)
-{
-  struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct Curl_easy *data = connssl->backend->data;
-
-  DEBUGASSERT(data);
-#ifdef SSL_ENABLE_OCSP_STAPLING
-  if(conn_config->verifystatus) {
-    SECStatus cacheResult;
-
-    const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
-    if(!csa) {
-      failf(data, "Invalid OCSP response");
-      return SECFailure;
-    }
-
-    if(csa->len == 0) {
-      failf(data, "No OCSP response received");
-      return SECFailure;
-    }
-
-    cacheResult = CERT_CacheOCSPResponseFromSideChannel(
-      CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd),
-      PR_Now(), &csa->items[0], arg
-    );
-
-    if(cacheResult != SECSuccess) {
-      failf(data, "Invalid OCSP response");
-      return cacheResult;
-    }
-  }
-#endif
-
-  if(!conn_config->verifypeer) {
-    infof(data, "skipping SSL peer certificate verification");
-    return SECSuccess;
-  }
-
-  return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer);
-}
-
-/**
- * Inform the application that the handshake is complete.
- */
-static void HandshakeCallback(PRFileDesc *sock, void *arg)
-{
-  struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct Curl_easy *data = connssl->backend->data;
-  unsigned int buflenmax = 50;
-  unsigned char buf[50];
-  unsigned int buflen;
-  SSLNextProtoState state;
-
-  DEBUGASSERT(data);
-  if(!connssl->alpn) {
-    return;
-  }
-
-  if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) {
-
-    switch(state) {
-#if NSSVERNUM >= 0x031a00 /* 3.26.0 */
-    /* used by NSS internally to implement 0-RTT */
-    case SSL_NEXT_PROTO_EARLY_VALUE:
-      /* fall through! */
-#endif
-    case SSL_NEXT_PROTO_NO_SUPPORT:
-    case SSL_NEXT_PROTO_NO_OVERLAP:
-      Curl_alpn_set_negotiated(cf, data, NULL, 0);
-      return;
-#ifdef SSL_ENABLE_ALPN
-    case SSL_NEXT_PROTO_SELECTED:
-      Curl_alpn_set_negotiated(cf, data, buf, buflen);
-      break;
-#endif
-    default:
-      /* ignore SSL_NEXT_PROTO_NEGOTIATED */
-      break;
-    }
-
-  }
-}
-
-#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
-static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data,
-                                       PRBool *canFalseStart)
-{
-  struct Curl_easy *data = (struct Curl_easy *)client_data;
-
-  SSLChannelInfo channelInfo;
-  SSLCipherSuiteInfo cipherInfo;
-
-  SECStatus rv;
-  PRBool negotiatedExtension;
-
-  *canFalseStart = PR_FALSE;
-
-  if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess)
-    return SECFailure;
-
-  if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo,
-                            sizeof(cipherInfo)) != SECSuccess)
-    return SECFailure;
-
-  /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for
-   * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310
-   */
-  if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2)
-    goto end;
-
-  /* Only allow ECDHE key exchange algorithm.
-   * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */
-  if(cipherInfo.keaType != ssl_kea_ecdh)
-    goto end;
-
-  /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC
-   * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt
-   * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */
-  if(cipherInfo.symCipher != ssl_calg_aes_gcm)
-    goto end;
-
-  /* Enforce ALPN to do False Start, as an indicator of server
-     compatibility. */
-  rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn,
-                                        &negotiatedExtension);
-  if(rv != SECSuccess || !negotiatedExtension) {
-    rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn,
-                                          &negotiatedExtension);
-  }
-
-  if(rv != SECSuccess || !negotiatedExtension)
-    goto end;
-
-  *canFalseStart = PR_TRUE;
-
-  infof(data, "Trying TLS False Start");
-
-end:
-  return SECSuccess;
-}
-#endif
-
-static void display_cert_info(struct Curl_easy *data,
-                              CERTCertificate *cert)
-{
-  char *subject, *issuer, *common_name;
-  PRExplodedTime printableTime;
-  char timeString[256];
-  PRTime notBefore, notAfter;
-
-  subject = CERT_NameToAscii(&cert->subject);
-  issuer = CERT_NameToAscii(&cert->issuer);
-  common_name = CERT_GetCommonName(&cert->subject);
-  infof(data, "subject: %s", subject);
-
-  CERT_GetCertTimes(cert, &notBefore, &notAfter);
-  PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
-  PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
-  infof(data, " start date: %s", timeString);
-  PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
-  PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
-  infof(data, " expire date: %s", timeString);
-  infof(data, " common name: %s", common_name);
-  infof(data, " issuer: %s", issuer);
-
-  PR_Free(subject);
-  PR_Free(issuer);
-  PR_Free(common_name);
-}
-
-/* A number of certs that will never occur in a real server handshake */
-#define TOO_MANY_CERTS 300
-
-static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock)
-{
-  CURLcode result = CURLE_OK;
-  SSLChannelInfo channel;
-  SSLCipherSuiteInfo suite;
-  CERTCertificate *cert;
-  CERTCertificate *cert2;
-  CERTCertificate *cert3;
-  PRTime now;
-
-  if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) ==
-     SECSuccess && channel.length == sizeof(channel) &&
-     channel.cipherSuite) {
-    if(SSL_GetCipherSuiteInfo(channel.cipherSuite,
-                              &suite, sizeof(suite)) == SECSuccess) {
-      infof(data, "SSL connection using %s", suite.cipherSuiteName);
-    }
-  }
-
-  cert = SSL_PeerCertificate(sock);
-  if(cert) {
-    infof(data, "Server certificate:");
-
-    if(!data->set.ssl.certinfo) {
-      display_cert_info(data, cert);
-      CERT_DestroyCertificate(cert);
-    }
-    else {
-      /* Count certificates in chain. */
-      int i = 1;
-      now = PR_Now();
-      if(!cert->isRoot) {
-        cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
-        while(cert2) {
-          i++;
-          if(i >= TOO_MANY_CERTS) {
-            CERT_DestroyCertificate(cert2);
-            failf(data, "certificate loop");
-            return CURLE_SSL_CERTPROBLEM;
-          }
-          if(cert2->isRoot) {
-            CERT_DestroyCertificate(cert2);
-            break;
-          }
-          cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA);
-          CERT_DestroyCertificate(cert2);
-          cert2 = cert3;
-        }
-      }
-
-      result = Curl_ssl_init_certinfo(data, i);
-      if(!result) {
-        for(i = 0; cert; cert = cert2) {
-          result = Curl_extract_certinfo(data, i++, (char *)cert->derCert.data,
-                                         (char *)cert->derCert.data +
-                                                 cert->derCert.len);
-          if(result)
-            break;
-
-          if(cert->isRoot) {
-            CERT_DestroyCertificate(cert);
-            break;
-          }
-
-          cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
-          CERT_DestroyCertificate(cert);
-        }
-      }
-    }
-  }
-
-  return result;
-}
-
-static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
-{
-  struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct Curl_easy *data = connssl->backend->data;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct ssl_config_data *ssl_config;
-  PRErrorCode err = PR_GetError();
-  CERTCertificate *cert;
-
-  DEBUGASSERT(data);
-  ssl_config = Curl_ssl_cf_get_config(cf, data);
-  /* remember the cert verification result */
-  ssl_config->certverifyresult = err;
-
-  if(err == SSL_ERROR_BAD_CERT_DOMAIN && !conn_config->verifyhost)
-    /* we are asked not to verify the host name */
-    return SECSuccess;
-
-  /* print only info about the cert, the error is printed off the callback */
-  cert = SSL_PeerCertificate(sock);
-  if(cert) {
-    infof(data, "Server certificate:");
-    display_cert_info(data, cert);
-    CERT_DestroyCertificate(cert);
-  }
-
-  return SECFailure;
-}
-
-/**
- *
- * Check that the Peer certificate's issuer certificate matches the one found
- * by issuer_nickname.  This is not exactly the way OpenSSL and GNU TLS do the
- * issuer check, so we provide comments that mimic the OpenSSL
- * X509_check_issued function (in x509v3/v3_purp.c)
- */
-static SECStatus check_issuer_cert(PRFileDesc *sock,
-                                   char *issuer_nickname)
-{
-  CERTCertificate *cert, *cert_issuer, *issuer;
-  SECStatus res = SECSuccess;
-  void *proto_win = NULL;
-
-  cert = SSL_PeerCertificate(sock);
-  cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner);
-
-  proto_win = SSL_RevealPinArg(sock);
-  issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
-
-  if((!cert_issuer) || (!issuer))
-    res = SECFailure;
-  else if(SECITEM_CompareItem(&cert_issuer->derCert,
-                              &issuer->derCert) != SECEqual)
-    res = SECFailure;
-
-  CERT_DestroyCertificate(cert);
-  CERT_DestroyCertificate(issuer);
-  CERT_DestroyCertificate(cert_issuer);
-  return res;
-}
-
-static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
-                                const char *pinnedpubkey)
-{
-  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-  struct ssl_backend_data *backend = connssl->backend;
-  struct Curl_easy *data = NULL;
-  CERTCertificate *cert;
-
-  DEBUGASSERT(backend);
-  data = backend->data;
-
-  if(!pinnedpubkey)
-    /* no pinned public key specified */
-    return CURLE_OK;
-
-  /* get peer certificate */
-  cert = SSL_PeerCertificate(backend->handle);
-  if(cert) {
-    /* extract public key from peer certificate */
-    SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert);
-    if(pubkey) {
-      /* encode the public key as DER */
-      SECItem *cert_der = PK11_DEREncodePublicKey(pubkey);
-      if(cert_der) {
-        /* compare the public key with the pinned public key */
-        result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data,
-                                      cert_der->len);
-        SECITEM_FreeItem(cert_der, PR_TRUE);
-      }
-      SECKEY_DestroyPublicKey(pubkey);
-    }
-    CERT_DestroyCertificate(cert);
-  }
-
-  /* report the resulting status */
-  switch(result) {
-  case CURLE_OK:
-    infof(data, "pinned public key verified successfully");
-    break;
-  case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
-    failf(data, "failed to verify pinned public key");
-    break;
-  default:
-    /* OOM, etc. */
-    break;
-  }
-
-  return result;
-}
-
-/**
- *
- * Callback to pick the SSL client certificate.
- */
-static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
-                                  struct CERTDistNamesStr *caNames,
-                                  struct CERTCertificateStr **pRetCert,
-                                  struct SECKEYPrivateKeyStr **pRetKey)
-{
-  struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
-  struct ssl_backend_data *backend = connssl->backend;
-  struct Curl_easy *data = NULL;
-  const char *nickname = NULL;
-  static const char pem_slotname[] = "PEM Token #1";
-
-  DEBUGASSERT(backend);
-
-  data = backend->data;
-  nickname = backend->client_nickname;
-
-  if(backend->obj_clicert) {
-    /* use the cert/key provided by PEM reader */
-    SECItem cert_der = { 0, NULL, 0 };
-    void *proto_win = SSL_RevealPinArg(sock);
-    struct CERTCertificateStr *cert;
-    struct SECKEYPrivateKeyStr *key;
-
-    PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname);
-    if(!slot) {
-      failf(data, "NSS: PK11 slot not found: %s", pem_slotname);
-      return SECFailure;
-    }
-
-    if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE,
-                             &cert_der) != SECSuccess) {
-      failf(data, "NSS: CKA_VALUE not found in PK11 generic object");
-      PK11_FreeSlot(slot);
-      return SECFailure;
-    }
-
-    cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win);
-    SECITEM_FreeItem(&cert_der, PR_FALSE);
-    if(!cert) {
-      failf(data, "NSS: client certificate from file not found");
-      PK11_FreeSlot(slot);
-      return SECFailure;
-    }
-
-    key = PK11_FindPrivateKeyFromCert(slot, cert, NULL);
-    PK11_FreeSlot(slot);
-    if(!key) {
-      failf(data, "NSS: private key from file not found");
-      CERT_DestroyCertificate(cert);
-      return SECFailure;
-    }
-
-    infof(data, "NSS: client certificate from file");
-    display_cert_info(data, cert);
-
-    *pRetCert = cert;
-    *pRetKey = key;
-    return SECSuccess;
-  }
-
-  /* use the default NSS hook */
-  if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames,
-                                          pRetCert, pRetKey)
-     || !*pRetCert) {
-
-    if(!nickname)
-      failf(data, "NSS: client certificate not found (nickname not "
-            "specified)");
-    else
-      failf(data, "NSS: client certificate not found: %s", nickname);
-
-    return SECFailure;
-  }
-
-  /* get certificate nickname if any */
-  nickname = (*pRetCert)->nickname;
-  if(!nickname)
-    nickname = "[unknown]";
-
-  if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) {
-    failf(data, "NSS: refusing previously loaded certificate from file: %s",
-          nickname);
-    return SECFailure;
-  }
-
-  if(!*pRetKey) {
-    failf(data, "NSS: private key not found for certificate: %s", nickname);
-    return SECFailure;
-  }
-
-  infof(data, "NSS: using client certificate: %s", nickname);
-  display_cert_info(data, *pRetCert);
-  return SECSuccess;
-}
-
-/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */
-static void nss_update_connecting_state(ssl_connect_state state, void *secret)
-{
-  struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret;
-  if(PR_GetError() != PR_WOULD_BLOCK_ERROR)
-    /* an unrelated error is passing by */
-    return;
-
-  switch(connssl->connecting_state) {
-  case ssl_connect_2:
-  case ssl_connect_2_reading:
-  case ssl_connect_2_writing:
-    break;
-  default:
-    /* we are not called from an SSL handshake */
-    return;
-  }
-
-  /* update the state accordingly */
-  connssl->connecting_state = state;
-}
-
-/* recv() wrapper we use to detect blocking direction during SSL handshake */
-static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
-                            PRIntn flags, PRIntervalTime timeout)
-{
-  const PRRecvFN recv_fn = fd->lower->methods->recv;
-  const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout);
-  if(rv < 0)
-    /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */
-    nss_update_connecting_state(ssl_connect_2_reading, fd->secret);
-  return rv;
-}
-
-/* send() wrapper we use to detect blocking direction during SSL handshake */
-static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
-                            PRIntn flags, PRIntervalTime timeout)
-{
-  const PRSendFN send_fn = fd->lower->methods->send;
-  const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout);
-  if(rv < 0)
-    /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */
-    nss_update_connecting_state(ssl_connect_2_writing, fd->secret);
-  return rv;
-}
-
-/* close() wrapper to avoid assertion failure due to fd->secret != NULL */
-static PRStatus nspr_io_close(PRFileDesc *fd)
-{
-  const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close;
-  fd->secret = NULL;
-  return close_fn(fd);
-}
-
-/* load a PKCS #11 module */
-static CURLcode nss_load_module(SECMODModule **pmod, const char *library,
-                                const char *name)
-{
-  char *config_string;
-  SECMODModule *module = *pmod;
-  if(module)
-    /* already loaded */
-    return CURLE_OK;
-
-  config_string = aprintf("library=%s name=%s", library, name);
-  if(!config_string)
-    return CURLE_OUT_OF_MEMORY;
-
-  module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE);
-  free(config_string);
-
-  if(module && module->loaded) {
-    /* loaded successfully */
-    *pmod = module;
-    return CURLE_OK;
-  }
-
-  if(module)
-    SECMOD_DestroyModule(module);
-  return CURLE_FAILED_INIT;
-}
-
-/* unload a PKCS #11 module */
-static void nss_unload_module(SECMODModule **pmod)
-{
-  SECMODModule *module = *pmod;
-  if(!module)
-    /* not loaded */
-    return;
-
-  if(SECMOD_UnloadUserModule(module) != SECSuccess)
-    /* unload failed */
-    return;
-
-  SECMOD_DestroyModule(module);
-  *pmod = NULL;
-}
-
-/* data might be NULL */
-static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
-{
-  NSSInitParameters initparams;
-  PRErrorCode err;
-  const char *err_name;
-
-  if(nss_context)
-    return CURLE_OK;
-
-  memset((void *) &initparams, '\0', sizeof(initparams));
-  initparams.length = sizeof(initparams);
-
-  if(cert_dir) {
-    char *certpath = aprintf("sql:%s", cert_dir);
-    if(!certpath)
-      return CURLE_OUT_OF_MEMORY;
-
-    infof(data, "Initializing NSS with certpath: %s", certpath);
-    nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
-                                  NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
-    free(certpath);
-
-    if(nss_context)
-      return CURLE_OK;
-
-    err = PR_GetError();
-    err_name = nss_error_to_name(err);
-    infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name);
-  }
-
-  infof(data, "Initializing NSS with certpath: none");
-  nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
-         | NSS_INIT_NOCERTDB   | NSS_INIT_NOMODDB       | NSS_INIT_FORCEOPEN
-         | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
-  if(nss_context)
-    return CURLE_OK;
-
-  err = PR_GetError();
-  err_name = nss_error_to_name(err);
-  failf(data, "Unable to initialize NSS: %d (%s)", err, err_name);
-  return CURLE_SSL_CACERT_BADFILE;
-}
-
-/* data might be NULL */
-static CURLcode nss_setup(struct Curl_easy *data)
-{
-  char *cert_dir;
-  struct_stat st;
-  CURLcode result;
-
-  if(initialized)
-    return CURLE_OK;
-
-  /* list of all CRL items we need to destroy in nss_cleanup() */
-  Curl_llist_init(&nss_crl_list, nss_destroy_crl_item);
-
-  /* First we check if $SSL_DIR points to a valid dir */
-  cert_dir = getenv("SSL_DIR");
-  if(cert_dir) {
-    if((stat(cert_dir, &st) != 0) ||
-        (!S_ISDIR(st.st_mode))) {
-      cert_dir = NULL;
-    }
-  }
-
-  /* Now we check if the default location is a valid dir */
-  if(!cert_dir) {
-    if((stat(SSL_DIR, &st) == 0) &&
-        (S_ISDIR(st.st_mode))) {
-      cert_dir = (char *)SSL_DIR;
-    }
-  }
-
-  if(nspr_io_identity == PR_INVALID_IO_LAYER) {
-    /* allocate an identity for our own NSPR I/O layer */
-    nspr_io_identity = PR_GetUniqueIdentity("libcurl");
-    if(nspr_io_identity == PR_INVALID_IO_LAYER)
-      return CURLE_OUT_OF_MEMORY;
-
-    /* the default methods just call down to the lower I/O layer */
-    memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(),
-           sizeof(nspr_io_methods));
-
-    /* override certain methods in the table by our wrappers */
-    nspr_io_methods.recv  = nspr_io_recv;
-    nspr_io_methods.send  = nspr_io_send;
-    nspr_io_methods.close = nspr_io_close;
-  }
-
-  result = nss_init_core(data, cert_dir);
-  if(result)
-    return result;
-
-  if(!any_cipher_enabled())
-    NSS_SetDomesticPolicy();
-
-  initialized = 1;
-
-  return CURLE_OK;
-}
-
-/**
- * Global SSL init
- *
- * @retval 0 error initializing SSL
- * @retval 1 SSL initialized successfully
- */
-static int nss_init(void)
-{
-  /* curl_global_init() is not thread-safe so this test is ok */
-  if(!nss_initlock) {
-    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
-    nss_initlock = PR_NewLock();
-    nss_crllock = PR_NewLock();
-    nss_findslot_lock = PR_NewLock();
-    nss_trustload_lock = PR_NewLock();
-  }
-
-  /* We will actually initialize NSS later */
-
-  return 1;
-}
-
-/* data might be NULL */
-CURLcode Curl_nss_force_init(struct Curl_easy *data)
-{
-  CURLcode result;
-  if(!nss_initlock) {
-    if(data)
-      failf(data, "unable to initialize NSS, curl_global_init() should have "
-                  "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL");
-    return CURLE_FAILED_INIT;
-  }
-
-  PR_Lock(nss_initlock);
-  result = nss_setup(data);
-  PR_Unlock(nss_initlock);
-
-  return result;
-}
-
-/* Global cleanup */
-static void nss_cleanup(void)
-{
-  /* This function isn't required to be threadsafe and this is only done
-   * as a safety feature.
-   */
-  PR_Lock(nss_initlock);
-  if(initialized) {
-    /* Free references to client certificates held in the SSL session cache.
-     * Omitting this hampers destruction of the security module owning
-     * the certificates. */
-    SSL_ClearSessionCache();
-
-    nss_unload_module(&pem_module);
-    nss_unload_module(&trust_module);
-    NSS_ShutdownContext(nss_context);
-    nss_context = NULL;
-  }
-
-  /* destroy all CRL items */
-  Curl_llist_destroy(&nss_crl_list, NULL);
-
-  PR_Unlock(nss_initlock);
-
-  PR_DestroyLock(nss_initlock);
-  PR_DestroyLock(nss_crllock);
-  PR_DestroyLock(nss_findslot_lock);
-  PR_DestroyLock(nss_trustload_lock);
-  nss_initlock = NULL;
-
-  initialized = 0;
-}
-
-static void close_one(struct ssl_connect_data *connssl)
-{
-  /* before the cleanup, check whether we are using a client certificate */
-  struct ssl_backend_data *backend = connssl->backend;
-  bool client_cert = true;
-
-  DEBUGASSERT(backend);
-
-  client_cert = (backend->client_nickname != NULL)
-    || (backend->obj_clicert != NULL);
-
-  if(backend->handle) {
-    char buf[32];
-    /* Maybe the server has already sent a close notify alert.
-       Read it to avoid an RST on the TCP connection. */
-    (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0,
-                  PR_INTERVAL_NO_WAIT);
-  }
-
-  free(backend->client_nickname);
-  backend->client_nickname = NULL;
-
-  /* destroy all NSS objects in order to avoid failure of NSS shutdown */
-  Curl_llist_destroy(&backend->obj_list, NULL);
-  backend->obj_clicert = NULL;
-
-  if(backend->handle) {
-    if(client_cert)
-      /* A server might require different authentication based on the
-       * particular path being requested by the client.  To support this
-       * scenario, we must ensure that a connection will never reuse the
-       * authentication data from a previous connection. */
-      SSL_InvalidateSession(backend->handle);
-
-    PR_Close(backend->handle);
-    backend->handle = NULL;
-  }
-}
-
-/*
- * This function is called when an SSL connection is closed.
- */
-static void nss_close(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
-  (void)data;
-  DEBUGASSERT(backend);
-
-  if(backend->handle) {
-    /* NSS closes the socket we previously handed to it, so we must mark it
-       as closed to avoid double close */
-    fake_sclose(cf->conn->sock[cf->sockindex]);
-    cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
-  }
-
-  close_one(connssl);
-}
-
-/* return true if NSS can provide error code (and possibly msg) for the
-   error */
-static bool is_nss_error(CURLcode err)
-{
-  switch(err) {
-  case CURLE_PEER_FAILED_VERIFICATION:
-  case CURLE_SSL_CERTPROBLEM:
-  case CURLE_SSL_CONNECT_ERROR:
-  case CURLE_SSL_ISSUER_ERROR:
-    return true;
-
-  default:
-    return false;
-  }
-}
-
-/* return true if the given error code is related to a client certificate */
-static bool is_cc_error(PRInt32 err)
-{
-  switch(err) {
-  case SSL_ERROR_BAD_CERT_ALERT:
-  case SSL_ERROR_EXPIRED_CERT_ALERT:
-  case SSL_ERROR_REVOKED_CERT_ALERT:
-    return true;
-
-  default:
-    return false;
-  }
-}
-
-static CURLcode nss_load_ca_certificates(struct Curl_cfilter *cf,
-                                         struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  const char *cafile = conn_config->CAfile;
-  const char *capath = conn_config->CApath;
-  bool use_trust_module;
-  CURLcode result = CURLE_OK;
-
-  /* treat empty string as unset */
-  if(cafile && !cafile[0])
-    cafile = NULL;
-  if(capath && !capath[0])
-    capath = NULL;
-
-  infof(data, " CAfile: %s", cafile ? cafile : "none");
-  infof(data, " CApath: %s", capath ? capath : "none");
-
-  /* load libnssckbi.so if no other trust roots were specified */
-  use_trust_module = !cafile && !capath;
-
-  PR_Lock(nss_trustload_lock);
-  if(use_trust_module && !trust_module) {
-    /* libnssckbi.so needed but not yet loaded --> load it! */
-    result = nss_load_module(&trust_module, trust_library, "trust");
-    infof(data, "%s %s", (result) ? "failed to load" : "loaded",
-          trust_library);
-    if(result == CURLE_FAILED_INIT)
-      /* If libnssckbi.so is not available (or fails to load), one can still
-         use CA certificates stored in NSS database.  Ignore the failure. */
-      result = CURLE_OK;
-  }
-  else if(!use_trust_module && trust_module) {
-    /* libnssckbi.so not needed but already loaded --> unload it! */
-    infof(data, "unloading %s", trust_library);
-    nss_unload_module(&trust_module);
-  }
-  PR_Unlock(nss_trustload_lock);
-
-  if(cafile)
-    result = nss_load_cert(connssl, cafile, PR_TRUE);
-
-  if(result)
-    return result;
-
-  if(capath) {
-    struct_stat st;
-    if(stat(capath, &st) == -1)
-      return CURLE_SSL_CACERT_BADFILE;
-
-    if(S_ISDIR(st.st_mode)) {
-      PRDirEntry *entry;
-      PRDir *dir = PR_OpenDir(capath);
-      if(!dir)
-        return CURLE_SSL_CACERT_BADFILE;
-
-      while((entry =
-             PR_ReadDir(dir, (PRDirFlags)(PR_SKIP_BOTH | PR_SKIP_HIDDEN)))) {
-        char *fullpath = aprintf("%s/%s", capath, entry->name);
-        if(!fullpath) {
-          PR_CloseDir(dir);
-          return CURLE_OUT_OF_MEMORY;
-        }
-
-        if(CURLE_OK != nss_load_cert(connssl, fullpath, PR_TRUE))
-          /* This is purposefully tolerant of errors so non-PEM files can
-           * be in the same directory */
-          infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath);
-
-        free(fullpath);
-      }
-
-      PR_CloseDir(dir);
-    }
-    else
-      infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath);
-  }
-
-  return CURLE_OK;
-}
-
-static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version)
-{
-  switch(version) {
-  case CURL_SSLVERSION_SSLv2:
-    *nssver = SSL_LIBRARY_VERSION_2;
-    return CURLE_OK;
-
-  case CURL_SSLVERSION_SSLv3:
-    return CURLE_NOT_BUILT_IN;
-
-  case CURL_SSLVERSION_TLSv1_0:
-    *nssver = SSL_LIBRARY_VERSION_TLS_1_0;
-    return CURLE_OK;
-
-  case CURL_SSLVERSION_TLSv1_1:
-#ifdef SSL_LIBRARY_VERSION_TLS_1_1
-    *nssver = SSL_LIBRARY_VERSION_TLS_1_1;
-    return CURLE_OK;
-#else
-    return CURLE_SSL_CONNECT_ERROR;
-#endif
-
-  case CURL_SSLVERSION_TLSv1_2:
-#ifdef SSL_LIBRARY_VERSION_TLS_1_2
-    *nssver = SSL_LIBRARY_VERSION_TLS_1_2;
-    return CURLE_OK;
-#else
-    return CURLE_SSL_CONNECT_ERROR;
-#endif
-
-  case CURL_SSLVERSION_TLSv1_3:
-#ifdef SSL_LIBRARY_VERSION_TLS_1_3
-    *nssver = SSL_LIBRARY_VERSION_TLS_1_3;
-    return CURLE_OK;
-#else
-    return CURLE_SSL_CONNECT_ERROR;
-#endif
-
-  default:
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-}
-
-static CURLcode nss_init_sslver(SSLVersionRange *sslver,
-                                struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
-{
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  CURLcode result;
-  const long min = conn_config->version;
-  const long max = conn_config->version_max;
-  SSLVersionRange vrange;
-
-  switch(min) {
-  case CURL_SSLVERSION_TLSv1:
-  case CURL_SSLVERSION_DEFAULT:
-    /* Bump our minimum TLS version if NSS has stricter requirements. */
-    if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess)
-      return CURLE_SSL_CONNECT_ERROR;
-    if(sslver->min < vrange.min)
-      sslver->min = vrange.min;
-    break;
-  default:
-    result = nss_sslver_from_curl(&sslver->min, min);
-    if(result) {
-      failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
-      return result;
-    }
-  }
-
-  switch(max) {
-  case CURL_SSLVERSION_MAX_NONE:
-  case CURL_SSLVERSION_MAX_DEFAULT:
-    break;
-  default:
-    result = nss_sslver_from_curl(&sslver->max, max >> 16);
-    if(result) {
-      failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
-      return result;
-    }
-  }
-
-  return CURLE_OK;
-}
-
-static CURLcode nss_fail_connect(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 CURLcode curlerr)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
-
-  DEBUGASSERT(backend);
-
-  if(is_nss_error(curlerr)) {
-    /* read NSPR error code */
-    PRErrorCode err = PR_GetError();
-    if(is_cc_error(err))
-      curlerr = CURLE_SSL_CERTPROBLEM;
-
-    /* print the error number and error string */
-    infof(data, "NSS error %d (%s)", err, nss_error_to_name(err));
-
-    /* print a human-readable message describing the error if available */
-    nss_print_error_message(data, err);
-  }
-
-  /* cleanup on connection failure */
-  Curl_llist_destroy(&backend->obj_list, NULL);
-
-  return curlerr;
-}
-
-/* Switch the SSL socket into blocking or non-blocking mode. */
-static CURLcode nss_set_blocking(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 bool blocking)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  PRSocketOptionData sock_opt;
-  struct ssl_backend_data *backend = connssl->backend;
-
-  DEBUGASSERT(backend);
-
-  sock_opt.option = PR_SockOpt_Nonblocking;
-  sock_opt.value.non_blocking = !blocking;
-
-  if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS)
-    return nss_fail_connect(cf, data, CURLE_SSL_CONNECT_ERROR);
-
-  return CURLE_OK;
-}
-
-static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data)
-{
-  PRFileDesc *model = NULL;
-  PRFileDesc *nspr_io = NULL;
-  PRFileDesc *nspr_io_stub = NULL;
-  PRBool ssl_no_cache;
-  PRBool ssl_cbc_random_iv;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
-  struct ssl_connect_data *connssl_next = cf_ssl_next?
-                                            cf_ssl_next->ctx : NULL;
-  CURLcode result;
-  bool second_layer = FALSE;
-  SSLVersionRange sslver_supported;
-  SSLVersionRange sslver = {
-    SSL_LIBRARY_VERSION_TLS_1_0,  /* min */
-#ifdef SSL_LIBRARY_VERSION_TLS_1_3
-    SSL_LIBRARY_VERSION_TLS_1_3   /* max */
-#elif defined SSL_LIBRARY_VERSION_TLS_1_2
-    SSL_LIBRARY_VERSION_TLS_1_2
-#elif defined SSL_LIBRARY_VERSION_TLS_1_1
-    SSL_LIBRARY_VERSION_TLS_1_1
-#else
-    SSL_LIBRARY_VERSION_TLS_1_0
-#endif
-  };
-  const char *hostname = connssl->hostname;
-  char *snihost;
-
-  snihost = Curl_ssl_snihost(data, hostname, NULL);
-  if(!snihost) {
-    failf(data, "Failed to set SNI");
-    return CURLE_SSL_CONNECT_ERROR;
-  }
-
-  DEBUGASSERT(backend);
-
-  backend->data = data;
-
-  /* list of all NSS objects we need to destroy in nss_do_close() */
-  Curl_llist_init(&backend->obj_list, nss_destroy_object);
-
-  PR_Lock(nss_initlock);
-  result = nss_setup(data);
-  if(result) {
-    PR_Unlock(nss_initlock);
-    goto error;
-  }
-
-  PK11_SetPasswordFunc(nss_get_password);
-
-  result = nss_load_module(&pem_module, pem_library, "PEM");
-  PR_Unlock(nss_initlock);
-  if(result == CURLE_FAILED_INIT)
-    infof(data, "WARNING: failed to load NSS PEM library %s. Using "
-                "OpenSSL PEM certificates will not work.", pem_library);
-  else if(result)
-    goto error;
-
-  result = CURLE_SSL_CONNECT_ERROR;
-
-  model = PR_NewTCPSocket();
-  if(!model)
-    goto error;
-  model = SSL_ImportFD(NULL, model);
-
-  if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
-    goto error;
-  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
-    goto error;
-  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
-    goto error;
-
-  /* do not use SSL cache if disabled or we are not going to verify peer */
-  ssl_no_cache = (ssl_config->primary.sessionid
-                  && conn_config->verifypeer) ? PR_FALSE : PR_TRUE;
-  if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
-    goto error;
-
-  /* enable/disable the requested SSL version(s) */
-  if(nss_init_sslver(&sslver, cf, data) != CURLE_OK)
-    goto error;
-  if(SSL_VersionRangeGetSupported(ssl_variant_stream,
-                                  &sslver_supported) != SECSuccess)
-    goto error;
-  if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) {
-    char *sslver_req_str, *sslver_supp_str;
-    sslver_req_str = nss_sslver_to_name(sslver.max);
-    sslver_supp_str = nss_sslver_to_name(sslver_supported.max);
-    if(sslver_req_str && sslver_supp_str)
-      infof(data, "Falling back from %s to max supported SSL version (%s)",
-            sslver_req_str, sslver_supp_str);
-    free(sslver_req_str);
-    free(sslver_supp_str);
-    sslver.max = sslver_supported.max;
-  }
-  if(SSL_VersionRangeSet(model, &sslver) != SECSuccess)
-    goto error;
-
-  ssl_cbc_random_iv = !ssl_config->enable_beast;
-#ifdef SSL_CBC_RANDOM_IV
-  /* unless the user explicitly asks to allow the protocol vulnerability, we
-     use the work-around */
-  if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
-    infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d",
-          ssl_cbc_random_iv);
-#else
-  if(ssl_cbc_random_iv)
-    infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in");
-#endif
-
-  if(conn_config->cipher_list) {
-    if(set_ciphers(data, model, conn_config->cipher_list) != SECSuccess) {
-      result = CURLE_SSL_CIPHER;
-      goto error;
-    }
-  }
-
-  if(!conn_config->verifypeer && conn_config->verifyhost)
-    infof(data, "WARNING: ignoring value of ssl.verifyhost");
-
-  /* bypass the default SSL_AuthCertificate() hook in case we do not want to
-   * verify peer */
-  if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, cf) != SECSuccess)
-    goto error;
-
-  /* not checked yet */
-  ssl_config->certverifyresult = 0;
-
-  if(SSL_BadCertHook(model, BadCertHandler, cf) != SECSuccess)
-    goto error;
-
-  if(SSL_HandshakeCallback(model, HandshakeCallback, cf) != SECSuccess)
-    goto error;
-
-  {
-    const CURLcode rv = nss_load_ca_certificates(cf, data);
-    if((rv == CURLE_SSL_CACERT_BADFILE) && !conn_config->verifypeer)
-      /* not a fatal error because we are not going to verify the peer */
-      infof(data, "WARNING: CA certificates failed to load");
-    else if(rv) {
-      result = rv;
-      goto error;
-    }
-  }
-
-  if(ssl_config->primary.CRLfile) {
-    const CURLcode rv = nss_load_crl(ssl_config->primary.CRLfile);
-    if(rv) {
-      result = rv;
-      goto error;
-    }
-    infof(data, "  CRLfile: %s", ssl_config->primary.CRLfile);
-  }
-
-  if(ssl_config->primary.clientcert) {
-    char *nickname = dup_nickname(data, ssl_config->primary.clientcert);
-    if(nickname) {
-      /* we are not going to use libnsspem.so to read the client cert */
-      backend->obj_clicert = NULL;
-    }
-    else {
-      CURLcode rv = cert_stuff(cf, data,
-                               ssl_config->primary.clientcert,
-                               ssl_config->key);
-      if(rv) {
-        /* failf() is already done in cert_stuff() */
-        result = rv;
-        goto error;
-      }
-    }
-
-    /* store the nickname for SelectClientCert() called during handshake */
-    backend->client_nickname = nickname;
-  }
-  else
-    backend->client_nickname = NULL;
-
-  if(SSL_GetClientAuthDataHook(model, SelectClientCert,
-                               (void *)connssl) != SECSuccess) {
-    result = CURLE_SSL_CERTPROBLEM;
-    goto error;
-  }
-
-  /* Is there an SSL filter "in front" of us or are we writing directly
-   * to the socket? */
-  if(connssl_next) {
-    /* The filter should be connected by now, with full handshake */
-    DEBUGASSERT(connssl_next->backend->handle);
-    DEBUGASSERT(ssl_connection_complete == connssl_next->state);
-    /* We tell our NSS instance to use do IO with the 'next' NSS
-    * instance. This NSS instance will take ownership of the next
-    * one, including its destruction. We therefore need to `disown`
-    * the next filter's handle, once import succeeds. */
-    nspr_io = connssl_next->backend->handle;
-    second_layer = TRUE;
-  }
-  else {
-    /* wrap OS file descriptor by NSPR's file descriptor abstraction */
-    nspr_io = PR_ImportTCPSocket(sockfd);
-    if(!nspr_io)
-      goto error;
-  }
-
-  /* create our own NSPR I/O layer */
-  nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods);
-  if(!nspr_io_stub) {
-    if(!second_layer)
-      PR_Close(nspr_io);
-    goto error;
-  }
-
-  /* make the per-connection data accessible from NSPR I/O callbacks */
-  nspr_io_stub->secret = (void *)connssl;
-
-  /* push our new layer to the NSPR I/O stack */
-  if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) {
-    if(!second_layer)
-      PR_Close(nspr_io);
-    PR_Close(nspr_io_stub);
-    goto error;
-  }
-
-  /* import our model socket onto the current I/O stack */
-  backend->handle = SSL_ImportFD(model, nspr_io);
-  if(!backend->handle) {
-    if(!second_layer)
-      PR_Close(nspr_io);
-    goto error;
-  }
-
-  PR_Close(model); /* We don't need this any more */
-  model = NULL;
-  if(connssl_next) /* steal the NSS handle we just imported successfully */
-    connssl_next->backend->handle = NULL;
-
-  /* This is the password associated with the cert that we're using */
-  if(ssl_config->key_passwd) {
-    SSL_SetPKCS11PinArg(backend->handle, ssl_config->key_passwd);
-  }
-
-#ifdef SSL_ENABLE_OCSP_STAPLING
-  if(conn_config->verifystatus) {
-    if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE)
-        != SECSuccess)
-      goto error;
-  }
-#endif
-
-#ifdef SSL_ENABLE_ALPN
-  if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN,
-                   connssl->alpn ? PR_TRUE : PR_FALSE)
-      != SECSuccess)
-    goto error;
-#endif
-
-#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
-  if(data->set.ssl.falsestart) {
-    if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE)
-        != SECSuccess)
-      goto error;
-
-    if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback,
-        data) != SECSuccess)
-      goto error;
-  }
-#endif
-
-#if defined(SSL_ENABLE_ALPN)
-  if(connssl->alpn) {
-    struct alpn_proto_buf proto;
-
-    result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
-    if(result || SSL_SetNextProtoNego(backend->handle, proto.data, proto.len)
-                   != SECSuccess) {
-      failf(data, "Error setting ALPN");
-      goto error;
-    }
-    Curl_alpn_to_proto_str(&proto, connssl->alpn);
-    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
-  }
-#endif
-
-
-  /* Force handshake on next I/O */
-  if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE)
-      != SECSuccess)
-    goto error;
-
-  /* propagate hostname to the TLS layer */
-  if(SSL_SetURL(backend->handle, snihost) != SECSuccess)
-    goto error;
-
-  /* prevent NSS from re-using the session for a different hostname */
-  if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess)
-    goto error;
-
-  return CURLE_OK;
-
-error:
-  if(model)
-    PR_Close(model);
-
-  return nss_fail_connect(cf, data, result);
-}
-
-static CURLcode nss_do_connect(struct Curl_cfilter *cf,
-                               struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  CURLcode result = CURLE_SSL_CONNECT_ERROR;
-  PRUint32 timeout;
-
-  /* check timeout situation */
-  const timediff_t time_left = Curl_timeleft(data, NULL, TRUE);
-  if(time_left < 0) {
-    failf(data, "timed out before SSL handshake");
-    result = CURLE_OPERATION_TIMEDOUT;
-    goto error;
-  }
-
-  DEBUGASSERT(backend);
-
-  /* Force the handshake now */
-  timeout = PR_MillisecondsToInterval((PRUint32) time_left);
-  if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) {
-    if(PR_GetError() == PR_WOULD_BLOCK_ERROR)
-      /* blocking direction is updated by nss_update_connecting_state() */
-      return CURLE_AGAIN;
-    else if(ssl_config->certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
-      result = CURLE_PEER_FAILED_VERIFICATION;
-    else if(ssl_config->certverifyresult)
-      result = CURLE_PEER_FAILED_VERIFICATION;
-    goto error;
-  }
-
-  result = display_conn_info(data, backend->handle);
-  if(result)
-    goto error;
-
-  if(conn_config->issuercert) {
-    SECStatus ret = SECFailure;
-    char *nickname = dup_nickname(data, conn_config->issuercert);
-    if(nickname) {
-      /* we support only nicknames in case of issuercert for now */
-      ret = check_issuer_cert(backend->handle, nickname);
-      free(nickname);
-    }
-
-    if(SECFailure == ret) {
-      infof(data, "SSL certificate issuer check failed");
-      result = CURLE_SSL_ISSUER_ERROR;
-      goto error;
-    }
-    else {
-      infof(data, "SSL certificate issuer check ok");
-    }
-  }
-
-  result = cmp_peer_pubkey(connssl,  Curl_ssl_cf_is_proxy(cf)?
-                           data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
-                           data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
-  if(result)
-    /* status already printed */
-    goto error;
-
-  return CURLE_OK;
-
-error:
-  return nss_fail_connect(cf, data, result);
-}
-
-static CURLcode nss_connect_common(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   bool *done)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  const bool blocking = (done == NULL);
-  CURLcode result;
-
-  if(connssl->state == ssl_connection_complete) {
-    if(!blocking)
-      *done = TRUE;
-    return CURLE_OK;
-  }
-
-  if(connssl->connecting_state == ssl_connect_1) {
-    result = nss_setup_connect(cf, data);
-    if(result)
-      /* we do not expect CURLE_AGAIN from nss_setup_connect() */
-      return result;
-
-    connssl->connecting_state = ssl_connect_2;
-  }
-
-  /* enable/disable blocking mode before handshake */
-  result = nss_set_blocking(cf, data, blocking);
-  if(result)
-    return result;
-
-  result = nss_do_connect(cf, data);
-  switch(result) {
-  case CURLE_OK:
-    break;
-  case CURLE_AGAIN:
-    /* CURLE_AGAIN in non-blocking mode is not an error */
-    if(!blocking)
-      return CURLE_OK;
-    else
-      return result;
-  default:
-    return result;
-  }
-
-  if(blocking) {
-    /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */
-    result = nss_set_blocking(cf, data, /* blocking */ FALSE);
-    if(result)
-      return result;
-  }
-  else
-    /* signal completed SSL handshake */
-    *done = TRUE;
-
-  connssl->state = ssl_connection_complete;
-
-  /* ssl_connect_done is never used outside, go back to the initial state */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-static CURLcode nss_connect(struct Curl_cfilter *cf,
-                            struct Curl_easy *data)
-{
-  return nss_connect_common(cf, data, /* blocking */ NULL);
-}
-
-static CURLcode nss_connect_nonblocking(struct Curl_cfilter *cf,
-                                        struct Curl_easy *data,
-                                        bool *done)
-{
-  return nss_connect_common(cf, data, done);
-}
-
-static ssize_t nss_send(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,    /* transfer */
-                        const void *mem,           /* send this data */
-                        size_t len,                /* amount to write */
-                        CURLcode *curlcode)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
-  ssize_t rc;
-
-  (void)data;
-  DEBUGASSERT(backend);
-
-  /* The SelectClientCert() hook uses this for infof() and failf() but the
-     handle stored in nss_setup_connect() could have already been freed. */
-  backend->data = data;
-
-  rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT);
-  if(rc < 0) {
-    PRInt32 err = PR_GetError();
-    if(err == PR_WOULD_BLOCK_ERROR)
-      *curlcode = CURLE_AGAIN;
-    else {
-      /* print the error number and error string */
-      const char *err_name = nss_error_to_name(err);
-      infof(data, "SSL write: error %d (%s)", err, err_name);
-
-      /* print a human-readable message describing the error if available */
-      nss_print_error_message(data, err);
-
-      *curlcode = (is_cc_error(err))
-        ? CURLE_SSL_CERTPROBLEM
-        : CURLE_SEND_ERROR;
-    }
-
-    return -1;
-  }
-
-  return rc; /* number of bytes */
-}
-
-static bool
-nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  PRFileDesc *fd = connssl->backend->handle->lower;
-  char buf;
-
-  (void) data;
-
-  /* Returns true in case of error to force reading. */
-  return PR_Recv(fd, (void *) &buf, 1, PR_MSG_PEEK, PR_INTERVAL_NO_WAIT) != 0;
-}
-
-static ssize_t nss_recv(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,    /* transfer */
-                        char *buf,             /* store read data here */
-                        size_t buffersize,     /* max amount to read */
-                        CURLcode *curlcode)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
-  ssize_t nread;
-
-  (void)data;
-  DEBUGASSERT(backend);
-
-  /* The SelectClientCert() hook uses this for infof() and failf() but the
-     handle stored in nss_setup_connect() could have already been freed. */
-  backend->data = data;
-
-  nread = PR_Recv(backend->handle, buf, (int)buffersize, 0,
-                  PR_INTERVAL_NO_WAIT);
-  if(nread < 0) {
-    /* failed SSL read */
-    PRInt32 err = PR_GetError();
-
-    if(err == PR_WOULD_BLOCK_ERROR)
-      *curlcode = CURLE_AGAIN;
-    else {
-      /* print the error number and error string */
-      const char *err_name = nss_error_to_name(err);
-      infof(data, "SSL read: errno %d (%s)", err, err_name);
-
-      /* print a human-readable message describing the error if available */
-      nss_print_error_message(data, err);
-
-      *curlcode = (is_cc_error(err))
-        ? CURLE_SSL_CERTPROBLEM
-        : CURLE_RECV_ERROR;
-    }
-
-    return -1;
-  }
-
-  return nread;
-}
-
-static size_t nss_version(char *buffer, size_t size)
-{
-  return msnprintf(buffer, size, "NSS/%s", NSS_GetVersion());
-}
-
-/* data might be NULL */
-static int Curl_nss_seed(struct Curl_easy *data)
-{
-  /* make sure that NSS is initialized */
-  return !!Curl_nss_force_init(data);
-}
-
-/* data might be NULL */
-static CURLcode nss_random(struct Curl_easy *data,
-                           unsigned char *entropy,
-                           size_t length)
-{
-  Curl_nss_seed(data);  /* Initiate the seed if not already done */
-
-  if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length)))
-    /* signal a failure */
-    return CURLE_FAILED_INIT;
-
-  return CURLE_OK;
-}
-
-static CURLcode nss_sha256sum(const unsigned char *tmp, /* input */
-                              size_t tmplen,
-                              unsigned char *sha256sum, /* output */
-                              size_t sha256len)
-{
-  PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256);
-  unsigned int SHA256out;
-
-  if(!SHA256pw)
-    return CURLE_NOT_BUILT_IN;
-
-  PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen));
-  PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len));
-  PK11_DestroyContext(SHA256pw, PR_TRUE);
-
-  return CURLE_OK;
-}
-
-static bool nss_cert_status_request(void)
-{
-#ifdef SSL_ENABLE_OCSP_STAPLING
-  return TRUE;
-#else
-  return FALSE;
-#endif
-}
-
-static bool nss_false_start(void)
-{
-#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
-  return TRUE;
-#else
-  return FALSE;
-#endif
-}
-
-static void *nss_get_internals(struct ssl_connect_data *connssl,
-                               CURLINFO info UNUSED_PARAM)
-{
-  struct ssl_backend_data *backend = connssl->backend;
-  (void)info;
-  DEBUGASSERT(backend);
-  return backend->handle;
-}
-
-static bool nss_attach_data(struct Curl_cfilter *cf,
-                            struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-
-  if(!connssl->backend->data)
-    connssl->backend->data = data;
-  return TRUE;
-}
-
-static void nss_detach_data(struct Curl_cfilter *cf,
-                            struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-
-  if(connssl->backend->data == data)
-    connssl->backend->data = NULL;
-}
-
-const struct Curl_ssl Curl_ssl_nss = {
-  { CURLSSLBACKEND_NSS, "nss" }, /* info */
-
-  SSLSUPP_CA_PATH |
-  SSLSUPP_CERTINFO |
-  SSLSUPP_PINNEDPUBKEY |
-  SSLSUPP_HTTPS_PROXY,
-
-  sizeof(struct ssl_backend_data),
-
-  nss_init,                     /* init */
-  nss_cleanup,                  /* cleanup */
-  nss_version,                  /* version */
-  Curl_none_check_cxn,          /* check_cxn */
-  /* NSS has no shutdown function provided and thus always fail */
-  Curl_none_shutdown,           /* shutdown */
-  nss_data_pending,             /* data_pending */
-  nss_random,                   /* random */
-  nss_cert_status_request,      /* cert_status_request */
-  nss_connect,                  /* connect */
-  nss_connect_nonblocking,      /* connect_nonblocking */
-  Curl_ssl_get_select_socks,             /* getsock */
-  nss_get_internals,            /* get_internals */
-  nss_close,                    /* close_one */
-  Curl_none_close_all,          /* close_all */
-  /* NSS has its own session ID cache */
-  Curl_none_session_free,       /* session_free */
-  Curl_none_set_engine,         /* set_engine */
-  Curl_none_set_engine_default, /* set_engine_default */
-  Curl_none_engines_list,       /* engines_list */
-  nss_false_start,              /* false_start */
-  nss_sha256sum,                /* sha256sum */
-  nss_attach_data,              /* associate_connection */
-  nss_detach_data,              /* disassociate_connection */
-  NULL,                         /* free_multi_ssl_backend_data */
-  nss_recv,                     /* recv decrypted data */
-  nss_send,                     /* send data to encrypt */
-};
-
-#endif /* USE_NSS */
diff --git a/Utilities/cmcurl/lib/vtls/nssg.h b/Utilities/cmcurl/lib/vtls/nssg.h
deleted file mode 100644
index ad7eef5..0000000
--- a/Utilities/cmcurl/lib/vtls/nssg.h
+++ /dev/null
@@ -1,41 +0,0 @@
-#ifndef HEADER_CURL_NSSG_H
-#define HEADER_CURL_NSSG_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#ifdef USE_NSS
-/*
- * This header should only be needed to get included by vtls.c and nss.c
- */
-
-#include "urldata.h"
-
-/* initialize NSS library if not already */
-CURLcode Curl_nss_force_init(struct Curl_easy *data);
-
-extern const struct Curl_ssl Curl_ssl_nss;
-
-#endif /* USE_NSS */
-#endif /* HEADER_CURL_NSSG_H */
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c
index 1c6c786..15d84ed 100644
--- a/Utilities/cmcurl/lib/vtls/openssl.c
+++ b/Utilities/cmcurl/lib/vtls/openssl.c
@@ -190,11 +190,12 @@
  * Whether SSL_CTX_set_keylog_callback is available.
  * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
  * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
- * LibreSSL: unsupported in at least 2.7.2 (explicitly check for it since it
- *           lies and pretends to be OpenSSL 2.0.0).
+ * LibreSSL: supported since 3.5.0 (released 2022-02-24)
  */
 #if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
      !defined(LIBRESSL_VERSION_NUMBER)) || \
+    (defined(LIBRESSL_VERSION_NUMBER) && \
+     LIBRESSL_VERSION_NUMBER >= 0x3050000fL) || \
     defined(OPENSSL_IS_BORINGSSL)
 #define HAVE_KEYLOG_CALLBACK
 #endif
@@ -202,11 +203,13 @@
 /* Whether SSL_CTX_set_ciphersuites is available.
  * OpenSSL: supported since 1.1.1 (commit a53b5be6a05)
  * BoringSSL: no
- * LibreSSL: no
+ * LibreSSL: supported since 3.4.1 (released 2021-10-14)
  */
-#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \
-     !defined(LIBRESSL_VERSION_NUMBER) &&       \
-     !defined(OPENSSL_IS_BORINGSSL))
+#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+      !defined(LIBRESSL_VERSION_NUMBER)) || \
+     (defined(LIBRESSL_VERSION_NUMBER) && \
+      LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \
+    !defined(OPENSSL_IS_BORINGSSL)
   #define HAVE_SSL_CTX_SET_CIPHERSUITES
   #if !defined(OPENSSL_IS_AWSLC)
     #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
@@ -268,7 +271,7 @@
 #define HAVE_OPENSSL_VERSION
 #endif
 
-#ifdef OPENSSL_IS_BORINGSSL
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
 typedef uint32_t sslerr_t;
 #else
 typedef unsigned long sslerr_t;
@@ -296,7 +299,7 @@
 #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
 #endif /* !LIBRESSL_VERSION_NUMBER */
 
-struct ssl_backend_data {
+struct ossl_ssl_backend_data {
   /* these ones requires specific SSL-types */
   SSL_CTX* ctx;
   SSL*     handle;
@@ -471,7 +474,9 @@
 
       X509_get0_signature(&psig, &sigalg, x);
       if(sigalg) {
-        i2a_ASN1_OBJECT(mem, sigalg->algorithm);
+        const ASN1_OBJECT *sigalgoid = NULL;
+        X509_ALGOR_get0(&sigalgoid, NULL, NULL, sigalg);
+        i2a_ASN1_OBJECT(mem, sigalgoid);
         push_certinfo("Signature Algorithm", i);
       }
 
@@ -666,7 +671,7 @@
 #define BIO_set_shutdown(x,v)      ((x)->shutdown=(v))
 #endif /* USE_PRE_1_1_API */
 
-static int bio_cf_create(BIO *bio)
+static int ossl_bio_cf_create(BIO *bio)
 {
   BIO_set_shutdown(bio, 1);
   BIO_set_init(bio, 1);
@@ -677,14 +682,14 @@
   return 1;
 }
 
-static int bio_cf_destroy(BIO *bio)
+static int ossl_bio_cf_destroy(BIO *bio)
 {
   if(!bio)
     return 0;
   return 1;
 }
 
-static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
+static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   long ret = 1;
@@ -718,20 +723,22 @@
   return ret;
 }
 
-static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
+static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result = CURLE_SEND_ERROR;
 
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
-  DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d",
-                blen, (int)nwritten, result));
+  CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d",
+              blen, (int)nwritten, result);
   BIO_clear_retry_flags(bio);
-  connssl->backend->io_result = result;
+  backend->io_result = result;
   if(nwritten < 0) {
     if(CURLE_AGAIN == result)
       BIO_set_retry_write(bio);
@@ -739,10 +746,12 @@
   return (int)nwritten;
 }
 
-static int bio_cf_in_read(BIO *bio, char *buf, int blen)
+static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result = CURLE_RECV_ERROR;
@@ -753,10 +762,10 @@
     return 0;
 
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
-  DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d",
-                blen, (int)nread, result));
+  CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d",
+              blen, (int)nread, result);
   BIO_clear_retry_flags(bio);
-  connssl->backend->io_result = result;
+  backend->io_result = result;
   if(nread < 0) {
     if(CURLE_AGAIN == result)
       BIO_set_retry_read(bio);
@@ -764,13 +773,13 @@
 
   /* Before returning server replies to the SSL instance, we need
    * to have setup the x509 store or verification will fail. */
-  if(!connssl->backend->x509_store_setup) {
-    result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx);
+  if(!backend->x509_store_setup) {
+    result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
     if(result) {
-      connssl->backend->io_result = result;
+      backend->io_result = result;
       return -1;
     }
-    connssl->backend->x509_store_setup = TRUE;
+    backend->x509_store_setup = TRUE;
   }
 
   return (int)nread;
@@ -778,42 +787,42 @@
 
 #if USE_PRE_1_1_API
 
-static BIO_METHOD bio_cf_meth_1_0 = {
+static BIO_METHOD ossl_bio_cf_meth_1_0 = {
   BIO_TYPE_MEM,
   "OpenSSL CF BIO",
-  bio_cf_out_write,
-  bio_cf_in_read,
+  ossl_bio_cf_out_write,
+  ossl_bio_cf_in_read,
   NULL,                    /* puts is never called */
   NULL,                    /* gets is never called */
-  bio_cf_ctrl,
-  bio_cf_create,
-  bio_cf_destroy,
+  ossl_bio_cf_ctrl,
+  ossl_bio_cf_create,
+  ossl_bio_cf_destroy,
   NULL
 };
 
-static BIO_METHOD *bio_cf_method_create(void)
+static BIO_METHOD *ossl_bio_cf_method_create(void)
 {
-  return &bio_cf_meth_1_0;
+  return &ossl_bio_cf_meth_1_0;
 }
 
-#define bio_cf_method_free(m) Curl_nop_stmt
+#define ossl_bio_cf_method_free(m) Curl_nop_stmt
 
 #else
 
-static BIO_METHOD *bio_cf_method_create(void)
+static BIO_METHOD *ossl_bio_cf_method_create(void)
 {
   BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO");
   if(m) {
-    BIO_meth_set_write(m, &bio_cf_out_write);
-    BIO_meth_set_read(m, &bio_cf_in_read);
-    BIO_meth_set_ctrl(m, &bio_cf_ctrl);
-    BIO_meth_set_create(m, &bio_cf_create);
-    BIO_meth_set_destroy(m, &bio_cf_destroy);
+    BIO_meth_set_write(m, &ossl_bio_cf_out_write);
+    BIO_meth_set_read(m, &ossl_bio_cf_in_read);
+    BIO_meth_set_ctrl(m, &ossl_bio_cf_ctrl);
+    BIO_meth_set_create(m, &ossl_bio_cf_create);
+    BIO_meth_set_destroy(m, &ossl_bio_cf_destroy);
   }
   return m;
 }
 
-static void bio_cf_method_free(BIO_METHOD *m)
+static void ossl_bio_cf_method_free(BIO_METHOD *m)
 {
   if(m)
     BIO_meth_free(m);
@@ -1000,20 +1009,6 @@
     return CURLE_OK;
 #endif
 
-#if defined(HAVE_RAND_EGD) && defined(EGD_SOCKET)
-  /* available in OpenSSL 0.9.5 and later */
-  /* EGD_SOCKET is set at configure time or not at all */
-  {
-    /* If there's an option and a define, the option overrides the
-       define */
-    int ret = RAND_egd(EGD_SOCKET);
-    if(-1 != ret) {
-      if(rand_enough())
-        return CURLE_OK;
-    }
-  }
-#endif
-
   /* fallback to a custom seeding of the PRNG using a hash based on a current
      time */
   do {
@@ -1566,11 +1561,9 @@
         UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
         UI_method_set_reader(ui_method, ssl_ui_reader);
         UI_method_set_writer(ui_method, ssl_ui_writer);
-        /* the typecast below was added to please mingw32 */
-        priv_key = (EVP_PKEY *)
-          ENGINE_load_private_key(data->state.engine, key_file,
-                                  ui_method,
-                                  key_passwd);
+        priv_key = ENGINE_load_private_key(data->state.engine, key_file,
+                                           ui_method,
+                                           key_passwd);
         UI_destroy_method(ui_method);
         if(!priv_key) {
           failf(data, "failed to load private key from crypto engine");
@@ -1709,7 +1702,7 @@
 static int ossl_init(void)
 {
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
-  !defined(LIBRESSL_VERSION_NUMBER)
+  (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
   const uint64_t flags =
 #ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN
     /* not present in BoringSSL */
@@ -1885,19 +1878,53 @@
 static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
 
   if(backend->handle) {
     if(cf->next && cf->next->connected) {
-      char buf[32];
+      char buf[1024];
+      int nread, err;
+      long sslerr;
+
       /* Maybe the server has already sent a close notify alert.
          Read it to avoid an RST on the TCP connection. */
       (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
+      ERR_clear_error();
+      if(SSL_shutdown(backend->handle) == 1) {
+        CURL_TRC_CF(data, cf, "SSL shutdown finished");
+      }
+      else {
+        nread = SSL_read(backend->handle, buf, (int)sizeof(buf));
+        err = SSL_get_error(backend->handle, nread);
+        switch(err) {
+        case SSL_ERROR_NONE: /* this is not an error */
+        case SSL_ERROR_ZERO_RETURN: /* no more data */
+          CURL_TRC_CF(data, cf, "SSL shutdown, EOF from server");
+          break;
+        case SSL_ERROR_WANT_READ:
+          /* SSL has send its notify and now wants to read the reply
+           * from the server. We are not really interested in that. */
+          CURL_TRC_CF(data, cf, "SSL shutdown sent");
+          break;
+        case SSL_ERROR_WANT_WRITE:
+          CURL_TRC_CF(data, cf, "SSL shutdown send blocked");
+          break;
+        default:
+          sslerr = ERR_get_error();
+          CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s', errno %d",
+                      (sslerr ?
+                       ossl_strerror(sslerr, buf, sizeof(buf)) :
+                       SSL_ERROR_to_str(err)),
+                      SOCKERRNO);
+          break;
+        }
+      }
 
-      (void)SSL_shutdown(backend->handle);
+      ERR_clear_error();
       SSL_set_connect_state(backend->handle);
     }
 
@@ -1910,7 +1937,7 @@
     backend->x509_store_setup = FALSE;
   }
   if(backend->bio_method) {
-    bio_cf_method_free(backend->bio_method);
+    ossl_bio_cf_method_free(backend->bio_method);
     backend->bio_method = NULL;
   }
 }
@@ -1931,7 +1958,8 @@
   int buffsize;
   int err;
   bool done = FALSE;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   int loop = 10;
 
   DEBUGASSERT(backend);
@@ -2322,14 +2350,19 @@
 {
   struct ssl_connect_data *connssl = cf->ctx;
   int i, ocsp_status;
+#if defined(OPENSSL_IS_AWSLC)
+  const uint8_t *status;
+#else
   unsigned char *status;
+#endif
   const unsigned char *p;
   CURLcode result = CURLE_OK;
   OCSP_RESPONSE *rsp = NULL;
   OCSP_BASICRESP *br = NULL;
   X509_STORE     *st = NULL;
   STACK_OF(X509) *ch = NULL;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   X509 *cert;
   OCSP_CERTID *id = NULL;
   int cert_status, crl_reason;
@@ -2419,7 +2452,7 @@
     goto end;
   }
 
-  for(i = 0; i < sk_X509_num(ch); i++) {
+  for(i = 0; i < (int)sk_X509_num(ch); i++) {
     X509 *issuer = sk_X509_value(ch, i);
     if(X509_check_issued(issuer, cert) == X509_V_OK) {
       id = OCSP_cert_to_id(EVP_sha1(), cert, issuer);
@@ -2729,7 +2762,7 @@
 
 #ifdef HAS_MODERN_SET_PROTO_VER
 static CURLcode
-set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx)
+ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   /* first, TLS min version... */
@@ -2826,9 +2859,9 @@
 
 #if !defined(HAS_MODERN_SET_PROTO_VER)
 static CURLcode
-set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
-                               struct Curl_cfilter *cf,
-                               struct Curl_easy *data)
+ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
+                                       struct Curl_cfilter *cf,
+                                       struct Curl_easy *data)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
@@ -2841,8 +2874,10 @@
 #ifdef TLS1_3_VERSION
   {
     struct ssl_connect_data *connssl = cf->ctx;
-    DEBUGASSERT(connssl->backend);
-    SSL_CTX_set_max_proto_version(connssl->backend->ctx, TLS1_3_VERSION);
+    struct ossl_ssl_backend_data *backend =
+      (struct ossl_ssl_backend_data *)connssl->backend;
+    DEBUGASSERT(backend);
+    SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
     *ctx_options |= SSL_OP_NO_TLSv1_2;
   }
 #else
@@ -3447,7 +3482,8 @@
   const char * const ssl_cert_type = ssl_config->cert_type;
   const bool verifypeer = conn_config->verifypeer;
   char error_buffer[256];
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
   DEBUGASSERT(backend);
@@ -3589,9 +3625,9 @@
     ctx_options |= SSL_OP_NO_SSLv3;
 
 #if HAS_MODERN_SET_PROTO_VER /* 1.1.0 */
-    result = set_ssl_version_min_max(cf, backend->ctx);
+    result = ossl_set_ssl_version_min_max(cf, backend->ctx);
 #else
-    result = set_ssl_version_min_max_legacy(&ctx_options, cf, data);
+    result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data);
 #endif
     if(result != CURLE_OK)
       return result;
@@ -3722,6 +3758,15 @@
 
   /* give application a chance to interfere with SSL set up. */
   if(data->set.ssl.fsslctx) {
+    /* When a user callback is installed to modify the SSL_CTX,
+     * we need to do the full initialization before calling it.
+     * See: #11800 */
+    if(!backend->x509_store_setup) {
+      result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+      if(result)
+        return result;
+      backend->x509_store_setup = TRUE;
+    }
     Curl_set_in_callback(data, true);
     result = (*data->set.ssl.fsslctx)(data, backend->ctx,
                                       data->set.ssl.fsslctxp);
@@ -3785,12 +3830,12 @@
         return CURLE_SSL_CONNECT_ERROR;
       }
       /* Informational message */
-      infof(data, "SSL re-using session ID");
+      infof(data, "SSL reusing session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
 
-  backend->bio_method = bio_cf_method_create();
+  backend->bio_method = ossl_bio_cf_method_create();
   if(!backend->bio_method)
     return CURLE_OUT_OF_MEMORY;
   bio = BIO_new(backend->bio_method);
@@ -3820,7 +3865,8 @@
 {
   int err;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
               || ssl_connect_2_reading == connssl->connecting_state
@@ -3869,7 +3915,13 @@
       return CURLE_OK;
     }
 #endif
-    else if(backend->io_result == CURLE_AGAIN) {
+#ifdef SSL_ERROR_WANT_RETRY_VERIFY
+    if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
+      connssl->connecting_state = ssl_connect_2;
+      return CURLE_OK;
+    }
+#endif
+    if(backend->io_result == CURLE_AGAIN) {
       return CURLE_OK;
     }
     else {
@@ -3909,11 +3961,7 @@
              error_buffer */
           strcpy(error_buffer, "SSL certificate verification failed");
       }
-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L &&   \
-     !defined(LIBRESSL_VERSION_NUMBER) &&       \
-     !defined(OPENSSL_IS_BORINGSSL) &&          \
-     !defined(OPENSSL_IS_AWSLC))
-
+#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
       /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
          OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
       else if((lib == ERR_LIB_SSL) &&
@@ -3983,8 +4031,8 @@
  * Heavily modified from:
  * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
  */
-static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
-                                    const char *pinnedpubkey)
+static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
+                                         const char *pinnedpubkey)
 {
   /* Scratch */
   int len1 = 0, len2 = 0;
@@ -4062,7 +4110,8 @@
   char buffer[2048];
   const char *ptr;
   BIO *mem = BIO_new(BIO_s_mem());
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -4077,7 +4126,7 @@
 
   if(data->set.ssl.certinfo)
     /* asked to gather certificate info */
-    (void)Curl_ossl_certchain(data, connssl->backend->handle);
+    (void)Curl_ossl_certchain(data, backend->handle);
 
   backend->server_cert = SSL_get1_peer_certificate(backend->handle);
   if(!backend->server_cert) {
@@ -4245,7 +4294,7 @@
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
     data->set.str[STRING_SSL_PINNEDPUBLICKEY];
   if(!result && ptr) {
-    result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
+    result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
     if(result)
       failf(data, "SSL: public key does not match pinned public key");
   }
@@ -4414,11 +4463,13 @@
 static bool ossl_data_pending(struct Curl_cfilter *cf,
                               const struct Curl_easy *data)
 {
-  struct ssl_connect_data *ctx = cf->ctx;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
-  DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->handle && SSL_pending(ctx->backend->handle))
+  DEBUGASSERT(connssl && backend);
+  if(backend->handle && SSL_pending(backend->handle))
     return TRUE;
   return FALSE;
 }
@@ -4437,7 +4488,8 @@
   int memlen;
   int rc;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
@@ -4533,7 +4585,8 @@
   int buffsize;
   struct connectdata *conn = cf->conn;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
@@ -4734,7 +4787,10 @@
   mdctx = EVP_MD_CTX_create();
   if(!mdctx)
     return CURLE_OUT_OF_MEMORY;
-  EVP_DigestInit(mdctx, EVP_sha256());
+  if(!EVP_DigestInit(mdctx, EVP_sha256())) {
+    EVP_MD_CTX_destroy(mdctx);
+    return CURLE_FAILED_INIT;
+  }
   EVP_DigestUpdate(mdctx, tmp, tmplen);
   EVP_DigestFinal_ex(mdctx, sha256sum, &len);
   EVP_MD_CTX_destroy(mdctx);
@@ -4756,7 +4812,8 @@
                                 CURLINFO info)
 {
   /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   DEBUGASSERT(backend);
   return info == CURLINFO_TLS_SESSION ?
     (void *)backend->ctx : (void *)backend->handle;
@@ -4789,7 +4846,7 @@
 #endif
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct ossl_ssl_backend_data),
 
   ossl_init,                /* init */
   ossl_cleanup,             /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/rustls.c b/Utilities/cmcurl/lib/vtls/rustls.c
index 097c58c..a3e9d96 100644
--- a/Utilities/cmcurl/lib/vtls/rustls.c
+++ b/Utilities/cmcurl/lib/vtls/rustls.c
@@ -40,7 +40,7 @@
 #include "strerror.h"
 #include "multiif.h"
 
-struct ssl_backend_data
+struct rustls_ssl_backend_data
 {
   const struct rustls_client_config *config;
   struct rustls_connection *conn;
@@ -67,10 +67,12 @@
 cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct rustls_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  return ctx->backend->data_pending;
+  backend = (struct rustls_ssl_backend_data *)ctx->backend;
+  return backend->data_pending;
 }
 
 static CURLcode
@@ -102,10 +104,6 @@
       ret = EINVAL;
   }
   *out_n = (int)nread;
-  /*
-  DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d",
-                len, nread, result));
-  */
   return ret;
 }
 
@@ -126,7 +124,7 @@
   }
   *out_n = (int)nwritten;
   /*
-  DEBUGF(LOG_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d",
+  CURL_TRC_CFX(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d",
                 len, nwritten, result));
   */
   return ret;
@@ -136,7 +134,8 @@
                              struct Curl_easy *data, CURLcode *err)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct io_ctx io_ctx;
   size_t tls_bytes_read = 0;
   rustls_io_result io_error;
@@ -191,7 +190,8 @@
             char *plainbuf, size_t plainlen, CURLcode *err)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   size_t n = 0;
   size_t plain_bytes_copied = 0;
@@ -263,8 +263,8 @@
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d",
-                plainlen, nread, *err));
+  CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d",
+              plainlen, nread, *err);
   return nread;
 }
 
@@ -283,7 +283,8 @@
         const void *plainbuf, size_t plainlen, CURLcode *err)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   struct io_ctx io_ctx;
   size_t plainwritten = 0;
@@ -297,7 +298,7 @@
   DEBUGASSERT(backend);
   rconn = backend->conn;
 
-  DEBUGF(LOG_CF(data, cf, "cf_send: %ld plain bytes", plainlen));
+  CURL_TRC_CF(data, cf, "cf_send: %ld plain bytes", plainlen);
 
   io_ctx.cf = cf;
   io_ctx.data = data;
@@ -322,8 +323,8 @@
     io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx,
                                            &tlswritten);
     if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
-      DEBUGF(LOG_CF(data, cf, "cf_send: EAGAIN after %zu bytes",
-                    tlswritten_total));
+      CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes",
+                  tlswritten_total);
       *err = CURLE_AGAIN;
       return -1;
     }
@@ -339,7 +340,7 @@
       *err = CURLE_WRITE_ERROR;
       return -1;
     }
-    DEBUGF(LOG_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten));
+    CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
     tlswritten_total += tlswritten;
   }
 
@@ -373,7 +374,7 @@
 
 static CURLcode
 cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
-                struct ssl_backend_data *const backend)
+                struct rustls_ssl_backend_data *const backend)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
@@ -491,7 +492,8 @@
 {
   struct ssl_connect_data *const connssl = cf->ctx;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   CURLcode tmperr = CURLE_OK;
   int result;
@@ -504,7 +506,8 @@
   DEBUGASSERT(backend);
 
   if(ssl_connection_none == connssl->state) {
-    result = cr_init_backend(cf, data, connssl->backend);
+    result = cr_init_backend(cf, data,
+               (struct rustls_ssl_backend_data *)connssl->backend);
     if(result != CURLE_OK) {
       return result;
     }
@@ -594,7 +597,8 @@
 {
   struct ssl_connect_data *const connssl = cf->ctx;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
 
   (void)data;
@@ -617,7 +621,8 @@
 cr_get_internals(struct ssl_connect_data *connssl,
                  CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct rustls_ssl_backend_data *backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   DEBUGASSERT(backend);
   return &backend->conn;
 }
@@ -626,7 +631,8 @@
 cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct rustls_ssl_backend_data *backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   CURLcode tmperr = CURLE_OK;
   ssize_t n = 0;
 
@@ -659,7 +665,7 @@
   SSLSUPP_CAINFO_BLOB |            /* supports */
   SSLSUPP_TLS13_CIPHERSUITES |
   SSLSUPP_HTTPS_PROXY,
-  sizeof(struct ssl_backend_data),
+  sizeof(struct rustls_ssl_backend_data),
 
   Curl_none_init,                  /* init */
   Curl_none_cleanup,               /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/schannel.c b/Utilities/cmcurl/lib/vtls/schannel.c
index 513811d..410a5c4 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.c
+++ b/Utilities/cmcurl/lib/vtls/schannel.c
@@ -33,13 +33,12 @@
 
 #ifdef USE_SCHANNEL
 
-#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
-
 #ifndef USE_WINDOWS_SSPI
 #  error "Can't compile SCHANNEL support without SSPI."
 #endif
 
 #include "schannel.h"
+#include "schannel_int.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "strcase.h"
@@ -69,22 +68,6 @@
 #  define HAS_ALPN 1
 #endif
 
-#ifndef UNISP_NAME_A
-#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider"
-#endif
-
-#ifndef UNISP_NAME_W
-#define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider"
-#endif
-
-#ifndef UNISP_NAME
-#ifdef UNICODE
-#define UNISP_NAME  UNISP_NAME_W
-#else
-#define UNISP_NAME  UNISP_NAME_A
-#endif
-#endif
-
 #ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
 #define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
 #endif
@@ -109,13 +92,6 @@
 #define BCRYPT_SHA384_ALGORITHM L"SHA384"
 #endif
 
-/* Workaround broken compilers like MinGW.
-   Return the number of elements in a statically sized array.
-*/
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
 #ifdef HAS_CLIENT_CERT_PATH
 #ifdef UNICODE
 #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W
@@ -124,18 +100,6 @@
 #endif
 #endif
 
-#ifndef SP_PROT_SSL2_CLIENT
-#define SP_PROT_SSL2_CLIENT             0x00000008
-#endif
-
-#ifndef SP_PROT_SSL3_CLIENT
-#define SP_PROT_SSL3_CLIENT             0x00000008
-#endif
-
-#ifndef SP_PROT_TLS1_CLIENT
-#define SP_PROT_TLS1_CLIENT             0x00000080
-#endif
-
 #ifndef SP_PROT_TLS1_0_CLIENT
 #define SP_PROT_TLS1_0_CLIENT           SP_PROT_TLS1_CLIENT
 #endif
@@ -176,19 +140,13 @@
 #  define CALG_SHA_256 0x0000800c
 #endif
 
-/* Work around typo in classic MinGW's w32api up to version 5.0,
-   see https://osdn.net/projects/mingw/ticket/38391 */
-#if !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH)
-#define ALG_CLASS_DHASH ALG_CLASS_HASH
-#endif
-
 #ifndef PKCS12_NO_PERSIST_KEY
 #define PKCS12_NO_PERSIST_KEY 0x00008000
 #endif
 
-static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    const char *pinnedpubkey);
+static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data,
+                                             const char *pinnedpubkey);
 
 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
                           void *BufDataPtr, unsigned long BufByteSize)
@@ -207,9 +165,9 @@
 }
 
 static CURLcode
-set_ssl_version_min_max(DWORD *enabled_protocols,
-                        struct Curl_cfilter *cf,
-                        struct Curl_easy *data)
+schannel_set_ssl_version_min_max(DWORD *enabled_protocols,
+                                 struct Curl_cfilter *cf,
+                                 struct Curl_easy *data)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
@@ -500,7 +458,8 @@
   DWORD flags = 0;
   DWORD enabled_protocols = 0;
 
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)(connssl->backend);
 
   DEBUGASSERT(backend);
 
@@ -563,7 +522,7 @@
   case CURL_SSLVERSION_TLSv1_2:
   case CURL_SSLVERSION_TLSv1_3:
   {
-    result = set_ssl_version_min_max(&enabled_protocols, cf, data);
+    result = schannel_set_ssl_version_min_max(&enabled_protocols, cf, data);
     if(result != CURLE_OK)
       return result;
     break;
@@ -769,7 +728,7 @@
   }
 #endif
 
-  /* allocate memory for the re-usable credential handle */
+  /* allocate memory for the reusable credential handle */
   backend->cred = (struct Curl_schannel_cred *)
     calloc(1, sizeof(struct Curl_schannel_cred));
   if(!backend->cred) {
@@ -793,8 +752,11 @@
   backend->cred->client_cert_store = client_cert_store;
 #endif
 
-  /* Windows 10, 1809 (a.k.a. Windows 10 build 17763) */
-  if(curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
+  /* We support TLS 1.3 starting in Windows 10 version 1809 (OS build 17763) as
+     long as the user did not set a legacy algorithm list
+     (CURLOPT_SSL_CIPHER_LIST). */
+  if(!conn_config->cipher_list &&
+     curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
                                   VERSION_GREATER_THAN_EQUAL)) {
 
     char *ciphers13 = 0;
@@ -807,9 +769,9 @@
 
     SCH_CREDENTIALS credentials = { 0 };
     TLS_PARAMETERS tls_parameters = { 0 };
-    CRYPTO_SETTINGS crypto_settings[4] = { 0 };
-    UNICODE_STRING blocked_ccm_modes[1] = { 0 };
-    UNICODE_STRING blocked_gcm_modes[1] = { 0 };
+    CRYPTO_SETTINGS crypto_settings[4] = { { 0 } };
+    UNICODE_STRING blocked_ccm_modes[1] = { { 0 } };
+    UNICODE_STRING blocked_gcm_modes[1] = { { 0 } };
 
     int crypto_settings_idx = 0;
 
@@ -844,7 +806,7 @@
 
         /* reject too-long cipher names */
         if(n > (LONGEST_ALG_ID - 1)) {
-          failf(data, "Cipher name too long, not checked.");
+          failf(data, "schannel: Cipher name too long, not checked");
           return CURLE_SSL_CIPHER;
         }
 
@@ -872,7 +834,7 @@
           disable_aes_ccm_sha256 = FALSE;
         }
         else {
-          failf(data, "Passed in an unknown TLS 1.3 cipher.");
+          failf(data, "schannel: Unknown TLS 1.3 cipher: %s", tmp);
           return CURLE_SSL_CIPHER;
         }
 
@@ -887,7 +849,7 @@
     if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256
        && disable_chacha_poly && disable_aes_ccm_8_sha256
        && disable_aes_ccm_sha256) {
-      failf(data, "All available TLS 1.3 ciphers were disabled.");
+      failf(data, "schannel: All available TLS 1.3 ciphers were disabled");
       return CURLE_SSL_CIPHER;
     }
 
@@ -1010,7 +972,9 @@
                                          &backend->cred->time_stamp);
   }
   else {
-    /* Pre-Windows 10 1809 */
+    /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS
+       doesn't document it, currently Schannel will not negotiate TLS 1.3 when
+       SCHANNEL_CRED is used. */
     ALG_ID algIds[NUM_CIPHERS];
     char *ciphers = conn_config->cipher_list;
     SCHANNEL_CRED schannel_cred = { 0 };
@@ -1019,9 +983,20 @@
     schannel_cred.grbitEnabledProtocols = enabled_protocols;
 
     if(ciphers) {
+      if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) {
+        infof(data, "schannel: WARNING: This version of Schannel may "
+              "negotiate a less-secure TLS version than TLS 1.3 because the "
+              "user set an algorithm cipher list.");
+      }
+      if(conn_config->cipher_list13) {
+        failf(data, "schannel: This version of Schannel does not support "
+              "setting an algorithm cipher list and TLS 1.3 cipher list at "
+              "the same time");
+        return CURLE_SSL_CIPHER;
+      }
       result = set_ssl_ciphers(&schannel_cred, ciphers, algIds);
       if(CURLE_OK != result) {
-        failf(data, "Unable to set ciphers to from connection ssl config");
+        failf(data, "schannel: Failed setting algorithm cipher list");
         return result;
       }
     }
@@ -1075,7 +1050,8 @@
 {
   ssize_t written = -1;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   SecBuffer outbuf;
@@ -1152,12 +1128,12 @@
 
   backend->cred = NULL;
 
-  /* check for an existing re-usable credential handle */
+  /* check for an existing reusable credential handle */
   if(ssl_config->primary.sessionid) {
     Curl_ssl_sessionid_lock(data);
     if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) {
       backend->cred = old_cred;
-      DEBUGF(infof(data, "schannel: re-using existing credential handle"));
+      DEBUGF(infof(data, "schannel: reusing existing credential handle"));
 
       /* increment the reference counter of the credential/session handle */
       backend->cred->refcount++;
@@ -1349,7 +1325,8 @@
 schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   int i;
   ssize_t nread = -1, written = -1;
@@ -1607,7 +1584,7 @@
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
     data->set.str[STRING_SSL_PINNEDPUBLICKEY];
   if(pubkey_ptr) {
-    result = pkp_pin_peer_pubkey(cf, data, pubkey_ptr);
+    result = schannel_pkp_pin_peer_pubkey(cf, data, pubkey_ptr);
     if(result) {
       failf(data, "SSL: public key does not match pinned public key");
       return result;
@@ -1616,10 +1593,16 @@
 
 #ifdef HAS_MANUAL_VERIFY_API
   if(conn_config->verifypeer && backend->use_manual_cred_validation) {
+    /* Certificate verification also verifies the hostname if verifyhost */
     return Curl_verify_certificate(cf, data);
   }
 #endif
 
+  /* Verify the hostname manually when certificate verification is disabled,
+     because in that case Schannel won't verify it. */
+  if(!conn_config->verifypeer && conn_config->verifyhost)
+    return Curl_verify_host(cf, data);
+
   return CURLE_OK;
 }
 
@@ -1632,7 +1615,8 @@
     (cert_context->cbCertEncoded > 0);
 }
 
-typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg);
+typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context,
+                             bool reverse_order, void *arg);
 
 static void
 traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func,
@@ -1640,19 +1624,32 @@
 {
   const CERT_CONTEXT *current_context = NULL;
   bool should_continue = true;
+  bool first = true;
+  bool reverse_order = false;
   while(should_continue &&
         (current_context = CertEnumCertificatesInStore(
           context->hCertStore,
-          current_context)) != NULL)
-    should_continue = func(current_context, arg);
+          current_context)) != NULL) {
+    /* Windows 11 22H2 OS Build 22621.674 or higher enumerates certificates in
+       leaf-to-root order while all previous versions of Windows enumerate
+       certificates in root-to-leaf order. Determine the order of enumeration
+       by comparing SECPKG_ATTR_REMOTE_CERT_CONTEXT's pbCertContext with the
+       first certificate's pbCertContext. */
+    if(first && context->pbCertEncoded != current_context->pbCertEncoded)
+      reverse_order = true;
+    should_continue = func(current_context, reverse_order, arg);
+    first = false;
+  }
 
   if(current_context)
     CertFreeCertificateContext(current_context);
 }
 
 static bool
-cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count)
+cert_counter_callback(const CERT_CONTEXT *ccert_context, bool reverse_order,
+                      void *certs_count)
 {
+  (void)reverse_order; /* unused */
   if(valid_cert_encoding(ccert_context))
     (*(int *)certs_count)++;
   return true;
@@ -1667,14 +1664,16 @@
 };
 
 static bool
-add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg)
+add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order,
+                     void *raw_arg)
 {
   struct Adder_args *args = (struct Adder_args*)raw_arg;
   args->result = CURLE_OK;
   if(valid_cert_encoding(ccert_context)) {
     const char *beg = (const char *) ccert_context->pbCertEncoded;
     const char *end = beg + ccert_context->cbCertEncoded;
-    int insert_index = (args->certs_count - 1) - args->idx;
+    int insert_index = reverse_order ? (args->certs_count - 1) - args->idx :
+                       args->idx;
     args->result = Curl_extract_certinfo(args->data, insert_index,
                                          beg, end);
     args->idx++;
@@ -1686,7 +1685,8 @@
 schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   CURLcode result = CURLE_OK;
   SECURITY_STATUS sspi_status = SEC_E_OK;
@@ -1755,7 +1755,7 @@
   }
 #endif
 
-  /* save the current session data for possible re-use */
+  /* save the current session data for possible reuse */
   if(ssl_config->primary.sessionid) {
     bool incache;
     bool added = FALSE;
@@ -1931,7 +1931,8 @@
      * Available on Windows 7 or later.
      */
     {
-      struct ssl_backend_data *backend = connssl->backend;
+      struct schannel_ssl_backend_data *backend =
+        (struct schannel_ssl_backend_data *)connssl->backend;
       DEBUGASSERT(backend);
       cf->conn->sslContext = &backend->ctxt->ctxt_handle;
     }
@@ -1960,7 +1961,8 @@
   SecBufferDesc outbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CURLcode result;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -2110,7 +2112,8 @@
   /* we want the length of the encrypted buffer to be at least large enough
      that it can hold all the bytes requested and some TLS record overhead. */
   size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -2443,12 +2446,13 @@
                                   const struct Curl_easy *data)
 {
   const struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
 
-  if(connssl->backend->ctxt) /* SSL/TLS is in use */
+  if(backend->ctxt) /* SSL/TLS is in use */
     return (backend->decdata_offset > 0 ||
             (backend->encdata_offset > 0 && !backend->encdata_is_incomplete));
   else
@@ -2486,12 +2490,13 @@
    * Shutting Down an Schannel Connection
    */
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(data);
   DEBUGASSERT(backend);
 
-  if(connssl->backend->ctxt) {
+  if(backend->ctxt) {
     infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
           connssl->hostname, connssl->port);
   }
@@ -2611,12 +2616,13 @@
   return Curl_win32_random(entropy, length);
 }
 
-static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    const char *pinnedpubkey)
+static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data,
+                                             const char *pinnedpubkey)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   CERT_CONTEXT *pCertContextServer = NULL;
 
   /* Result is returned to caller */
@@ -2705,8 +2711,7 @@
     if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
       break; /* failed */
 
-    /* workaround for original MinGW, should be (const BYTE*) */
-    if(!CryptHashData(hHash, (BYTE*)input, (DWORD)inputlen, 0))
+    if(!CryptHashData(hHash, input, (DWORD)inputlen, 0))
       break; /* failed */
 
     /* get hash size */
@@ -2742,7 +2747,8 @@
 static void *schannel_get_internals(struct ssl_connect_data *connssl,
                                     CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return &backend->ctxt->ctxt_handle;
@@ -2759,7 +2765,7 @@
   SSLSUPP_TLS13_CIPHERSUITES |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct schannel_ssl_backend_data),
 
   schannel_init,                     /* init */
   schannel_cleanup,                  /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/schannel.h b/Utilities/cmcurl/lib/vtls/schannel.h
index 7fae39f..b26334b 100644
--- a/Utilities/cmcurl/lib/vtls/schannel.h
+++ b/Utilities/cmcurl/lib/vtls/schannel.h
@@ -28,8 +28,6 @@
 
 #ifdef USE_SCHANNEL
 
-#define SCHANNEL_USE_BLACKLISTS 1
-
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable: 4201)
@@ -70,7 +68,7 @@
  * BoringSSL's <openssl/x509.h>: So just undefine those defines here
  * (and only here).
  */
-#if defined(HAVE_BORINGSSL) || defined(OPENSSL_IS_BORINGSSL)
+#if defined(OPENSSL_IS_BORINGSSL)
 # undef X509_NAME
 # undef X509_CERT_PAIR
 # undef X509_EXTENSIONS
@@ -78,122 +76,11 @@
 
 extern const struct Curl_ssl Curl_ssl_schannel;
 
+CURLcode Curl_verify_host(struct Curl_cfilter *cf,
+                          struct Curl_easy *data);
+
 CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
                                  struct Curl_easy *data);
 
-/* structs to expose only in schannel.c and schannel_verify.c */
-#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
-
-#ifdef __MINGW32__
-#ifdef __MINGW64_VERSION_MAJOR
-#define HAS_MANUAL_VERIFY_API
-#endif
-#else
-#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
-#define HAS_MANUAL_VERIFY_API
-#endif
-#endif
-
-#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)   \
-  && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
-#define HAS_CLIENT_CERT_PATH
-#endif
-
-#ifndef SCH_CREDENTIALS_VERSION
-
-#define SCH_CREDENTIALS_VERSION  0x00000005
-
-typedef enum _eTlsAlgorithmUsage
-{
-    TlsParametersCngAlgUsageKeyExchange,
-    TlsParametersCngAlgUsageSignature,
-    TlsParametersCngAlgUsageCipher,
-    TlsParametersCngAlgUsageDigest,
-    TlsParametersCngAlgUsageCertSig
-} eTlsAlgorithmUsage;
-
-typedef struct _CRYPTO_SETTINGS
-{
-    eTlsAlgorithmUsage  eAlgorithmUsage;
-    UNICODE_STRING      strCngAlgId;
-    DWORD               cChainingModes;
-    PUNICODE_STRING     rgstrChainingModes;
-    DWORD               dwMinBitLength;
-    DWORD               dwMaxBitLength;
-} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
-
-typedef struct _TLS_PARAMETERS
-{
-    DWORD               cAlpnIds;
-    PUNICODE_STRING     rgstrAlpnIds;
-    DWORD               grbitDisabledProtocols;
-    DWORD               cDisabledCrypto;
-    PCRYPTO_SETTINGS    pDisabledCrypto;
-    DWORD               dwFlags;
-} TLS_PARAMETERS, * PTLS_PARAMETERS;
-
-typedef struct _SCH_CREDENTIALS
-{
-    DWORD               dwVersion;
-    DWORD               dwCredFormat;
-    DWORD               cCreds;
-    PCCERT_CONTEXT* paCred;
-    HCERTSTORE          hRootStore;
-
-    DWORD               cMappers;
-    struct _HMAPPER **aphMappers;
-
-    DWORD               dwSessionLifespan;
-    DWORD               dwFlags;
-    DWORD               cTlsParameters;
-    PTLS_PARAMETERS     pTlsParameters;
-} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
-
-#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
-#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
-#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
-#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
-
-#endif
-
-struct Curl_schannel_cred {
-  CredHandle cred_handle;
-  TimeStamp time_stamp;
-  TCHAR *sni_hostname;
-#ifdef HAS_CLIENT_CERT_PATH
-  HCERTSTORE client_cert_store;
-#endif
-  int refcount;
-};
-
-struct Curl_schannel_ctxt {
-  CtxtHandle ctxt_handle;
-  TimeStamp time_stamp;
-};
-
-struct ssl_backend_data {
-  struct Curl_schannel_cred *cred;
-  struct Curl_schannel_ctxt *ctxt;
-  SecPkgContext_StreamSizes stream_sizes;
-  size_t encdata_length, decdata_length;
-  size_t encdata_offset, decdata_offset;
-  unsigned char *encdata_buffer, *decdata_buffer;
-  /* encdata_is_incomplete: if encdata contains only a partial record that
-     can't be decrypted without another recv() (that is, status is
-     SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
-     more bytes into encdata then set this back to false. */
-  bool encdata_is_incomplete;
-  unsigned long req_flags, ret_flags;
-  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
-  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
-  bool recv_connection_closed; /* true if connection closed, regardless how */
-  bool recv_renegotiating;     /* true if recv is doing renegotiation */
-  bool use_alpn; /* true if ALPN is used for this connection */
-#ifdef HAS_MANUAL_VERIFY_API
-  bool use_manual_cred_validation; /* true if manual cred validation is used */
-#endif
-};
-#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */
-
 #endif /* USE_SCHANNEL */
 #endif /* HEADER_CURL_SCHANNEL_H */
diff --git a/Utilities/cmcurl/lib/vtls/schannel_int.h b/Utilities/cmcurl/lib/vtls/schannel_int.h
new file mode 100644
index 0000000..a128e04
--- /dev/null
+++ b/Utilities/cmcurl/lib/vtls/schannel_int.h
@@ -0,0 +1,153 @@
+#ifndef HEADER_CURL_SCHANNEL_INT_H
+#define HEADER_CURL_SCHANNEL_INT_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifdef USE_SCHANNEL
+
+#if defined(__MINGW32__) || defined(CERT_CHAIN_REVOCATION_CHECK_CHAIN)
+#define HAS_MANUAL_VERIFY_API
+#endif
+
+#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)   \
+  && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
+#define HAS_CLIENT_CERT_PATH
+#endif
+
+#ifndef CRYPT_DECODE_NOCOPY_FLAG
+#define CRYPT_DECODE_NOCOPY_FLAG 0x1
+#endif
+
+#ifndef CRYPT_DECODE_ALLOC_FLAG
+#define CRYPT_DECODE_ALLOC_FLAG 0x8000
+#endif
+
+#ifndef CERT_ALT_NAME_DNS_NAME
+#define CERT_ALT_NAME_DNS_NAME 3
+#endif
+
+#ifndef CERT_ALT_NAME_IP_ADDRESS
+#define CERT_ALT_NAME_IP_ADDRESS 8
+#endif
+
+
+#ifndef SCH_CREDENTIALS_VERSION
+
+#define SCH_CREDENTIALS_VERSION  0x00000005
+
+typedef enum _eTlsAlgorithmUsage
+{
+    TlsParametersCngAlgUsageKeyExchange,
+    TlsParametersCngAlgUsageSignature,
+    TlsParametersCngAlgUsageCipher,
+    TlsParametersCngAlgUsageDigest,
+    TlsParametersCngAlgUsageCertSig
+} eTlsAlgorithmUsage;
+
+typedef struct _CRYPTO_SETTINGS
+{
+    eTlsAlgorithmUsage  eAlgorithmUsage;
+    UNICODE_STRING      strCngAlgId;
+    DWORD               cChainingModes;
+    PUNICODE_STRING     rgstrChainingModes;
+    DWORD               dwMinBitLength;
+    DWORD               dwMaxBitLength;
+} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
+
+typedef struct _TLS_PARAMETERS
+{
+    DWORD               cAlpnIds;
+    PUNICODE_STRING     rgstrAlpnIds;
+    DWORD               grbitDisabledProtocols;
+    DWORD               cDisabledCrypto;
+    PCRYPTO_SETTINGS    pDisabledCrypto;
+    DWORD               dwFlags;
+} TLS_PARAMETERS, * PTLS_PARAMETERS;
+
+typedef struct _SCH_CREDENTIALS
+{
+    DWORD               dwVersion;
+    DWORD               dwCredFormat;
+    DWORD               cCreds;
+    PCCERT_CONTEXT* paCred;
+    HCERTSTORE          hRootStore;
+
+    DWORD               cMappers;
+    struct _HMAPPER **aphMappers;
+
+    DWORD               dwSessionLifespan;
+    DWORD               dwFlags;
+    DWORD               cTlsParameters;
+    PTLS_PARAMETERS     pTlsParameters;
+} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
+
+#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
+#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
+#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
+#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
+
+#endif /* SCH_CREDENTIALS_VERSION */
+
+struct Curl_schannel_cred {
+  CredHandle cred_handle;
+  TimeStamp time_stamp;
+  TCHAR *sni_hostname;
+#ifdef HAS_CLIENT_CERT_PATH
+  HCERTSTORE client_cert_store;
+#endif
+  int refcount;
+};
+
+struct Curl_schannel_ctxt {
+  CtxtHandle ctxt_handle;
+  TimeStamp time_stamp;
+};
+
+struct schannel_ssl_backend_data {
+  struct Curl_schannel_cred *cred;
+  struct Curl_schannel_ctxt *ctxt;
+  SecPkgContext_StreamSizes stream_sizes;
+  size_t encdata_length, decdata_length;
+  size_t encdata_offset, decdata_offset;
+  unsigned char *encdata_buffer, *decdata_buffer;
+  /* encdata_is_incomplete: if encdata contains only a partial record that
+     can't be decrypted without another recv() (that is, status is
+     SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
+     more bytes into encdata then set this back to false. */
+  bool encdata_is_incomplete;
+  unsigned long req_flags, ret_flags;
+  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
+  bool recv_connection_closed; /* true if connection closed, regardless how */
+  bool recv_renegotiating;     /* true if recv is doing renegotiation */
+  bool use_alpn; /* true if ALPN is used for this connection */
+#ifdef HAS_MANUAL_VERIFY_API
+  bool use_manual_cred_validation; /* true if manual cred validation is used */
+#endif
+};
+
+#endif /* USE_SCHANNEL */
+#endif /* HEADER_CURL_SCHANNEL_INT_H */
diff --git a/Utilities/cmcurl/lib/vtls/schannel_verify.c b/Utilities/cmcurl/lib/vtls/schannel_verify.c
index d75ee8d..a5d5c98 100644
--- a/Utilities/cmcurl/lib/vtls/schannel_verify.c
+++ b/Utilities/cmcurl/lib/vtls/schannel_verify.c
@@ -36,10 +36,8 @@
 #  error "Can't compile SCHANNEL support without SSPI."
 #endif
 
-#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
 #include "schannel.h"
-
-#ifdef HAS_MANUAL_VERIFY_API
+#include "schannel_int.h"
 
 #include "vtls.h"
 #include "vtls_int.h"
@@ -54,7 +52,10 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define BACKEND connssl->backend
+#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
+
+
+#ifdef HAS_MANUAL_VERIFY_API
 
 #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
 #define BEGIN_CERT "-----BEGIN CERTIFICATE-----"
@@ -330,6 +331,8 @@
   return result;
 }
 
+#endif /* HAS_MANUAL_VERIFY_API */
+
 /*
  * Returns the number of characters necessary to populate all the host_names.
  * If host_names is not NULL, populate it with all the host names. Each string
@@ -353,10 +356,10 @@
   LPTSTR current_pos = NULL;
   DWORD i;
 
+#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
   /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */
   if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
                                   VERSION_GREATER_THAN_EQUAL)) {
-#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
     /* CertGetNameString will provide the 8-bit character string without
      * any decoding */
     DWORD name_flags =
@@ -368,8 +371,8 @@
                                       host_names,
                                       length);
     return actual_length;
-#endif
   }
+#endif
 
   compute_content = host_names != NULL && length != 0;
 
@@ -457,17 +460,34 @@
   return actual_length;
 }
 
-static CURLcode verify_host(struct Curl_easy *data,
-                            CERT_CONTEXT *pCertContextServer,
-                            const char *conn_hostname)
+/* Verify the server's hostname */
+CURLcode Curl_verify_host(struct Curl_cfilter *cf,
+                          struct Curl_easy *data)
 {
+  struct ssl_connect_data *connssl = cf->ctx;
+  SECURITY_STATUS sspi_status;
   CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+  CERT_CONTEXT *pCertContextServer = NULL;
   TCHAR *cert_hostname_buff = NULL;
   size_t cert_hostname_buff_index = 0;
+  const char *conn_hostname = connssl->hostname;
   size_t hostlen = strlen(conn_hostname);
   DWORD len = 0;
   DWORD actual_len = 0;
 
+  sspi_status =
+    s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+                                     SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+                                     &pCertContextServer);
+
+  if((sspi_status != SEC_E_OK) || !pCertContextServer) {
+    char buffer[STRERROR_LEN];
+    failf(data, "schannel: Failed to read remote certificate context: %s",
+          Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+    result = CURLE_PEER_FAILED_VERIFICATION;
+    goto cleanup;
+  }
+
   /* Determine the size of the string needed for the cert hostname */
   len = cert_get_name_string(data, pCertContextServer, NULL, 0);
   if(len == 0) {
@@ -498,10 +518,9 @@
     goto cleanup;
   }
 
-  /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output
-   * will contain all DNS names, where each name is null-terminated
-   * and the last DNS name is double null-terminated. Due to this
-   * encoding, use the length of the buffer to iterate over all names.
+  /* cert_hostname_buff contains all DNS names, where each name is
+   * null-terminated and the last DNS name is double null-terminated. Due to
+   * this encoding, use the length of the buffer to iterate over all names.
    */
   result = CURLE_PEER_FAILED_VERIFICATION;
   while(cert_hostname_buff_index < len &&
@@ -560,9 +579,15 @@
 cleanup:
   Curl_safefree(cert_hostname_buff);
 
+  if(pCertContextServer)
+    CertFreeCertificateContext(pCertContextServer);
+
   return result;
 }
 
+
+#ifdef HAS_MANUAL_VERIFY_API
+/* Verify the server's certificate and hostname */
 CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
                                  struct Curl_easy *data)
 {
@@ -721,7 +746,7 @@
 
   if(result == CURLE_OK) {
     if(conn_config->verifyhost) {
-      result = verify_host(data, pCertContextServer, connssl->hostname);
+      result = Curl_verify_host(cf, data);
     }
   }
 
diff --git a/Utilities/cmcurl/lib/vtls/sectransp.c b/Utilities/cmcurl/lib/vtls/sectransp.c
index c9f02f2..3378f76 100644
--- a/Utilities/cmcurl/lib/vtls/sectransp.c
+++ b/Utilities/cmcurl/lib/vtls/sectransp.c
@@ -146,7 +146,7 @@
 #define ioErr -36
 #define paramErr -50
 
-struct ssl_backend_data {
+struct st_ssl_backend_data {
   SSLContextRef ssl_ctx;
   bool ssl_direction; /* true if writing, false if reading */
   size_t ssl_write_buffered_length;
@@ -830,13 +830,14 @@
 #endif /* SECTRANSP_PINNEDPUBKEY_V1 */
 #endif /* SECTRANSP_PINNEDPUBKEY */
 
-static OSStatus bio_cf_in_read(SSLConnectionRef connection,
-                               void *buf,
-                               size_t *dataLength)  /* IN/OUT */
+static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection,
+                                         void *buf,
+                                         size_t *dataLength)  /* IN/OUT */
 {
   struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result;
@@ -844,8 +845,8 @@
 
   DEBUGASSERT(data);
   nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result);
-  DEBUGF(LOG_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d",
-                *dataLength, nread, result));
+  CURL_TRC_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d",
+              *dataLength, nread, result);
   if(nread < 0) {
     switch(result) {
       case CURLE_OK:
@@ -859,6 +860,9 @@
     }
     nread = 0;
   }
+  else if(nread == 0) {
+    rtn = errSSLClosedGraceful;
+  }
   else if((size_t)nread < *dataLength) {
     rtn = errSSLWouldBlock;
   }
@@ -866,13 +870,14 @@
   return rtn;
 }
 
-static OSStatus bio_cf_out_write(SSLConnectionRef connection,
-                                 const void *buf,
-                                 size_t *dataLength)  /* IN/OUT */
+static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
+                                           const void *buf,
+                                           size_t *dataLength)  /* IN/OUT */
 {
   struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result;
@@ -880,8 +885,8 @@
 
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, &result);
-  DEBUGF(LOG_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d",
-                *dataLength, nwritten, result));
+  CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d",
+              *dataLength, nwritten, result);
   if(nwritten <= 0) {
     if(result == CURLE_AGAIN) {
       rtn = errSSLWouldBlock;
@@ -1081,7 +1086,6 @@
   CFArrayRef keys_list;
   CFIndex keys_list_count;
   CFIndex i;
-  CFStringRef common_name;
 
   /* SecItemCopyMatching() was introduced in iOS and Snow Leopard.
      kSecClassIdentity was introduced in Lion. If both exist, let's use them
@@ -1129,6 +1133,7 @@
           (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i);
         err = SecIdentityCopyCertificate(identity, &cert);
         if(err == noErr) {
+          CFStringRef common_name = NULL;
           OSStatus copy_status = noErr;
 #if CURL_BUILD_IOS
           common_name = SecCertificateCopySubjectSummary(cert);
@@ -1144,7 +1149,8 @@
             status = noErr;
             break;
           }
-          CFRelease(common_name);
+          if(common_name)
+            CFRelease(common_name);
         }
         CFRelease(cert);
       }
@@ -1288,7 +1294,7 @@
 
 /* This code was borrowed from nss.c, with some modifications:
  * Determine whether the nickname passed in is a filename that needs to
- * be loaded as a PEM or a regular NSS nickname.
+ * be loaded as a PEM or a nickname.
  *
  * returns 1 for a file
  * returns 0 for not a file
@@ -1338,7 +1344,8 @@
                                         struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
   long ssl_version_max = conn_config->version_max;
@@ -1605,7 +1612,7 @@
          The message is a bit cryptic and longer than necessary but can be
          understood by humans. */
       failf(data, "SSL: cipher string \"%s\" contains unsupported cipher name"
-            " starting position %d and ending position %d",
+            " starting position %zd and ending position %zd",
             ciphers,
             cipher_start - ciphers,
             cipher_end - ciphers);
@@ -1633,7 +1640,8 @@
                                         struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   const struct curl_blob *ssl_cablob = conn_config->ca_info_blob;
@@ -1655,7 +1663,7 @@
 
   DEBUGASSERT(backend);
 
-  DEBUGF(LOG_CF(data, cf, "connect_step1"));
+  CURL_TRC_CF(data, cf, "connect_step1");
   GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
 #endif /* CURL_BUILD_MAC */
 
@@ -2062,7 +2070,7 @@
         return CURLE_SSL_CONNECT_ERROR;
       }
       /* Informational message */
-      infof(data, "SSL re-using session ID");
+      infof(data, "SSL reusing session ID");
     }
     /* If there isn't one, then let's make one up! This has to be done prior
        to starting the handshake. */
@@ -2092,7 +2100,9 @@
     }
   }
 
-  err = SSLSetIOFuncs(backend->ssl_ctx, bio_cf_in_read, bio_cf_out_write);
+  err = SSLSetIOFuncs(backend->ssl_ctx,
+                      sectransp_bio_cf_in_read,
+                      sectransp_bio_cf_out_write);
   if(err != noErr) {
     failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err);
     return CURLE_SSL_CONNECT_ERROR;
@@ -2286,7 +2296,7 @@
       /* This is not a PEM file, probably a certificate in DER format. */
       rc = append_cert_to_array(data, certbuf, buflen, array);
       if(rc != CURLE_OK) {
-        DEBUGF(LOG_CF(data, cf, "append_cert for CA failed"));
+        CURL_TRC_CF(data, cf, "append_cert for CA failed");
         result = rc;
         goto out;
       }
@@ -2300,7 +2310,7 @@
     rc = append_cert_to_array(data, der, derlen, array);
     free(der);
     if(rc != CURLE_OK) {
-      DEBUGF(LOG_CF(data, cf, "append_cert for CA failed"));
+      CURL_TRC_CF(data, cf, "append_cert for CA failed");
       result = rc;
       goto out;
     }
@@ -2316,7 +2326,7 @@
     goto out;
   }
 
-  DEBUGF(LOG_CF(data, cf, "setting %d trust anchors", n));
+  CURL_TRC_CF(data, cf, "setting %d trust anchors", n);
   ret = SecTrustSetAnchorCertificates(trust, array);
   if(ret != noErr) {
     failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret);
@@ -2338,11 +2348,11 @@
   switch(trust_eval) {
     case kSecTrustResultUnspecified:
       /* what does this really mean? */
-      DEBUGF(LOG_CF(data, cf, "trust result: Unspecified"));
+      CURL_TRC_CF(data, cf, "trust result: Unspecified");
       result = CURLE_OK;
       goto out;
     case kSecTrustResultProceed:
-      DEBUGF(LOG_CF(data, cf, "trust result: Proceed"));
+      CURL_TRC_CF(data, cf, "trust result: Proceed");
       result = CURLE_OK;
       goto out;
 
@@ -2375,7 +2385,7 @@
   size_t buflen;
 
   if(ca_info_blob) {
-    DEBUGF(LOG_CF(data, cf, "verify_peer, CA from config blob"));
+    CURL_TRC_CF(data, cf, "verify_peer, CA from config blob");
     certbuf = (unsigned char *)malloc(ca_info_blob->len + 1);
     if(!certbuf) {
       return CURLE_OUT_OF_MEMORY;
@@ -2385,7 +2395,7 @@
     certbuf[ca_info_blob->len]='\0';
   }
   else if(cafile) {
-    DEBUGF(LOG_CF(data, cf, "verify_peer, CA from file '%s'", cafile));
+    CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile);
     if(read_cert(cafile, &certbuf, &buflen) < 0) {
       failf(data, "SSL: failed to read or invalid CA certificate");
       return CURLE_SSL_CACERT_BADFILE;
@@ -2425,7 +2435,6 @@
     SecTrustRef trust;
     OSStatus ret;
     SecKeyRef keyRef;
-    OSStatus success;
 
     ret = SSLCopyPeerTrust(ctx, &trust);
     if(ret != noErr || !trust)
@@ -2445,11 +2454,14 @@
 
 #elif SECTRANSP_PINNEDPUBKEY_V2
 
-    success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
-                            &publicKeyBits);
-    CFRelease(keyRef);
-    if(success != errSecSuccess || !publicKeyBits)
-      break;
+    {
+        OSStatus success;
+        success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
+                                &publicKeyBits);
+        CFRelease(keyRef);
+        if(success != errSecSuccess || !publicKeyBits)
+          break;
+    }
 
 #endif /* SECTRANSP_PINNEDPUBKEY_V2 */
 
@@ -2477,7 +2489,7 @@
         spkiHeaderLength = 23;
         break;
       default:
-        infof(data, "SSL: unhandled public key length: %d", pubkeylen);
+        infof(data, "SSL: unhandled public key length: %zu", pubkeylen);
 #elif SECTRANSP_PINNEDPUBKEY_V2
       default:
         /* ecDSA secp256r1 pubkeylen == 91 header already included?
@@ -2515,7 +2527,8 @@
                                         struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   OSStatus err;
   SSLCipherSuite cipher;
@@ -2525,7 +2538,7 @@
               || ssl_connect_2_reading == connssl->connecting_state
               || ssl_connect_2_writing == connssl->connecting_state);
   DEBUGASSERT(backend);
-  DEBUGF(LOG_CF(data, cf, "connect_step2"));
+  CURL_TRC_CF(data, cf, "connect_step2");
 
   /* Here goes nothing: */
 check_handshake:
@@ -2896,7 +2909,8 @@
   CURLcode result = ssl_config->certinfo ?
     CURLE_PEER_FAILED_VERIFICATION : CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   CFArrayRef server_certs = NULL;
   SecCertificateRef server_cert;
   OSStatus err;
@@ -2991,7 +3005,7 @@
   struct ssl_connect_data *connssl = cf->ctx;
   CURLcode result;
 
-  DEBUGF(LOG_CF(data, cf, "connect_step3"));
+  CURL_TRC_CF(data, cf, "connect_step3");
   /* There is no step 3!
    * Well, okay, let's collect server certificates, and if verbose mode is on,
    * let's print the details of the server certificates. */
@@ -3100,7 +3114,7 @@
   }
 
   if(ssl_connect_done == connssl->connecting_state) {
-    DEBUGF(LOG_CF(data, cf, "connected"));
+    CURL_TRC_CF(data, cf, "connected");
     connssl->state = ssl_connection_complete;
     *done = TRUE;
   }
@@ -3139,14 +3153,15 @@
 static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
 
   (void) data;
 
   DEBUGASSERT(backend);
 
   if(backend->ssl_ctx) {
-    DEBUGF(LOG_CF(data, cf, "close"));
+    CURL_TRC_CF(data, cf, "close");
     (void)SSLClose(backend->ssl_ctx);
 #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
     if(SSLCreateContext)
@@ -3166,7 +3181,8 @@
                               struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   ssize_t nread;
   int what;
   int rc;
@@ -3191,7 +3207,7 @@
   what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
                          SSL_SHUTDOWN_TIMEOUT);
 
-  DEBUGF(LOG_CF(data, cf, "shutdown"));
+  CURL_TRC_CF(data, cf, "shutdown");
   while(loop--) {
     if(what < 0) {
       /* anything that gets here is fatally bad */
@@ -3244,7 +3260,8 @@
                                    const struct Curl_easy *data)
 {
   const struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   OSStatus err;
   size_t buffer;
 
@@ -3252,7 +3269,7 @@
   DEBUGASSERT(backend);
 
   if(backend->ssl_ctx) {  /* SSL is in use */
-    DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data_pending"));
+    CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
     err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
     if(err == noErr)
       return buffer > 0UL;
@@ -3287,6 +3304,7 @@
                                     unsigned char *sha256sum, /* output */
                                     size_t sha256len)
 {
+  (void)sha256len;
   assert(sha256len >= CURL_SHA256_DIGEST_LENGTH);
   (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum);
   return CURLE_OK;
@@ -3308,7 +3326,8 @@
                               CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   size_t processed = 0UL;
   OSStatus err;
 
@@ -3376,7 +3395,8 @@
                               CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   size_t processed = 0UL;
   OSStatus err;
@@ -3434,7 +3454,8 @@
 static void *sectransp_get_internals(struct ssl_connect_data *connssl,
                                      CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->ssl_ctx;
@@ -3450,7 +3471,7 @@
 #endif /* SECTRANSP_PINNEDPUBKEY */
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct st_ssl_backend_data),
 
   Curl_none_init,                     /* init */
   Curl_none_cleanup,                  /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/vtls.c b/Utilities/cmcurl/lib/vtls/vtls.c
index a4ff7d6..494b660 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.c
+++ b/Utilities/cmcurl/lib/vtls/vtls.c
@@ -417,7 +417,7 @@
   DEBUGASSERT(ssl_config->primary.sessionid);
 
   if(!ssl_config->primary.sessionid || !data->state.session)
-    /* session ID re-use is disabled or the session cache has not been
+    /* session ID reuse is disabled or the session cache has not been
        setup */
     return TRUE;
 
@@ -453,7 +453,7 @@
     }
   }
 
-  DEBUGF(infof(data, DMSG(data, "%s Session ID in cache for %s %s://%s:%d"),
+  DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
                no_match? "Didn't find": "Found",
                Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
                cf->conn->handler->scheme, connssl->hostname, connssl->port));
@@ -601,8 +601,8 @@
   if(added)
     *added = TRUE;
 
-  DEBUGF(infof(data, DMSG(data, "Added Session ID to cache for %s://%s:%d"
-               " [%s]"), store->scheme, store->name, store->remote_port,
+  DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]",
+               store->scheme, store->name, store->remote_port,
                Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"));
   return CURLE_OK;
 }
@@ -635,19 +635,16 @@
   struct ssl_connect_data *connssl = cf->ctx;
   curl_socket_t sock = Curl_conn_cf_get_socket(cf->next, data);
 
-  if(sock != CURL_SOCKET_BAD) {
-    if(connssl->connecting_state == ssl_connect_2_writing) {
-      /* write mode */
-      socks[0] = sock;
-      return GETSOCK_WRITESOCK(0);
-    }
-    if(connssl->connecting_state == ssl_connect_2_reading) {
-      /* read mode */
-      socks[0] = sock;
-      return GETSOCK_READSOCK(0);
-    }
+  if(sock == CURL_SOCKET_BAD)
+    return GETSOCK_BLANK;
+
+  if(connssl->connecting_state == ssl_connect_2_writing) {
+    /* we are only interested in writing */
+    socks[0] = sock;
+    return GETSOCK_WRITESOCK(0);
   }
-  return GETSOCK_BLANK;
+  socks[0] = sock;
+  return GETSOCK_READSOCK(0);
 }
 
 /* Selects an SSL crypto engine
@@ -883,6 +880,9 @@
   FILE *fp;
   unsigned char *buf = NULL, *pem_ptr = NULL;
   CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+  (void)data;
+#endif
 
   /* if a path wasn't specified, don't pin */
   if(!pinnedpubkey)
@@ -893,8 +893,8 @@
   /* only do this if pinnedpubkey starts with "sha256//", length 8 */
   if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
     CURLcode encode;
-    size_t encodedlen, pinkeylen;
-    char *encoded, *pinkeycopy, *begin_pos, *end_pos;
+    size_t encodedlen = 0, pinkeylen;
+    char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos;
     unsigned char *sha256sumdigest;
 
     if(!Curl_ssl->sha256sum) {
@@ -907,14 +907,12 @@
     if(!sha256sumdigest)
       return CURLE_OUT_OF_MEMORY;
     encode = Curl_ssl->sha256sum(pubkey, pubkeylen,
-                        sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
+                                 sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
 
-    if(encode != CURLE_OK)
-      return encode;
-
-    encode = Curl_base64_encode((char *)sha256sumdigest,
-                                CURL_SHA256_DIGEST_LENGTH, &encoded,
-                                &encodedlen);
+    if(!encode)
+      encode = Curl_base64_encode((char *)sha256sumdigest,
+                                  CURL_SHA256_DIGEST_LENGTH, &encoded,
+                                  &encodedlen);
     Curl_safefree(sha256sumdigest);
 
     if(encode)
@@ -1242,12 +1240,8 @@
   &Curl_ssl_sectransp;
 #elif defined(USE_GNUTLS)
   &Curl_ssl_gnutls;
-#elif defined(USE_GSKIT)
-  &Curl_ssl_gskit;
 #elif defined(USE_MBEDTLS)
   &Curl_ssl_mbedtls;
-#elif defined(USE_NSS)
-  &Curl_ssl_nss;
 #elif defined(USE_RUSTLS)
   &Curl_ssl_rustls;
 #elif defined(USE_OPENSSL)
@@ -1270,15 +1264,9 @@
 #if defined(USE_GNUTLS)
   &Curl_ssl_gnutls,
 #endif
-#if defined(USE_GSKIT)
-  &Curl_ssl_gskit,
-#endif
 #if defined(USE_MBEDTLS)
   &Curl_ssl_mbedtls,
 #endif
-#if defined(USE_NSS)
-  &Curl_ssl_nss,
-#endif
 #if defined(USE_OPENSSL)
   &Curl_ssl_openssl,
 #endif
@@ -1506,7 +1494,8 @@
 
   CF_DATA_SAVE(save, cf, data);
   cf_close(cf, data);
-  cf->next->cft->close(cf->next, data);
+  if(cf->next)
+    cf->next->cft->do_close(cf->next, data);
   CF_DATA_RESTORE(cf, save);
 }
 
@@ -1524,13 +1513,14 @@
   }
 
   CF_DATA_SAVE(save, cf, data);
+  CURL_TRC_CF(data, cf, "cf_connect()");
   (void)connssl;
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn == cf->conn);
   DEBUGASSERT(connssl);
   DEBUGASSERT(cf->conn->host.name);
 
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     goto out;
 
@@ -1553,6 +1543,7 @@
     DEBUGASSERT(connssl->state == ssl_connection_complete);
   }
 out:
+  CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done);
   CF_DATA_RESTORE(cf, save);
   return result;
 }
@@ -1594,6 +1585,7 @@
   ssize_t nread;
 
   CF_DATA_SAVE(save, cf, data);
+  *err = CURLE_OK;
   nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
   if(nread > 0) {
     DEBUGASSERT((size_t)nread <= len);
@@ -1602,7 +1594,7 @@
     /* eof */
     *err = CURLE_OK;
   }
-  DEBUGF(LOG_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err));
+  CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, nread, *err);
   CF_DATA_RESTORE(cf, save);
   return nread;
 }
@@ -1612,12 +1604,17 @@
                                    curl_socket_t *socks)
 {
   struct cf_call_data save;
-  int result;
+  int fds = GETSOCK_BLANK;
 
-  CF_DATA_SAVE(save, cf, data);
-  result = Curl_ssl->get_select_socks(cf, data, socks);
-  CF_DATA_RESTORE(cf, save);
-  return result;
+  if(!cf->next->connected) {
+    fds = cf->next->cft->get_select_socks(cf->next, data, socks);
+  }
+  else if(!cf->connected) {
+    CF_DATA_SAVE(save, cf, data);
+    fds = Curl_ssl->get_select_socks(cf, data, socks);
+    CF_DATA_RESTORE(cf, save);
+  }
+  return fds;
 }
 
 static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
@@ -1703,7 +1700,7 @@
 struct Curl_cftype Curl_cft_ssl = {
   "SSL",
   CF_TYPE_SSL,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   ssl_cf_destroy,
   ssl_cf_connect,
   ssl_cf_close,
@@ -1721,7 +1718,7 @@
 struct Curl_cftype Curl_cft_ssl_proxy = {
   "SSL-PROXY",
   CF_TYPE_SSL,
-  CURL_LOG_DEFAULT,
+  CURL_LOG_LVL_NONE,
   ssl_cf_destroy,
   ssl_cf_connect,
   ssl_cf_close,
diff --git a/Utilities/cmcurl/lib/vtls/vtls.h b/Utilities/cmcurl/lib/vtls/vtls.h
index 3516247..8ad1cf6 100644
--- a/Utilities/cmcurl/lib/vtls/vtls.h
+++ b/Utilities/cmcurl/lib/vtls/vtls.h
@@ -43,7 +43,7 @@
 #define VTLS_INFOF_NO_ALPN                                      \
   "ALPN: server did not agree on a protocol. Uses default."
 #define VTLS_INFOF_ALPN_OFFER_1STR              \
-  "ALPN: offers %s"
+  "ALPN: curl offers %s"
 #define VTLS_INFOF_ALPN_ACCEPTED_1STR           \
   ALPN_ACCEPTED "%s"
 #define VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR       \
diff --git a/Utilities/cmcurl/lib/vtls/vtls_int.h b/Utilities/cmcurl/lib/vtls/vtls_int.h
index ed49339..a6e4544 100644
--- a/Utilities/cmcurl/lib/vtls/vtls_int.h
+++ b/Utilities/cmcurl/lib/vtls/vtls_int.h
@@ -73,7 +73,7 @@
   char *hostname;                   /* hostname for verification */
   char *dispname;                   /* display version of hostname */
   const struct alpn_spec *alpn;     /* ALPN to use or NULL for none */
-  struct ssl_backend_data *backend; /* vtls backend specific props */
+  void *backend;                    /* vtls backend specific props */
   struct cf_call_data call_data;    /* data handle used in current call */
   struct curltime handshake_done;   /* time when handshake finished */
   int port;                         /* remote port at origin */
@@ -81,6 +81,7 @@
 };
 
 
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct ssl_connect_data *)(cf)->ctx)->call_data
 
@@ -216,8 +217,6 @@
 
 #include "openssl.h"        /* OpenSSL versions */
 #include "gtls.h"           /* GnuTLS versions */
-#include "nssg.h"           /* NSS versions */
-#include "gskit.h"          /* Global Secure ToolKit versions */
 #include "wolfssl.h"        /* wolfSSL versions */
 #include "schannel.h"       /* Schannel SSPI version */
 #include "sectransp.h"      /* SecureTransport (Darwin) version */
diff --git a/Utilities/cmcurl/lib/vtls/wolfssl.c b/Utilities/cmcurl/lib/vtls/wolfssl.c
index 2928728..b1384a6 100644
--- a/Utilities/cmcurl/lib/vtls/wolfssl.c
+++ b/Utilities/cmcurl/lib/vtls/wolfssl.c
@@ -91,10 +91,10 @@
 #undef USE_BIO_CHAIN
 #endif
 
-struct ssl_backend_data {
-  SSL_CTX* ctx;
-  SSL*     handle;
-  CURLcode io_result;       /* result of last BIO cfilter operation */
+struct wolfssl_ssl_backend_data {
+  WOLFSSL_CTX *ctx;
+  WOLFSSL     *handle;
+  CURLcode    io_result;   /* result of last BIO cfilter operation */
 };
 
 #ifdef OPENSSL_EXTRA
@@ -180,7 +180,8 @@
   }
 #endif
 
-  if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) {
+  if(wolfSSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) !=
+     SSL_SUCCESS) {
     return;
   }
 
@@ -228,7 +229,7 @@
 
 #ifdef USE_BIO_CHAIN
 
-static int bio_cf_create(WOLFSSL_BIO *bio)
+static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
 {
   wolfSSL_BIO_set_shutdown(bio, 1);
   wolfSSL_BIO_set_init(bio, 1);
@@ -236,14 +237,14 @@
   return 1;
 }
 
-static int bio_cf_destroy(WOLFSSL_BIO *bio)
+static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio)
 {
   if(!bio)
     return 0;
   return 1;
 }
 
-static long bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
+static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   long ret = 1;
@@ -277,29 +278,34 @@
   return ret;
 }
 
-static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
+static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
+                                    const char *buf, int blen)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
-  connssl->backend->io_result = result;
-  DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
-                blen, nwritten, result));
+  backend->io_result = result;
+  CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
+              blen, nwritten, result);
   wolfSSL_BIO_clear_retry_flags(bio);
   if(nwritten < 0 && CURLE_AGAIN == result)
-    BIO_set_retry_read(bio);
+    BIO_set_retry_write(bio);
   return (int)nwritten;
 }
 
-static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
+static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result = CURLE_OK;
@@ -310,36 +316,35 @@
     return 0;
 
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
-  connssl->backend->io_result = result;
-  DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d",
-                blen, nread, result));
+  backend->io_result = result;
+  CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result);
   wolfSSL_BIO_clear_retry_flags(bio);
   if(nread < 0 && CURLE_AGAIN == result)
     BIO_set_retry_read(bio);
   return (int)nread;
 }
 
-static WOLFSSL_BIO_METHOD *bio_cf_method = NULL;
+static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL;
 
-static void bio_cf_init_methods(void)
+static void wolfssl_bio_cf_init_methods(void)
 {
-    bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO");
-    wolfSSL_BIO_meth_set_write(bio_cf_method, &bio_cf_out_write);
-    wolfSSL_BIO_meth_set_read(bio_cf_method, &bio_cf_in_read);
-    wolfSSL_BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl);
-    wolfSSL_BIO_meth_set_create(bio_cf_method, &bio_cf_create);
-    wolfSSL_BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy);
+  wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(BIO_TYPE_MEM, "wolfSSL CF BIO");
+  wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write);
+  wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read);
+  wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl);
+  wolfSSL_BIO_meth_set_create(wolfssl_bio_cf_method, &wolfssl_bio_cf_create);
+  wolfSSL_BIO_meth_set_destroy(wolfssl_bio_cf_method, &wolfssl_bio_cf_destroy);
 }
 
-static void bio_cf_free_methods(void)
+static void wolfssl_bio_cf_free_methods(void)
 {
-    wolfSSL_BIO_meth_free(bio_cf_method);
+  wolfSSL_BIO_meth_free(wolfssl_bio_cf_method);
 }
 
 #else /* USE_BIO_CHAIN */
 
-#define bio_cf_init_methods() Curl_nop_stmt
-#define bio_cf_free_methods() Curl_nop_stmt
+#define wolfssl_bio_cf_init_methods() Curl_nop_stmt
+#define wolfssl_bio_cf_free_methods() Curl_nop_stmt
 
 #endif /* !USE_BIO_CHAIN */
 
@@ -352,10 +357,16 @@
 {
   char *ciphers, *curves;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
   const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  SSL_METHOD* req_method = NULL;
+  const char * const ssl_cafile =
+    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+    (ca_info_blob ? NULL : conn_config->CAfile);
+  const char * const ssl_capath = conn_config->CApath;
+  WOLFSSL_METHOD* req_method = NULL;
 #ifdef HAVE_LIBOQS
   word16 oqsAlg = 0;
   size_t idx = 0;
@@ -366,6 +377,8 @@
 #else
 #define use_sni(x)  Curl_nop_stmt
 #endif
+  bool imported_native_ca = false;
+  bool imported_ca_info_blob = false;
 
   DEBUGASSERT(backend);
 
@@ -410,8 +423,13 @@
 #endif
     break;
   case CURL_SSLVERSION_TLSv1_2:
+#ifndef WOLFSSL_NO_TLS12
     req_method = TLSv1_2_client_method();
     use_sni(TRUE);
+#else
+    failf(data, "wolfSSL does not support TLS 1.2");
+    return CURLE_NOT_BUILT_IN;
+#endif
     break;
   case CURL_SSLVERSION_TLSv1_3:
 #ifdef WOLFSSL_TLS13
@@ -433,8 +451,8 @@
   }
 
   if(backend->ctx)
-    SSL_CTX_free(backend->ctx);
-  backend->ctx = SSL_CTX_new(req_method);
+    wolfSSL_CTX_free(backend->ctx);
+  backend->ctx = wolfSSL_CTX_new(req_method);
 
   if(!backend->ctx) {
     failf(data, "SSL: couldn't create a context");
@@ -494,20 +512,55 @@
       }
     }
   }
+
 #ifndef NO_FILESYSTEM
-  /* load trusted cacert */
-  if(conn_config->CAfile) {
-    if(1 != SSL_CTX_load_verify_locations(backend->ctx,
-                                      conn_config->CAfile,
-                                      conn_config->CApath)) {
-      if(conn_config->verifypeer) {
+  /* load native CA certificates */
+  if(ssl_config->native_ca_store) {
+    if(wolfSSL_CTX_load_system_CA_certs(backend->ctx) != WOLFSSL_SUCCESS) {
+      infof(data, "error importing native CA store, continuing anyway");
+    }
+    else {
+      imported_native_ca = true;
+      infof(data, "successfully imported native CA store");
+    }
+  }
+#endif /* !NO_FILESYSTEM */
+
+  /* load certificate blob */
+  if(ca_info_blob) {
+    if(wolfSSL_CTX_load_verify_buffer(backend->ctx, ca_info_blob->data,
+                                      ca_info_blob->len,
+                                      SSL_FILETYPE_PEM) != SSL_SUCCESS) {
+      if(imported_native_ca) {
+        infof(data, "error importing CA certificate blob, continuing anyway");
+      }
+      else {
+        failf(data, "error importing CA certificate blob");
+        return CURLE_SSL_CACERT_BADFILE;
+      }
+    }
+    else {
+      imported_ca_info_blob = true;
+      infof(data, "successfully imported CA certificate blob");
+    }
+  }
+
+#ifndef NO_FILESYSTEM
+  /* load trusted cacert from file if not blob */
+  if(ssl_cafile || ssl_capath) {
+    int rc =
+      wolfSSL_CTX_load_verify_locations_ex(backend->ctx,
+                                           ssl_cafile,
+                                           ssl_capath,
+                                           WOLFSSL_LOAD_FLAG_IGNORE_ERR);
+    if(SSL_SUCCESS != rc) {
+      if(conn_config->verifypeer && !imported_ca_info_blob &&
+         !imported_native_ca) {
         /* Fail if we insist on successfully verifying the server. */
         failf(data, "error setting certificate verify locations:"
               " CAfile: %s CApath: %s",
-              conn_config->CAfile?
-              conn_config->CAfile: "none",
-              conn_config->CApath?
-              conn_config->CApath : "none");
+              ssl_cafile ? ssl_cafile : "none",
+              ssl_capath ? ssl_capath : "none");
         return CURLE_SSL_CACERT_BADFILE;
       }
       else {
@@ -521,27 +574,25 @@
       /* Everything is fine. */
       infof(data, "successfully set certificate verify locations:");
     }
-    infof(data, " CAfile: %s",
-          conn_config->CAfile ? conn_config->CAfile : "none");
-    infof(data, " CApath: %s",
-          conn_config->CApath ? conn_config->CApath : "none");
+    infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+    infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
   }
 
   /* Load the client certificate, and private key */
   if(ssl_config->primary.clientcert && ssl_config->key) {
     int file_type = do_file_type(ssl_config->cert_type);
 
-    if(SSL_CTX_use_certificate_file(backend->ctx,
-                                    ssl_config->primary.clientcert,
-                                    file_type) != 1) {
+    if(wolfSSL_CTX_use_certificate_file(backend->ctx,
+                                        ssl_config->primary.clientcert,
+                                        file_type) != 1) {
       failf(data, "unable to use client certificate (no key or wrong pass"
             " phrase?)");
       return CURLE_SSL_CONNECT_ERROR;
     }
 
     file_type = do_file_type(ssl_config->key_type);
-    if(SSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key,
-                                    file_type) != 1) {
+    if(wolfSSL_CTX_use_PrivateKey_file(backend->ctx, ssl_config->key,
+                                       file_type) != 1) {
       failf(data, "unable to set private key");
       return CURLE_SSL_CONNECT_ERROR;
     }
@@ -552,10 +603,9 @@
    * fail to connect if the verification fails, or if it should continue
    * anyway. In the latter case the result of the verification is checked with
    * SSL_get_verify_result() below. */
-  SSL_CTX_set_verify(backend->ctx,
-                     conn_config->verifypeer?SSL_VERIFY_PEER:
-                                             SSL_VERIFY_NONE,
-                     NULL);
+  wolfSSL_CTX_set_verify(backend->ctx,
+                         conn_config->verifypeer?SSL_VERIFY_PEER:
+                         SSL_VERIFY_NONE, NULL);
 
 #ifdef HAVE_SNI
   if(sni) {
@@ -604,8 +654,8 @@
 
   /* Let's make an SSL structure */
   if(backend->handle)
-    SSL_free(backend->handle);
-  backend->handle = SSL_new(backend->ctx);
+    wolfSSL_free(backend->handle);
+  backend->handle = wolfSSL_new(backend->ctx);
   if(!backend->handle) {
     failf(data, "SSL: couldn't create a handle");
     return CURLE_OUT_OF_MEMORY;
@@ -665,7 +715,7 @@
         infof(data, "Can't use session ID, going on without");
       }
       else
-        infof(data, "SSL re-using session ID");
+        infof(data, "SSL reusing session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
@@ -674,7 +724,7 @@
   {
     WOLFSSL_BIO *bio;
 
-    bio = BIO_new(bio_cf_method);
+    bio = BIO_new(wolfssl_bio_cf_method);
     if(!bio)
       return CURLE_OUT_OF_MEMORY;
 
@@ -683,7 +733,8 @@
   }
 #else /* USE_BIO_CHAIN */
   /* pass the raw socket into the SSL layer */
-  if(!SSL_set_fd(backend->handle, (int)Curl_conn_cf_get_socket(cf, data))) {
+  if(!wolfSSL_set_fd(backend->handle,
+                     (int)Curl_conn_cf_get_socket(cf, data))) {
     failf(data, "SSL: SSL_set_fd failed");
     return CURLE_SSL_CONNECT_ERROR;
   }
@@ -699,7 +750,8 @@
 {
   int ret = -1;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
@@ -707,7 +759,7 @@
 
   DEBUGASSERT(backend);
 
-  ERR_clear_error();
+  wolfSSL_ERR_clear_error();
 
   /* Enable RFC2818 checks */
   if(conn_config->verifyhost) {
@@ -717,7 +769,7 @@
       return CURLE_SSL_CONNECT_ERROR;
   }
 
-  ret = SSL_connect(backend->handle);
+  ret = wolfSSL_connect(backend->handle);
 
 #ifdef OPENSSL_EXTRA
   if(Curl_tls_keylog_enabled()) {
@@ -745,7 +797,7 @@
 
   if(ret != 1) {
     char error_buffer[WOLFSSL_MAX_ERROR_SZ];
-    int  detail = SSL_get_error(backend->handle, ret);
+    int  detail = wolfSSL_get_error(backend->handle, ret);
 
     if(SSL_ERROR_WANT_READ == detail) {
       connssl->connecting_state = ssl_connect_2_reading;
@@ -803,7 +855,7 @@
     }
     else {
       failf(data, "SSL_connect failed with error %d: %s", detail,
-          ERR_error_string(detail, error_buffer));
+            wolfSSL_ERR_error_string(detail, error_buffer));
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
@@ -817,7 +869,7 @@
     struct Curl_asn1Element *pubkey;
     CURLcode result;
 
-    x509 = SSL_get_peer_certificate(backend->handle);
+    x509 = wolfSSL_get_peer_certificate(backend->handle);
     if(!x509) {
       failf(data, "SSL: failed retrieving server certificate");
       return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
@@ -892,7 +944,8 @@
 {
   CURLcode result = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -902,8 +955,8 @@
     bool incache;
     bool added = FALSE;
     void *old_ssl_sessionid = NULL;
-    /* SSL_get1_session allocates memory that has to be freed. */
-    SSL_SESSION *our_ssl_sessionid = SSL_get1_session(backend->handle);
+    /* wolfSSL_get1_session allocates memory that has to be freed. */
+    WOLFSSL_SESSION *our_ssl_sessionid = wolfSSL_get1_session(backend->handle);
 
     if(our_ssl_sessionid) {
       Curl_ssl_sessionid_lock(data);
@@ -920,7 +973,7 @@
         result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL);
         if(result) {
           Curl_ssl_sessionid_unlock(data);
-          SSL_SESSION_free(our_ssl_sessionid);
+          wolfSSL_SESSION_free(our_ssl_sessionid);
           failf(data, "failed to store ssl session");
           return result;
         }
@@ -932,7 +985,7 @@
 
       if(!added) {
         /* If the session info wasn't added to the cache, free our copy. */
-        SSL_SESSION_free(our_ssl_sessionid);
+        wolfSSL_SESSION_free(our_ssl_sessionid);
       }
     }
   }
@@ -950,49 +1003,50 @@
                             CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   char error_buffer[WOLFSSL_MAX_ERROR_SZ];
   int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
   int rc;
 
   DEBUGASSERT(backend);
 
-  ERR_clear_error();
+  wolfSSL_ERR_clear_error();
 
-  rc = SSL_write(backend->handle, mem, memlen);
+  rc = wolfSSL_write(backend->handle, mem, memlen);
   if(rc <= 0) {
-    int err = SSL_get_error(backend->handle, rc);
+    int err = wolfSSL_get_error(backend->handle, rc);
 
     switch(err) {
     case SSL_ERROR_WANT_READ:
     case SSL_ERROR_WANT_WRITE:
       /* there's data pending, re-invoke SSL_write() */
-      DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len));
+      CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
       *curlcode = CURLE_AGAIN;
       return -1;
     default:
       if(backend->io_result == CURLE_AGAIN) {
-        DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len));
+        CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
         *curlcode = CURLE_AGAIN;
         return -1;
       }
-      DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d",
-                    len, rc, err));
+      CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err);
       failf(data, "SSL write: %s, errno %d",
-            ERR_error_string(err, error_buffer),
+            wolfSSL_ERR_error_string(err, error_buffer),
             SOCKERRNO);
       *curlcode = CURLE_SEND_ERROR;
       return -1;
     }
   }
-  DEBUGF(LOG_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc));
+  CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc);
   return rc;
 }
 
 static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
 
   (void) data;
 
@@ -1002,13 +1056,13 @@
     char buf[32];
     /* Maybe the server has already sent a close notify alert.
        Read it to avoid an RST on the TCP connection. */
-    (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
-    (void)SSL_shutdown(backend->handle);
-    SSL_free(backend->handle);
+    (void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf));
+    (void)wolfSSL_shutdown(backend->handle);
+    wolfSSL_free(backend->handle);
     backend->handle = NULL;
   }
   if(backend->ctx) {
-    SSL_CTX_free(backend->ctx);
+    wolfSSL_CTX_free(backend->ctx);
     backend->ctx = NULL;
   }
 }
@@ -1019,24 +1073,25 @@
                             CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   char error_buffer[WOLFSSL_MAX_ERROR_SZ];
   int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
   int nread;
 
   DEBUGASSERT(backend);
 
-  ERR_clear_error();
+  wolfSSL_ERR_clear_error();
   *curlcode = CURLE_OK;
 
-  nread = SSL_read(backend->handle, buf, buffsize);
+  nread = wolfSSL_read(backend->handle, buf, buffsize);
 
   if(nread <= 0) {
-    int err = SSL_get_error(backend->handle, nread);
+    int err = wolfSSL_get_error(backend->handle, nread);
 
     switch(err) {
     case SSL_ERROR_ZERO_RETURN: /* no more data */
-      DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen));
+      CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
       *curlcode = CURLE_OK;
       return 0;
     case SSL_ERROR_NONE:
@@ -1044,30 +1099,30 @@
     case SSL_ERROR_WANT_READ:
       /* FALLTHROUGH */
     case SSL_ERROR_WANT_WRITE:
-      /* there's data pending, re-invoke SSL_read() */
-      DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen));
+      /* there's data pending, re-invoke wolfSSL_read() */
+      CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
       *curlcode = CURLE_AGAIN;
       return -1;
     default:
       if(backend->io_result == CURLE_AGAIN) {
-        DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen));
+        CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
         *curlcode = CURLE_AGAIN;
         return -1;
       }
       failf(data, "SSL read: %s, errno %d",
-            ERR_error_string(err, error_buffer), SOCKERRNO);
+            wolfSSL_ERR_error_string(err, error_buffer), SOCKERRNO);
       *curlcode = CURLE_RECV_ERROR;
       return -1;
     }
   }
-  DEBUGF(LOG_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread));
+  CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread);
   return nread;
 }
 
 
 static void wolfssl_session_free(void *ptr)
 {
-  SSL_SESSION_free(ptr);
+  wolfSSL_SESSION_free(ptr);
 }
 
 
@@ -1089,14 +1144,14 @@
   Curl_tls_keylog_open();
 #endif
   ret = (wolfSSL_Init() == SSL_SUCCESS);
-  bio_cf_init_methods();
+  wolfssl_bio_cf_init_methods();
   return ret;
 }
 
 
 static void wolfssl_cleanup(void)
 {
-  bio_cf_free_methods();
+  wolfssl_bio_cf_free_methods();
   wolfSSL_Cleanup();
 #ifdef OPENSSL_EXTRA
   Curl_tls_keylog_close();
@@ -1108,11 +1163,14 @@
                                  const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->handle)   /* SSL is in use */
-    return (0 != SSL_pending(ctx->backend->handle)) ? TRUE : FALSE;
+
+  backend = (struct wolfssl_ssl_backend_data *)ctx->backend;
+  if(backend->handle)   /* SSL is in use */
+    return (0 != wolfSSL_pending(backend->handle)) ? TRUE : FALSE;
   else
     return FALSE;
 }
@@ -1126,15 +1184,17 @@
                             struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend;
   int retval = 0;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
 
-  if(ctx->backend->handle) {
-    ERR_clear_error();
-    SSL_free(ctx->backend->handle);
-    ctx->backend->handle = NULL;
+  backend = (struct wolfssl_ssl_backend_data *)ctx->backend;
+  if(backend->handle) {
+    wolfSSL_ERR_clear_error();
+    wolfSSL_free(backend->handle);
+    backend->handle = NULL;
   }
   return retval;
 }
@@ -1296,7 +1356,8 @@
 {
   wc_Sha256 SHA256pw;
   (void)unused;
-  wc_InitSha256(&SHA256pw);
+  if(wc_InitSha256(&SHA256pw))
+    return CURLE_FAILED_INIT;
   wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen);
   wc_Sha256Final(&SHA256pw, sha256sum);
   return CURLE_OK;
@@ -1305,7 +1366,8 @@
 static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->handle;
@@ -1320,9 +1382,11 @@
 #ifdef USE_BIO_CHAIN
   SSLSUPP_HTTPS_PROXY |
 #endif
+  SSLSUPP_CA_PATH |
+  SSLSUPP_CAINFO_BLOB |
   SSLSUPP_SSL_CTX,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct wolfssl_ssl_backend_data),
 
   wolfssl_init,                    /* init */
   wolfssl_cleanup,                 /* cleanup */
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.c b/Utilities/cmcurl/lib/vtls/x509asn1.c
index acf8bdb..c3fd3a3 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.c
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.c
@@ -24,24 +24,18 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) ||    \
-  defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) ||      \
+  defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
 
-#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
 #define WANT_PARSEX509 /* uses Curl_parseX509() */
 #endif
 
-#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) ||    \
-  defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
 #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
 #define WANT_PARSEX509 /* ... uses Curl_parseX509() */
 #endif
 
-#if defined(USE_GSKIT)
-#define WANT_VERIFYHOST /* uses Curl_verifyhost () */
-#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
-#endif
-
 #include <curl/curl.h>
 #include "urldata.h"
 #include "strcase.h"
@@ -973,7 +967,7 @@
       infof(data, "   ECC Public Key (%lu bits)", len);
     if(data->set.ssl.certinfo) {
       char q[sizeof(len) * 8 / 3 + 1];
-      (void)msnprintf(q, sizeof(q), "%lu", len);
+      (void)msnprintf(q, sizeof(q), "%zu", len);
       if(ssl_push_certinfo(data, certnum, "ECC Public Key", q))
         return 1;
     }
@@ -1007,7 +1001,7 @@
       infof(data, "   RSA Public Key (%lu bits)", len);
     if(data->set.ssl.certinfo) {
       char r[sizeof(len) * 8 / 3 + 1];
-      msnprintf(r, sizeof(r), "%lu", len);
+      msnprintf(r, sizeof(r), "%zu", len);
       if(ssl_push_certinfo(data, certnum, "RSA Public Key", r))
         return 1;
     }
@@ -1261,8 +1255,7 @@
 
 #endif /* WANT_EXTRACT_CERTINFO */
 
-#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
-        * or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
 
 #ifdef WANT_VERIFYHOST
 
diff --git a/Utilities/cmcurl/lib/vtls/x509asn1.h b/Utilities/cmcurl/lib/vtls/x509asn1.h
index 5496de4..23a67b8 100644
--- a/Utilities/cmcurl/lib/vtls/x509asn1.h
+++ b/Utilities/cmcurl/lib/vtls/x509asn1.h
@@ -27,8 +27,8 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \
-    defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
+#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
+  defined(USE_SCHANNEL) || defined(USE_SECTRANSP)
 
 #include "cfilters.h"
 #include "urldata.h"
@@ -76,6 +76,5 @@
                                const char *beg, const char *end);
 CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
                          const char *beg, const char *end);
-#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
-        * or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
 #endif /* HEADER_CURL_X509ASN1_H */
diff --git a/Utilities/cmcurl/lib/warnless.c b/Utilities/cmcurl/lib/warnless.c
index 10c91fb..7e077f8 100644
--- a/Utilities/cmcurl/lib/warnless.c
+++ b/Utilities/cmcurl/lib/warnless.c
@@ -35,10 +35,13 @@
 
 #endif /* __INTEL_COMPILER && __unix__ */
 
-#define BUILDING_WARNLESS_C 1
-
 #include "warnless.h"
 
+#ifdef WIN32
+#undef read
+#undef write
+#endif
+
 #include <limits.h>
 
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
@@ -376,56 +379,8 @@
   return (ssize_t)write(fd, buf, curlx_uztoui(count));
 }
 
+/* Ensure that warnless.h continues to have an effect in "unity" builds. */
+#undef HEADER_CURL_WARNLESS_H
+
 #endif /* WIN32 */
 
-#if defined(__INTEL_COMPILER) && defined(__unix__)
-
-int curlx_FD_ISSET(int fd, fd_set *fdset)
-{
-  #pragma warning(push)
-  #pragma warning(disable:1469) /* clobber ignored */
-  return FD_ISSET(fd, fdset);
-  #pragma warning(pop)
-}
-
-void curlx_FD_SET(int fd, fd_set *fdset)
-{
-  #pragma warning(push)
-  #pragma warning(disable:1469) /* clobber ignored */
-  FD_SET(fd, fdset);
-  #pragma warning(pop)
-}
-
-void curlx_FD_ZERO(fd_set *fdset)
-{
-  #pragma warning(push)
-  #pragma warning(disable:593) /* variable was set but never used */
-  FD_ZERO(fdset);
-  #pragma warning(pop)
-}
-
-unsigned short curlx_htons(unsigned short usnum)
-{
-#if (__INTEL_COMPILER == 910) && defined(__i386__)
-  return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
-#else
-  #pragma warning(push)
-  #pragma warning(disable:810) /* conversion may lose significant bits */
-  return htons(usnum);
-  #pragma warning(pop)
-#endif
-}
-
-unsigned short curlx_ntohs(unsigned short usnum)
-{
-#if (__INTEL_COMPILER == 910) && defined(__i386__)
-  return (unsigned short)(((usnum << 8) & 0xFF00) | ((usnum >> 8) & 0x00FF));
-#else
-  #pragma warning(push)
-  #pragma warning(disable:810) /* conversion may lose significant bits */
-  return ntohs(usnum);
-  #pragma warning(pop)
-#endif
-}
-
-#endif /* __INTEL_COMPILER && __unix__ */
diff --git a/Utilities/cmcurl/lib/warnless.h b/Utilities/cmcurl/lib/warnless.h
index 99b2433..2a53016 100644
--- a/Utilities/cmcurl/lib/warnless.h
+++ b/Utilities/cmcurl/lib/warnless.h
@@ -75,12 +75,10 @@
 
 ssize_t curlx_write(int fd, const void *buf, size_t count);
 
-#ifndef BUILDING_WARNLESS_C
-#  undef  read
-#  define read(fd, buf, count)  curlx_read(fd, buf, count)
-#  undef  write
-#  define write(fd, buf, count) curlx_write(fd, buf, count)
-#endif
+#undef  read
+#define read(fd, buf, count)  curlx_read(fd, buf, count)
+#undef  write
+#define write(fd, buf, count) curlx_write(fd, buf, count)
 
 #endif /* WIN32 */
 
diff --git a/Utilities/cmcurl/lib/ws.c b/Utilities/cmcurl/lib/ws.c
index c60bbc9..3c1964b 100644
--- a/Utilities/cmcurl/lib/ws.c
+++ b/Utilities/cmcurl/lib/ws.c
@@ -126,8 +126,9 @@
             dec->head_len, dec->head_total);
     }
     else {
-      infof(data, "WS-DEC: %s [%s%s payload=%zd/%zd]", msg,
-            ws_frame_name_of_op(dec->head[0]),
+      infof(data, "WS-DEC: %s [%s%s payload=%" CURL_FORMAT_CURL_OFF_T
+                  "/%" CURL_FORMAT_CURL_OFF_T "]",
+            msg, ws_frame_name_of_op(dec->head[0]),
             (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL",
             dec->payload_offset, dec->payload_len);
     }
@@ -272,7 +273,8 @@
     Curl_bufq_skip(inraw, (size_t)nwritten);
     dec->payload_offset += (curl_off_t)nwritten;
     remain = dec->payload_len - dec->payload_offset;
-    /* infof(data, "WS-DEC: passed  %zd bytes payload, %zd remain",
+    /* infof(data, "WS-DEC: passed  %zd bytes payload, %"
+                CURL_FORMAT_CURL_OFF_T " remain",
           nwritten, remain); */
   }
 
@@ -351,8 +353,9 @@
 static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
                         const char *msg)
 {
-  infof(data, "WS-ENC: %s [%s%s%s payload=%zd/%zd]", msg,
-        ws_frame_name_of_op(enc->firstbyte),
+  infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T
+              "/%" CURL_FORMAT_CURL_OFF_T "]",
+        msg, ws_frame_name_of_op(enc->firstbyte),
         (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ?
         " CONT" : "",
         (enc->firstbyte & WSBIT_FIN)? "" : " NON-FIN",
@@ -839,7 +842,7 @@
 
 CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
                                   size_t buflen, size_t *nread,
-                                  struct curl_ws_frame **metap)
+                                  const struct curl_ws_frame **metap)
 {
   struct connectdata *conn = data->conn;
   struct websocket *ws;
@@ -921,7 +924,8 @@
               ctx.payload_len, ctx.bufidx);
   *metap = &ws->frame;
   *nread = ws->frame.len;
-  /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %zd, %zd left)",
+  /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
+              CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)",
         buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */
   return CURLE_OK;
 }
@@ -966,10 +970,10 @@
   return CURLE_OK;
 }
 
-CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
+CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
                                   size_t buflen, size_t *sent,
-                                  curl_off_t totalsize,
-                                  unsigned int sendflags)
+                                  curl_off_t fragsize,
+                                  unsigned int flags)
 {
   struct websocket *ws;
   ssize_t nwritten, n;
@@ -987,14 +991,13 @@
     return CURLE_SEND_ERROR;
   }
   if(!data->conn->proto.ws) {
-    failf(data, "Not a websocket transfer on connection #%ld",
-          data->conn->connection_id);
+    failf(data, "Not a websocket transfer");
     return CURLE_SEND_ERROR;
   }
   ws = data->conn->proto.ws;
 
   if(data->set.ws_raw_mode) {
-    if(totalsize || sendflags)
+    if(fragsize || flags)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     if(!buflen)
       /* nothing to do */
@@ -1027,23 +1030,24 @@
   if(space < 14)
     return CURLE_AGAIN;
 
-  if(sendflags & CURLWS_OFFSET) {
-    if(totalsize) {
-      /* a frame series 'totalsize' bytes big, this is the first */
-      n = ws_enc_write_head(data, &ws->enc, sendflags, totalsize,
+  if(flags & CURLWS_OFFSET) {
+    if(fragsize) {
+      /* a frame series 'fragsize' bytes big, this is the first */
+      n = ws_enc_write_head(data, &ws->enc, flags, fragsize,
                             &ws->sendbuf, &result);
       if(n < 0)
         return result;
     }
     else {
       if((curl_off_t)buflen > ws->enc.payload_remain) {
-        infof(data, "WS: unaligned frame size (sending %zu instead of %zd)",
+        infof(data, "WS: unaligned frame size (sending %zu instead of %"
+                    CURL_FORMAT_CURL_OFF_T ")",
               buflen, ws->enc.payload_remain);
       }
     }
   }
   else if(!ws->enc.payload_remain) {
-    n = ws_enc_write_head(data, &ws->enc, sendflags, (curl_off_t)buflen,
+    n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen,
                           &ws->sendbuf, &result);
     if(n < 0)
       return result;
@@ -1082,7 +1086,7 @@
   return CURLE_OK;
 }
 
-CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
 {
   /* we only return something for websocket, called from within the callback
      when not using raw mode */
@@ -1096,7 +1100,7 @@
 
 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
                                   size_t *nread,
-                                  struct curl_ws_frame **metap)
+                                  const struct curl_ws_frame **metap)
 {
   (void)curl;
   (void)buffer;
@@ -1108,19 +1112,19 @@
 
 CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
                                   size_t buflen, size_t *sent,
-                                  curl_off_t framesize,
-                                  unsigned int sendflags)
+                                  curl_off_t fragsize,
+                                  unsigned int flags)
 {
   (void)curl;
   (void)buffer;
   (void)buflen;
   (void)sent;
-  (void)framesize;
-  (void)sendflags;
+  (void)fragsize;
+  (void)flags;
   return CURLE_NOT_BUILT_IN;
 }
 
-CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
 {
   (void)data;
   return NULL;
diff --git a/Utilities/cmlibarchive/libarchive/CMakeLists.txt b/Utilities/cmlibarchive/libarchive/CMakeLists.txt
index bee69c2..e820853 100644
--- a/Utilities/cmlibarchive/libarchive/CMakeLists.txt
+++ b/Utilities/cmlibarchive/libarchive/CMakeLists.txt
@@ -246,6 +246,10 @@
 ADD_LIBRARY(cmlibarchive STATIC ${libarchive_SOURCES} ${include_HEADERS})
 TARGET_LINK_LIBRARIES(cmlibarchive ${ADDITIONAL_LIBS})
 
+if(WIN32 AND CMake_BUILD_PCH)
+  target_precompile_headers(cmlibarchive PRIVATE "archive_platform.h" "archive_entry.h" "<cm3p/bzlib.h>")
+endif()
+
 IF(0) # CMake does not build libarchive's full package.
 # Libarchive is a shared library
 ADD_LIBRARY(archive SHARED ${libarchive_SOURCES} ${include_HEADERS})
diff --git a/Utilities/cmliblzma/CMakeLists.txt b/Utilities/cmliblzma/CMakeLists.txt
index 3121fbe..3ba3ce9 100644
--- a/Utilities/cmliblzma/CMakeLists.txt
+++ b/Utilities/cmliblzma/CMakeLists.txt
@@ -186,4 +186,8 @@
   SET_PROPERTY(TARGET cmliblzma PROPERTY COMPILE_FLAGS "-O0")
 ENDIF()
 
+if(WIN32 AND CMake_BUILD_PCH)
+  target_precompile_headers(cmliblzma PRIVATE "common/mythread.h")
+endif()
+
 INSTALL(FILES COPYING DESTINATION ${CMAKE_DOC_DIR}/cmliblzma)
diff --git a/Utilities/cmlibuv/CMakeLists.txt b/Utilities/cmlibuv/CMakeLists.txt
index ad3d433..a0b161b 100644
--- a/Utilities/cmlibuv/CMakeLists.txt
+++ b/Utilities/cmlibuv/CMakeLists.txt
@@ -294,27 +294,13 @@
     )
   list(APPEND uv_defines
     __EXTENSIONS__
+    _XOPEN_SOURCE=600
     )
+  if(NOT CMAKE_C_STANDARD OR CMAKE_C_STANDARD EQUAL 90)
+    set(CMAKE_C_STANDARD 11)
+  endif()
   if(CMAKE_SYSTEM_VERSION STREQUAL "5.10")
-    set(CMAKE_C_STANDARD 90)
-    if(CMAKE_VERSION VERSION_LESS 3.8.20170504 AND CMAKE_C_COMPILER_ID STREQUAL "SunPro" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.14)
-      # The running version of CMake does not know how to add this flag.
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c90")
-    endif()
-    list(APPEND uv_defines
-      _XOPEN_SOURCE=500
-      )
-  else()
-    if(NOT CMAKE_C_STANDARD OR CMAKE_C_STANDARD EQUAL 90)
-      set(CMAKE_C_STANDARD 11)
-    endif()
-    if(CMAKE_VERSION VERSION_LESS 3.8.20170505 AND CMAKE_C_COMPILER_ID STREQUAL "SunPro" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.14)
-      # The running version of CMake does not know how to add this flag.
-      set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -xc99")
-    endif()
-    list(APPEND uv_defines
-      _XOPEN_SOURCE=600
-      )
+    list(APPEND uv_defines SUNOS_NO_IFADDRS)
   endif()
   list(APPEND uv_sources
     src/unix/no-proctitle.c
@@ -365,4 +351,8 @@
 target_link_libraries(cmlibuv ${uv_libraries})
 set_property(TARGET cmlibuv PROPERTY COMPILE_DEFINITIONS ${uv_defines})
 
+if(WIN32 AND CMake_BUILD_PCH)
+  target_precompile_headers(cmlibuv PRIVATE "include/uv.h" "src/win/internal.h")
+endif()
+
 install(FILES LICENSE DESTINATION ${CMAKE_DOC_DIR}/cmlibuv)
diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c
index 0de5c46..30872cf 100644
--- a/Utilities/cmlibuv/src/unix/process.c
+++ b/Utilities/cmlibuv/src/unix/process.c
@@ -75,7 +75,9 @@
 #endif
 #endif
 
-#if defined(__APPLE__) || \
+#ifdef CMAKE_BOOTSTRAP
+#define UV_USE_SIGCHLD
+#elif defined(__APPLE__) || \
     defined(__DragonFly__) || \
     defined(__FreeBSD__) || \
     defined(__NetBSD__) || \
diff --git a/Utilities/cmlibuv/src/win/process.c b/Utilities/cmlibuv/src/win/process.c
index 248b7ea..14d7a69 100644
--- a/Utilities/cmlibuv/src/win/process.c
+++ b/Utilities/cmlibuv/src/win/process.c
@@ -102,6 +102,21 @@
                                &info,
                                sizeof info))
     uv_fatal_error(GetLastError(), "SetInformationJobObject");
+
+
+  if (!AssignProcessToJobObject(uv_global_job_handle_, GetCurrentProcess())) {
+    /* Make sure this handle is functional. The Windows kernel has a bug that
+     * if the first use of AssignProcessToJobObject is for a Windows Store
+     * program, subsequent attempts to use the handle with fail with
+     * INVALID_PARAMETER (87). This is possibly because all uses of the handle
+     * must be for the same Terminal Services session. We can ensure it is tied
+     * to our current session now by adding ourself to it. We could remove
+     * ourself afterwards, but there doesn't seem to be a reason to.
+     */
+    DWORD err = GetLastError();
+    if (err != ERROR_ACCESS_DENIED)
+      uv_fatal_error(err, "AssignProcessToJobObject");
+  }
 }
 
 
@@ -394,7 +409,7 @@
                                   name_has_ext);
 
     while (result == NULL) {
-      if (*dir_end == L'\0') {
+      if (dir_end == NULL || *dir_end == L'\0') {
         break;
       }
 
@@ -1027,22 +1042,19 @@
     DWORD path_len, r;
 
     path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
-    if (path_len == 0) {
-      err = GetLastError();
-      goto done;
-    }
+    if (path_len != 0) {
+      alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
+      if (alloc_path == NULL) {
+        err = ERROR_OUTOFMEMORY;
+        goto done;
+      }
+      path = alloc_path;
 
-    alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR));
-    if (alloc_path == NULL) {
-      err = ERROR_OUTOFMEMORY;
-      goto done;
-    }
-    path = alloc_path;
-
-    r = GetEnvironmentVariableW(L"PATH", path, path_len);
-    if (r == 0 || r >= path_len) {
-      err = GetLastError();
-      goto done;
+      r = GetEnvironmentVariableW(L"PATH", path, path_len);
+      if (r == 0 || r >= path_len) {
+        err = GetLastError();
+        goto done;
+      }
     }
   }
 
@@ -1104,6 +1116,7 @@
      * breakaway.
      */
     process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP;
+    process_flags |= CREATE_SUSPENDED;
   }
 
   if (options->cpumask != NULL) {
@@ -1162,20 +1175,8 @@
       TerminateProcess(info.hProcess, 1);
       goto done;
     }
-
-    /* The process affinity of the child is set.  Let it run.  */
-    if (ResumeThread(info.hThread) == ((DWORD)-1)) {
-      err = GetLastError();
-      TerminateProcess(info.hProcess, 1);
-      goto done;
-    }
   }
 
-  /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
-
-  process->process_handle = info.hProcess;
-  process->pid = info.dwProcessId;
-
   /* If the process isn't spawned as detached, assign to the global job object
    * so windows will kill it when the parent process dies. */
   if (!(options->flags & UV_PROCESS_DETACHED)) {
@@ -1198,6 +1199,19 @@
     }
   }
 
+  if (process_flags & CREATE_SUSPENDED) {
+    if (ResumeThread(info.hThread) == ((DWORD)-1)) {
+      err = GetLastError();
+      TerminateProcess(info.hProcess, 1);
+      goto done;
+    }
+  }
+
+  /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */
+
+  process->process_handle = info.hProcess;
+  process->pid = info.dwProcessId;
+
   /* Set IPC pid to all IPC pipes. */
   for (i = 0; i < options->stdio_count; i++) {
     const uv_stdio_container_t* fdopt = &options->stdio[i];
diff --git a/Utilities/std/cmext/algorithm b/Utilities/std/cmext/algorithm
index 11514fc..46377f4 100644
--- a/Utilities/std/cmext/algorithm
+++ b/Utilities/std/cmext/algorithm
@@ -16,6 +16,7 @@
 
 #if defined(__SUNPRO_CC) && defined(__sparc)
 #  include <list>
+#  include <string>
 #  include <vector>
 #endif
 
@@ -67,11 +68,15 @@
     APPEND_TWO(C1, C2)                                                        \
     APPEND_TWO(C2, C1)
 
-// For now, manage only support for std::vector and std::list.
-// Other sequential container support can be added if needed.
+// For now, manage only support for std::vector, std::list, and
+// std::basic_string. Other sequential container support can be added if
+// needed.
 APPEND(std::vector)
 APPEND(std::list)
+APPEND(std::basic_string)
 APPEND_MIX(std::vector, std::list)
+APPEND_MIX(std::vector, std::basic_string)
+APPEND_MIX(std::list, std::basic_string)
 
 #  undef APPEND
 #  undef APPEND_MIX
diff --git a/Utilities/std/cmext/iterator b/Utilities/std/cmext/iterator
index eba10dd..85a28fa 100644
--- a/Utilities/std/cmext/iterator
+++ b/Utilities/std/cmext/iterator
@@ -24,9 +24,18 @@
 
 // checks if a type is a range type: std::begin() and std::end() are supported
 template <typename Range>
-using is_range = cm::bool_constant<
-  cm::is_iterator<decltype(std::begin(std::declval<const Range>()))>::value &&
-  cm::is_iterator<decltype(std::end(std::declval<const Range>()))>::value>;
+using is_range =
+#if defined(_MSC_VER) && _MSC_VER < 1920
+  // MS C++ is not able to evaluate complex type introspection,
+  // so use a simplified version
+  cm::bool_constant<std::is_class<Range>::value ||
+                    std::is_array<Range>::value>;
+#else
+  cm::bool_constant<
+    cm::is_iterator<decltype(std::begin(
+      std::declval<const Range>()))>::value &&
+    cm::is_iterator<decltype(std::end(std::declval<const Range>()))>::value>;
+#endif
 
 // checks if a type is an input range type: std::begin() and std::end() are
 // returning an input iterator
diff --git a/bootstrap b/bootstrap
index cc60425..2b7ddfe 100755
--- a/bootstrap
+++ b/bootstrap
@@ -333,6 +333,7 @@
   cmCustomCommandGenerator \
   cmCustomCommandLines \
   cmCxxModuleMapper \
+  cmCxxModuleUsageEffects \
   cmDefinePropertyCommand \
   cmDefinitions \
   cmDocumentationFormatter \
@@ -392,6 +393,7 @@
   cmGlobVerificationManager \
   cmHexFileConverter \
   cmIfCommand \
+  cmImportedCxxModuleInfo \
   cmIncludeCommand \
   cmIncludeGuardCommand \
   cmIncludeDirectoryCommand \
@@ -414,6 +416,8 @@
   cmInstallTargetGenerator \
   cmInstallTargetsCommand \
   cmInstalledFile \
+  cmJSONHelpers \
+  cmJSONState \
   cmLDConfigLDConfigTool \
   cmLDConfigTool \
   cmLinkDirectoriesCommand \
@@ -450,6 +454,7 @@
   cmGccDepfileReader \
   cmReturnCommand \
   cmPlaceholderExpander \
+  cmPlistParser \
   cmRulePlaceholderExpander \
   cmRuntimeDependencyArchive \
   cmScriptGenerator \
@@ -499,9 +504,11 @@
   cmWhileCommand \
   cmWindowsRegistry \
   cmWorkingDirectory \
+  cmXcFramework \
   cmake  \
   cmakemain \
   cmcmd  \
+  cm_fileno \
 "
 
 if ${cmake_system_mingw}; then
@@ -588,6 +595,12 @@
   librhash/sha512.c \
   "
 
+JSONCPP_CXX_SOURCES="\
+  src/lib_json/json_reader.cpp \
+  src/lib_json/json_value.cpp \
+  src/lib_json/json_writer.cpp \
+  "
+
 if ${cmake_system_mingw}; then
   LIBUV_C_SOURCES="\
     src/fs-poll.c \
@@ -1091,12 +1104,6 @@
     cmFortranLexer \
     cmFortranParser \
     "
-
-  JSONCPP_CXX_SOURCES="\
-    src/lib_json/json_reader.cpp \
-    src/lib_json/json_value.cpp \
-    src/lib_json/json_writer.cpp \
-    "
 else
   CMAKE_CXX_SOURCES="${CMAKE_CXX_SOURCES} \
     cmDepends \
@@ -1110,8 +1117,6 @@
     cmMakefileUtilityTargetGenerator \
     cmProcessTools \
     "
-
-  JSONCPP_CXX_SOURCES=
 fi
 
 # Add Cygwin-specific flags
@@ -1725,12 +1730,10 @@
     objs="${objs} rhash-`cmake_obj ${a}`"
   done
 fi
-if test "${cmake_bootstrap_generator}" = "Ninja"; then
-  if test "x${bootstrap_system_jsoncpp}" = "x"; then
-    for a in ${JSONCPP_CXX_SOURCES}; do
-      objs="${objs} jsoncpp-`cmake_obj ${a}`"
-    done
-  fi
+if test "x${bootstrap_system_jsoncpp}" = "x"; then
+  for a in ${JSONCPP_CXX_SOURCES}; do
+    objs="${objs} jsoncpp-`cmake_obj ${a}`"
+  done
 fi
 
 libs=""
@@ -1762,8 +1765,6 @@
       libs="${libs} -lkvm"
       ;;
     *SunOS*)
-      # Normally libuv uses '-D_XOPEN_SOURCE=500 -std=c90' on Solaris 5.10,
-      # but we do not need to do that because we bootstrap using POSIX APIs.
       uv_c_flags="${uv_c_flags} -D__EXTENSIONS__ -D_XOPEN_SOURCE=600"
       libs="${libs} -lkstat -lnsl -lsendfile -lsocket -lrt"
       ;;
@@ -1799,17 +1800,15 @@
   libs="${libs} -lrhash"
 fi
 
-if test "${cmake_bootstrap_generator}" = "Ninja"; then
-  jsoncpp_cxx_flags=
-  if test "x${bootstrap_system_jsoncpp}" = "x"; then
-    jsoncpp_cxx_flags="${jsoncpp_cxx_flags} `cmake_escape_shell "-I${cmake_source_dir}/Utilities/cmjsoncpp/include"`"
-  else
-    if test `which pkg-config`; then
-      use_jsoncpp_flags="`pkg-config --cflags jsoncpp`"
-      cmake_cxx_flags="${cmake_cxx_flags} ${use_jsoncpp_flags}"
-    fi
-    libs="${libs} -ljsoncpp"
+jsoncpp_cxx_flags=
+if test "x${bootstrap_system_jsoncpp}" = "x"; then
+  jsoncpp_cxx_flags="${jsoncpp_cxx_flags} `cmake_escape_shell "-I${cmake_source_dir}/Utilities/cmjsoncpp/include"`"
+else
+  if test `which pkg-config`; then
+    use_jsoncpp_flags="`pkg-config --cflags jsoncpp`"
+    cmake_cxx_flags="${cmake_cxx_flags} ${use_jsoncpp_flags}"
   fi
+  libs="${libs} -ljsoncpp"
 fi
 
 if test "x${cmake_ansi_cxx_flags}" != "x"; then
@@ -1949,13 +1948,11 @@
     write_source_rule "c" "rhash-`cmake_obj ${a}`" "${src}" ""
   done
 fi
-if test "${cmake_bootstrap_generator}" = "Ninja"; then
-  if test "x${bootstrap_system_jsoncpp}" = "x"; then
-    for a in ${JSONCPP_CXX_SOURCES}; do
-      src=`cmake_escape_artifact "${cmake_source_dir}/Utilities/cmjsoncpp/${a}"`
-      write_source_rule "cxx" "jsoncpp-`cmake_obj ${a}`" "${src}" "${jsoncpp_cxx_flags}"
-    done
-  fi
+if test "x${bootstrap_system_jsoncpp}" = "x"; then
+  for a in ${JSONCPP_CXX_SOURCES}; do
+    src=`cmake_escape_artifact "${cmake_source_dir}/Utilities/cmjsoncpp/${a}"`
+    write_source_rule "cxx" "jsoncpp-`cmake_obj ${a}`" "${src}" "${jsoncpp_cxx_flags}"
+  done
 fi
 if test "${cmake_bootstrap_generator}" = "Ninja"; then
   echo "
diff --git a/cmake_uninstall.cmake.in b/cmake_uninstall.cmake.in
index b5fc700..9e61b00 100644
--- a/cmake_uninstall.cmake.in
+++ b/cmake_uninstall.cmake.in
@@ -7,10 +7,10 @@
 foreach(file ${files})
   message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
   if(EXISTS "$ENV{DESTDIR}${file}")
-    exec_program(
-      "@CMAKE_COMMAND@" ARGS "-E rm -f \"$ENV{DESTDIR}${file}\""
+    execute_process(
+      COMMAND "@CMAKE_COMMAND@" -E rm -f "$ENV{DESTDIR}${file}"
       OUTPUT_VARIABLE rm_out
-      RETURN_VALUE rm_retval
+      RESULT_VARIABLE rm_retval
       )
     if("${rm_retval}" STREQUAL 0)
     else()